In part 1 of this post, I talked about using modules to help break source code of large models into smaller, comprehensible pieces. As Ariel Valentin pointed out, we need to do more to lighten up the runtime memory footprint when large models are instantiated.
Let’s imagine we have an application with a
User model (might not be a stretch) and we’re building a page that loads a bunch of user gravatars. Our
User model has accumulated a ton of attributes, but we may only need a limited set to render the gravatar properly. We might add a line like this in the controller action to select only the needed attributes:
@users = User.select(%w[ id name gravatar\_id ]).all
Perhaps to make this more readable, we’ll extract our select statement into a scope in the
# user.rb scope :select\_gravatar\_attributes, select(%w[ id name gravatar\_id ]) # controller action @users = User.select\_gravatar\_attributes.all
We’ll incur less memory overhead during this action at runtime since the app won’t have to load as much data. The caveat is that our
@users won’t have access to the data we left behind, so we need to take care only to call methods that rely on the data we have actually loaded. It would be easier to test our lighter users if we could treat them as a different type.
Applying the model-slimming refactoring we discussed in part 1, we can extract gravatar related functionality into a
UserExtensions::Gravatar module and create a
Gravatar::User that inherits from our
User model. Furthermore, we can change our
select_gravatar_attributes to a
default_scope in the subclass, so whenever we grab
Gravatar::User from the db, it’ll have only the attributes we need. Our controller action would now look like this:
@users = Gravatar::User.all
To put our savings to the test, we can run some basic benchmark tests. I created a sample rails 3.1 app and created ~2000 users records in my development database and ran the
rails benchmarker script to load all users both normally and as gravatars. Here are the results:
All as gravatars
The memory footprint for gravatar users is half of that for our ‘fat’ users. Obviously results will vary greatly depending on architecture, application and the attributes selected, but this anecdotal case demonstrates potential for some big wins in instantiating slimmer subclasses of your heavy models.