Tom Peterson
Tom Peterson

Reputation: 13

Rails scope where clause appears to be cached

Using Rails 3.2.22.5, Ruby 2.0.0-p648, I have this query, which among other things is supposed to return patients less than 18 years old:

scope :should_be_underage, joins("left join patient_status on patient_status.patient_id = patient.patient_id left join patient_state on patient_state.patient_id = patient.patient_id LEFT JOIN patient_account ON patient_account.patient_id = patient.patient_id INNER JOIN account_covers_disease ON account_covers_disease.account_id = patient_account.account_id").where("patient.dob > ?", 18.years.ago).where("((patient_state.patient_particip_status_id <> 7 and patient_account.patient_account_status = 1 and account_covers_disease.disease_id = 2) OR ((patient_status.patient_status_id not in (9, 36)) and patient_account.patient_account_status = 1 and account_covers_disease.disease_id = 1))").uniq

This runs fine, but a few days ago it seemed like the results of where("patient.dob > ?", 18.years.ago) had been cached. We were getting patients returned who were four days past their 18th birthday. When I artificially set one of those patient's birthdays back a day, they fell out of the results. When I returned the patient to the original birthday, they joined the results again. So it seems that the query as a whole was not being cached.

Five days later, I tried again, and now the query was returning patients nine days past their 18th birthday, i.e., the same group of patients. I tried the artificial birthday reset test again and again it worked. It's as if just the value of 18.years.ago had been cached, but the query otherwise runs dynamically.

When I rebuilt the codebase, the problem went away, but that's obviously not a production solution. Does anyone know what's going on here and how I can correct it? Thanks.

Upvotes: 1

Views: 1746

Answers (2)

S.Spencer
S.Spencer

Reputation: 611

You could/should put the relation expression in a lambda so the date is evaluated when called, not just at load time

scope :should_be_underage, -> { joins(...).where(...) }

Alternatively you could put the expression in a static method

def self.should_be_underage
  joins(...)
    .where(...)
    .uniq
end

Fire up the console and check what query is being used

> Patient.should_be_underage.to_sql

Call to_sql twice here to see how/if the query changes. I imagine at the moment it is unchanging but with a lambda it will re-evaluate each call.

Upvotes: 5

Josh Brody
Josh Brody

Reputation: 5363

If caching is indeed breaking your results, you can verify this by wrapping your query in a block like so:

Model.uncached do 
  # ... 
end

Upvotes: 2

Related Questions