Hi guys,
One of the odd things that's blocking one big app I work on from moving up to Rails 3.0 or 3.1 is that with these versions whenever an object is saved, a reference to it is retained until the end of the enclosing transaction block, so that its state (new_record?, etc.) can be rolled back if the transaction is rolled back. This is a nice feature, but because Ruby's WeakRef implementation is broken on 1.9, the ActiveRecord implementation uses regular strong references.
This is bearable in production code in most cases, but it's absolutely killing large test suites. When transactional fixtures is on, then the transaction in question is open for the length of the entire set of tests, so all objects ever saved at any point during the test cases are retained in memory.
Our test suite bloats up to 6GB before we give up and kill it - by that point the VM is doing almost nothing but garbage collecting. (It can run with transactional fixtures off, but that's very, very slow for an app with many tables.)
There are several ways we can fix this, and I thought it would be good to discuss on the list which is best because they will have different behavior. The options as I see them are:
1. Change the transaction code to not count the test transaction when determining whether to fire the commit/rollback hooks and discard the reference list. This is a bit of work, and will change the behavior of tests, but some might argue in a good way (if you want to test that your after_commit and after_rollback hooks work, pretending that you are not already inside a test transaction).
2. Explicitly throw away the reference list after each test completes, so that after_commit and after_rollback are not fired. A bit of a hack, but I imagine some will prefer it given what after_commit and after_rollback are typically used for.
3. Hassle someone to fix that WeakRef so that we can use it, making the issue largely moot (assuming you didn't care about those callbacks). The best long-term solution for other reasons, but difficult (1.9 needs patching). See Feature #4168: WeakRef is unsafe to use in Ruby 1.9 - Ruby master - Ruby Issue Tracking System.
If you want to see the problem yourself, just save some records (any model will do) in tests and watch them not get GCd:
class TestTest < ActiveSupport::TestCase self.use_transactional_fixtures = true
1.upto(20) do |i| test "some test #{i}" do TestRecord.first.update_attributes!({})
GC.start count = 0 ObjectSpace.each_object(ActiveRecord::Base) do |b| count += 1 end puts "#{count} ActiveRecord" end end end