giant robots smashing into other giant robots

Written by thoughtbot

jdclayton

Factory Girl 2.4 Goes Meta

Factory Girl has been getting some hardcore internal refactorings over the past few months. Traits are a great example of something that’s started very bare-bones with a few caveats and been transformed to one of my favorite features of the Factory Girl syntax. It required a pretty decent chunk of refactoring and there were bugs for quite a while due to the way attributes in general were handled.

The Old Way

Internally, Factory Girl has four different types of attributes (Dynamic, Static, Association, and Sequence); when compiling attributes, we were sorting attributes by the order attributes were added, moving all static attributes to the beginning of the list (with a priority attribute). This worked for 99% of all cases but did have bugs (the most recent involved traits and dynamic attributes). I wanted to resolve this once and for all.

The New Way

Factory Girl now creates a new class per factory (with some awesome usage of Class.new) and defines methods on that class for each attribute declared in the factory files. This means Factory Girl doesn’t have to worry about sorting attributes anymore; every attribute is effectively lazily-evaluated so it’ll return the correct value every time. Factory Girl also uses inheritance for these anonymous classes, resulting in a pretty significant speedup of factory interaction (namely because we were copying all parent attributes and callbacks to each child to mimic inheritance before).

What Does It Mean?

Factory Girl is now faster!

Here’s some benchmarks from Factory Girl 2.3.0:

                                user     system      total        real
build x10000                4.920000   0.010000   4.930000 (  5.028088)
trait build x10000          6.180000   0.010000   6.190000 (  6.296030)
inline trait build x10000   5.870000   0.010000   5.880000 (  5.989512)
deep build x10000           8.100000   0.020000   8.120000 (  8.285039)
attributes_for x10000       3.200000   0.010000   3.210000 (  3.289043)
create x500                 1.010000   0.320000   1.330000 (  4.186271)

And from Factory Girl 2.4.0:

                                user     system      total        real
build x10000                3.050000   0.000000   3.050000 (  3.121359)
trait build x10000          3.260000   0.010000   3.270000 (  3.353590)
inline trait build x10000   6.700000   0.040000   6.740000 (  7.037460)
deep build x10000           3.400000   0.010000   3.410000 (  3.503984)
attributes_for x10000       0.820000   0.010000   0.830000 (  0.886641)
create x500                 0.710000   0.310000   1.020000 (  3.980102)

Grab a copy of Factory Girl 2.4.0 and speed up your suite!

hgimenez

How-To: Quick Rails Benchmarking

This February 6th we are launching a new workshop: Advanced Rails. The workshop draws content from the Scaling Rails and Rails Antipatterns workshops, replacing them and creating best-of-breed content that will take your skill to the next level in creating well-crafted Rails applications that scale.

One of the topics we touch on is profiling and benchmarking your app. There are a number of tools available to achieve this, one of which is baked into Rails itself. Although we do discuss all of the great ways you can perform caching in a Rails app, experience has shown us that caching should be your last resource in your scaling strategy. Remember the two hardest things in computer science: Cache invalidation, naming things and off-by-one errors.

On to benchmarking, say you have identified an expensive method in one of your models that needs to be tuned. One easy and straight-forward way to measure your refactored process is to use the built in benchmarker to run quick tests. To get set up, you need to add the ruby-prof gem to your Gemfile, and have a properly patched ruby interpreter. I’m using the gcdata patch for MRI 1.9.2:

rvm install 1.9.2-p180 --patch gcdata --name gcdata

Now let’s assume the following expensive method in the Account class:

class Account
  def self.expensive_method
    sleep(1)
  end
end

We can now run a quick benchmark on that method by running it 10 times and taking some benchmarking measurements:

bundle exec rails benchmarker --runs 10 'Account.expensive_method'
Loaded suite script/rails
Started
BenchmarkerTest#test_10 (0 ms warmup)
           wall_time: 0 ms
              memory: 0 Bytes
             objects: 0
             gc_runs: 0
             gc_time: 0 ms
BenchmarkerTest#test_user_expensive_method (1.10 sec warmup)
           wall_time: 1.00 sec
              memory: 0 Bytes
             objects: 0
             gc_runs: 0
             gc_time: 0 ms

Finished in 24.933979 seconds.

You can even run a profiler with rails profiler 'Account.expensive_method' 10 flat and get more information on what’s being called and which components in your system are taking longer.

With this quick benchmark, you can now create a second, hopefully optimized, Account.expensive_method_fast and run them side-by-side, allowing you to quickly measure two implementations of the same behavior, and allowing you to quickly iterate to find the best solution.

This is just the tip of the iceberg. If you have some Rails experience and want to take it to the next level to grow your app into a well-factored and scalable system, check out our new Advanced Rails workshop.