Tyler SchultzTyler Schultz
Standup 09/30/2011: Bulk inserts
edit Posted by Tyler Schultz on Friday September 30, 2011 at 09:39AM

Ask for Help

"Our delayed job consumes 2G of memory creating ~20k ActiveRecords in a loop!"

It doesn't answer why your job is using so much memory, but check out activerecord-import.

The Ugly Truth

On a recent project, we had an ActiveRecord model that declared some relationships and callbacks like so:

belongs_to :credit_card
before_create :build_credit_card

The intent was that build_credit_card would build the associated CreditCard instance, and ActiveRecord's default :autosave feature on the belongs_to would save it.

What we discovered was that no CreditCard object was being persisted. We confirmed that :autosave is on by default for belongs_to relationships, so we couldn't immediately understand why the new CreditCard wasn't being created.

Googling proved futile, so we dove right in to the ActiveRecord source- and boy did we have a good laugh about 10 minutes later.

What we found was that the :autosave option works by simply declaring a before_save callback- that makes perfect sense.

In our case, however, we were building the object to be autosaved in a before_create callback, which ActiveRecords runs after the before_save callbacks (cf. the callback ordering docs).

So our first problem was that we needed to move the call to build_credit_card from a before_create callback to a before_save :on => :create callback.

Did you catch that? There is a difference between before_create and before_save :on => :create. A big difference.

While I understand the how and why of this, the semantics don't make it obvious. So beware!

Now with our declarations changed to

belongs_to :credit_card
before_save :build_credit_card, :on => :create

We ran our tests again, and, still, no love. Ahhh, we've still got an ordering problem. In addition to the ordering semantics detailed in the docs, ActiveRecord also runs callbacks within a single group in the order in which they are declared. So, even though we changed the call to build_credit_card to occur in a before_save, it was still occurring after the :autosave before_save callback, because of the declaration order.

Finally, we changed our declarations to

before_save :build_credit_card, :on => :create
belongs_to :credit_card

and our tests were happy.

Takeaways

  • When using autosave with any ActiveRecord association, be very careful of callback ordering if you are building or modifying the inverse objects using ActiveRecord callbacks.

  • before_create isn't ever the same thing as before_save :on => :create, even if it sounds like it should be.

Interesting Things

  • has_many and belongs_to associations can now automatically create back references each other, thanks to a Backport of :inverse_of from Rails 3 to rails 2.3.6. This allows us to keep our object graphs more correct and avoid situations where we have 2 copies of the same object because the object graph is walked in reverse. Here's how to use it:
class Parent < ActiveRecord::Base
  has_one :child, :inverse_of => :parent
  accepts_nested_attributes_for :child
end

class Child < ActiveRecord::Base
  belongs_to :parent
  validates_presence_of :parent
end

Alex ChaffeeAlex Chaffee
Basic Ruby Webapp Performance Tuning (Rails or Sinatra)
edit Posted by Alex Chaffee on Wednesday April 28, 2010 at 07:37PM

My company launched our app, Cohuman, a few weeks ago. The rush of finishing features, fixing bugs, and responding to user feedback has subsided a bit, and it's time to go back and give the little baby a tune-up. I find that a good development process will ebb and flow, and as long as you don't let something slide for too long, it's perfectly acceptable to let bugs, or performance issues, or development chores pile up for a bit and then attack them concertedly for an entire day or two. A bug-fest or chore-fest or tuning-fest can actually increase efficiency as you get in a rhythm... and it feels really good at the end of the day when you see all the bugs you slayed or all the milliseconds you shaved.

In this article I'd like to describe some of my techniques. I make no claim of originality or great expertise; I just want to share what I know, and hear (in comments) what other people have learned. I'm using Sinatra and ActiveRecord, but not Rails; hopefully this discussion will help people no matter what framework they're using.

Ask for Help

"We keep getting webrat thread exceptions running our integration specs with the rails integration runner: Thread tried to join itself. The error message varies with different versions of ruby 1.8.6 vs 1.8.7."

Anyone had this problem or know why?

"How do I skin an iphone mobile site to be the correct width so it's not 980px wide?"

<meta name="viewport" content="width = device width" />

*"We're trying to deploy some nginx configuration changes to EngineYard Cloud, what's the right way to do that?"

We've tried building custom chef recipes to solve this problem, but they run after nginx has already restarted, so are a poor solution to this problem. The better solution might be to check in configuration files into the application and symlink them into the nginx configuration directory using a before_symlink.rb hook in the /deploy directory.

*"We've got a has_many association where some of the child records are originally saved in an invalid state. When we later load the parent and ask it if it's valid, it returns true even with validates_associated. How can we get the desired validation behavior?"

Turns out that unloaded associations are not validated. Solution: load the association before calling .valid? on the parent. In general, you should also not create invalid objects, instead using a state variable to put them into a "draft" or "incomplete" state where they are still valid but not complete. Then remove that state and you'll see the errors required to finish that object.

Interesting Things

  • When RubyMine 2.0.1 won't run your focused specs, try attaching rspec 1.2.9 to it rather than 1.3.x. It fixed this issue for one of our teams.
  • Rubymine 2.0.2 came out today: can finally run focused contexts?! Also including bundler support! What's new
  • We tried our Unicorn on EngineYard cloud: so far so good. It's still "experimental" but seems to work.

Kelly FelkinsKelly Felkins
Standup 4/1/2010: “update_attribute is almost never the right thing to use”
edit Posted by Kelly Felkins on Thursday April 01, 2010 at 09:29AM

Ask for Help

"mp4s do not play during download in chrome"

Talks at Pivotal are recorded and published in various formats. Our talks page has an embedded viewer so that people can watch the video without downloading. We also offer a downloadable version in mp4. Most browsers will play the video as it is downloading. Google Chrome does not. Are we doing something wrong?

Pivotal Talks

Interesting Things

  • Beware: has_many associated objects are saved before has_one associated objects.

  • update_attribute of foreign_key value on belongs_to association does not save...when object was created by factory girl?

This is pretty specific, but perhaps not enough to be useful. The team tried to do an update_attribute on an object generated by factory girl, changing the belongs_to column value. However, no database update would occur. They later resolved this by doing a reload on the object before the update_attribute.

"Update_attribute is almost never the right thing to do" --anon

Alex ChaffeeAlex Chaffee
UTC vs Ruby, ActiveRecord, Sinatra, Heroku and Postgres
edit Posted by Alex Chaffee on Friday January 22, 2010 at 12:17PM

Now that I'm starting to use DelayedJob to perform jobs in the future in my Heroku Sinatra app, its important that they happen at the scheduled time. But unless you pay attention, you'll find that times get mysteriously changed -- in my case, since I'm in San Francisco in the wintertime, by +/-8 hours -- which means that some conversion to or from UTC is being attempted, but it's only working halfway.

Trying to keep a handle on which libraries are attempting, and which are failing, to convert times is a losing battle, so I'm trying to do the right thing and save all my times in the database in UTC, and convert them to and from the user's local time as close to the UI as possible. Unfortunately, a variety of gotchas in Ruby and ActiveRecord and PostgreSQL makes this trickier than it should be. Here's a little catalog of my workarounds.

Mike GraftonMike Grafton
Standup 10/6/2009
edit Posted by Mike Grafton on Tuesday October 06, 2009 at 10:33AM

Help

Why is upgrading to Ruby 1.8.7 so painful?

More specifically, a Pivot was wondering why there seem to be so many ways to install Ruby and Rubygems on a Mac. There are a lot of different places where gems end up being installed depending on which version of Ruby you have installed, and the specifics of how you installed it. The conversation turned into one about RVM and Yehuda Katz' Bundler, two technologies that appear destined to make it much easier to easily combine a version of Ruby with a set of gems under a particular project.


What is that technology that allows for more complex condition hashes in ActiveRecord?

This must be ActiveRecord::Extensions, which allows for an expanded syntax in the conditions hash of AR finders. A debate was had as to whether hashes and arrays could possibly comprise a reasonable DSL for complex query logic, but surprisingly, the final word on the subject was not reached during standup.


We are using curl to talk to a Mongrel/Rack server that is running some specs. That server is emitting dots (just as any Rspec process would), but we cannot get those dots to show up in real-time on the client. The only way we've been able to force a flush is with a newline character, but that gives us an ugly vertical column of dots. Any suggested hacks for this?


The Bay Area Chef Meetup Group is meeting on 10/14 in Mountain View. If you're into Chef (and here at Pivotal we use it extensively), you might want to check it out.

Adam MilliganAdam Milligan
Beware the frumious nested attribute
edit Posted by Adam Milligan on Sunday September 20, 2009 at 06:10PM

Nested attribute assignment is one of the recent additions to Rails that made a great deal of sense, and made a lot of people happy. Chances are you've either used nested attribute assignment by now, or you worked on an older project that really could have used it. If you haven't yet, check it out and see what you think.

Unfortunately, not all is well in Railstown. Nested attribute assignment is slick, and the related implementation of #fields_for makes it even slicker, but #fields_for can cause you some headaches if you're not careful. Possibly if you are careful as well.

Adam MilliganAdam Milligan
Little, shiny robots
edit Posted by Adam Milligan on Saturday August 29, 2009 at 08:01PM

One of my favorite computer games when I was growing up was Robot Odyssey; I imagine it will come as a surprise to no one that I was a nerdy kid. This article is a little bit of a tribute to that game, and the coolness of solving complex problems with a handful of simple concepts combined in clever ways.

Imagine you want to write a web-based game that involves robots. The robots in your game are a bit like the robots in Robot Odyssey: you program them with a list of simple instructions and when you turn them on they follow those instructions faithfully. Let's say, for the sake of argument, that your robots can Walk Forward, Turn Left, Turn Right, Jump, and Beep.

Other articles: