Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

Scopes cached in production mode

#4960

In Rails 3 beta 4....

class Auction < ActiveRecord::Base
  scope :started, lambda { where("starting_at <= ?", Time.now) } # same problem if I use proc instead of lambda
end

If I start the server in production mode and create with an auction with the current starting time, the auction does not appear when I call "Auction.started". It seems Time.now in the scope is evaluated when the server is started and used for all further calls. In development mode the scope seems to return the proper results. Are there any workarounds?

Reported by Caleb Perkins · June 24th, 2010 @ 09:56 PM

State: invalid
Milestone: 3.0.2
Assigned to: José Valim José Valim
Importance: Low

Activity

  1. Neeraj Singh
    Neeraj Singh

    ticket #4708 Query cache broken when using associations with scopes has same issue. I fixed that issue by not caching the arguments. That patch never got picked up. However Pratik did patch the code by caching the arguments.

    I am working on a patch for this ticket.

    June 24th, 2010 @ 10:50 PM

  2. Neeraj Singh
    Neeraj Singh

    @Caleb in script/console in production mode I am getting new value every single time.

    
    class User < ActiveRecord::Base
      scope :recent, lambda { where("created_at <= ?", Time.now) }
    end
    
    ruby-1.8.7-p249 > User.recent
      User Load (0.3ms)  SELECT "users".* FROM "users" WHERE (created_at <= '2010-06-24 21:55:06.057378')
     => [] 
    ruby-1.8.7-p249 > User.recent
      User Load (0.3ms)  SELECT "users".* FROM "users" WHERE (created_at <= '2010-06-24 21:55:07.225097')
     => [] 
    ruby-1.8.7-p249 > User.recent
      User Load (0.2ms)  SELECT "users".* FROM "users" WHERE (created_at <= '2010-06-24 21:55:07.792841')
     => []
    

    June 24th, 2010 @ 10:56 PM

  3. Caleb Perkins
    Caleb Perkins

    Yes, in console mode you get the correct values. However, if you put the code in your controller and use a web server, you do not.

    June 25th, 2010 @ 01:23 AM

  4. Neeraj Singh
    Neeraj Singh

    Here is my code for rail3 app.

    class User < ActiveRecord::Base
      scope :recent, lambda { where("created_at <= ?", Time.now) }
    end
    
    def index
       User.recent.all.inspect
       User.recent.all.inspect
       render :text => 'done'
    end
    

    I changed the log settings for production mode to see sql statement.
    I started server in production mode. This is the sql statements I got.

    User Load (0.2ms)  SELECT "users".* FROM "users" WHERE (created_at <= '2010-06-25 01:15:31.495205')
    User Load (0.2ms)  SELECT "users".* FROM "users" WHERE (created_at <= '2010-06-25 01:15:31.510747')
    

    When I refresh the page I get this sql

    User Load (0.2ms)  SELECT "users".* FROM "users" WHERE (created_at <= '2010-06-25 01:15:39.042884')
    User Load (0.2ms)  SELECT "users".* FROM "users" WHERE (created_at <= '2010-06-25 01:15:39.044806')
    

    Could it be possible that some other gem/plugin is interfering with the behavior.

    June 25th, 2010 @ 02:19 AM

  5. Caleb Perkins
    Caleb Perkins

    I must apologize; the code snippet I posted wasn't the line not working properly. I have found the source of the issue, after disabling all Gems/plugins. The problem is in chaining scopes.

    In the following code, Auction.active returns new results depending on the time:

    class Auction < ActiveRecord::Base
      scope :started, lambda { where("starting_at <= ?", Time.now) }
      scope :unfinished, lambda { where("ending_at > ?", Time.now) }
      scope :active, lambda {where("ending_at > ?", Time.now).where("starting_at <= ?", Time.now) }
    end
    

    However, in this code the result of Auction.active is the same from server start:

    class Auction < ActiveRecord::Base
      scope :started, lambda { where("starting_at <= ?", Time.now) }
      scope :unfinished, lambda { where("ending_at > ?", Time.now) }
      scope :active, started.unfinished
    end
    

    June 25th, 2010 @ 06:54 AM

  6. Neeraj Singh
    Neeraj Singh

    I did not even know what one could do something like that -- define a scope which in turn depends on other scope.

    June 25th, 2010 @ 04:27 PM

  7. José Valim
    José Valim
    • Milestone cleared.
    • Assigned user set to José Valim
    • State changed from new to invalid
    • Importance changed from to Low

    You can chain scopes, but they will be evaluated at the moment you call them. That said, when you call started, it will execute the lambda, so it will have a frozen Time.now. In other words, chaining lambda scopes will likely give you the wrong result.

    You need to wrap the scope :active in a lambda as well.

    scope :active, lambda { started.unfinished }
    

    July 13th, 2010 @ 07:30 PM

  8. Jeremy Kemper