views:

169

answers:

2

i'm using the new caching solution for Rails as described here.

the development environment works fine, but the test and production sends invalid ETag header ignores the parameter of the stale? function.

heres is the corresponding part of one of my controllers:

def index
  @categories = Category.all

  if stale?(:etag => @categories)
    respond_to do |format|
      format.html
      format.xml  { render :xml => @categories }
      format.json { render :json => @categories }
    end
  end
end

the stale? method of the ActionController::Base calls the fresh_when method which sets the etag of the Response object, which has the following code:

def etag=(etag)
  if etag.blank?
    headers.delete('ETag')
  else
    headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
  end
end

the Category model sends the correct cache_key if i get it in every environment:

>> Category.find(1).cache_key
=> "categories/1-20100117153353"
>> ActiveSupport::Cache.expand_cache_key(Category.find(:all))
=> "categories/1-20100117153353/categories/2-20100117152007/categories/3-20100116094423/categories/4-20100116094423/categories/5-20100116094423/categories/6-20100116094423/categories/7-20100116094423/categories/8-20100117145800/categories/9-20100117145808"

so i simply don't understand what's going on, because when I select the url http://localhost:3000/admin/categories/ with the development environment, the ETag changes every time when i save on a Category but with test or production it does not.

i've tested it with webrick and thin

A: 

I do not know whether there is an maximum on header lines/parameters, but with a lot of categories the ETag value becomes very long, as your example already shows.

Instead of creating a big string containing all creation/modified dates, you could find the latest modification date and use that for an ETag.

Furthermore the article you quoted uses fresh? instead of stale? including some other methods. Why are you not using those?

Edit: When looking at the article in your updated link the solution seems to be, to fill in just one @category (same as they do). Therefore find the last modified category

@category = Category.find(:first, :order => 'date DESC')

and use this value to generate the MD5 for the ETag.
Get the complete list of @categories only if you are rendering the page.

Veger
Rails calls an MD5 checksum on the results of `expand_cache_key` so this won't be the problem. this MD5 checksum guarantees that the `ETag` parameter has the same length every time.
KARASZI István
Sorry for linking the bad article, I fix it now.
KARASZI István
But still I think it is better to just find the category with the latest modification date. Since that basically defines whether the ETag should change or not and saves (a lot) of data being send from the database to your application.
Veger
as you can see from the controller snippet the `@categories` are loaded anyway, so it won't speedup anything because every way I should walk by every element of the `@categories` array to find out the newest `Category`
KARASZI István
The categories should not be loaded completely when your page is not modified (ETag corresponds with `request.etag`). To get the latest modification date, you can ask the database with `ORDER BY date DESC` and `LIMIT 1,1` (but then the rails equivalents).
Veger
in this you're right yes. (btw if the cached etag is stale it would be two queries this way).but the problem lies somewhere else and the question is about the not updating ETag header not the speed of the application.
KARASZI István
You could at least try to use `@category` instead of `@categories` so your code follows the example more strictly. So we can see whether it solves your problem or that it has some other roots.
Veger
i rewrote everything to work as the link said, i'll fix the question soon
KARASZI István
I've found the source of the problem. So thanks for your time to answer my question.
KARASZI István
Care to tell what the problem was? I am kinda curious to know!
Veger
is in the accepted answer, thanks for your help!
KARASZI István
Oh caching of the information... nice find! That's indeed a huge difference between development and production environments!
Veger
A: 

the solution was that the Category.all method cached the results on class level, so once it was fetched everything was cached for the rest of the requests.

it was not there in development environment because every time the model was reloaded because in that environment the config.cache_classes was false!

KARASZI István
that's why the ETag haven't been updated
KARASZI István