Caching objects

Hi,

i just wanted to say my sorries in advance (sorrie/sorry/sorri)…i have
just started learning about the subject of caching, but unfortunately
haven’t effictively managed to implement what i had learned.
obviously it’s possible to cache a whole page, or action with the
caches_page :x or caches_action :y methods, but for some reason it
hasn’t really worked with what i had been doing. (i was trying to cache
Time.now to no avail)
either way, the issue at hand (not Time.now) is trying to cache an
object that i will usually pull out of the database once per
session…say i have a table database.groups and i want to get all of
the records out of the table, and keep them in my application, without
pulling them out every other action (unless i specify otherwise)…

i was hoping there was something as simple as

MyController
#here
caches_action :get_groups

def index
get_groups
end…

def another_action
get_groups
end


private

#and here
def get_groups
@groups = Group.find(:all)
end

but i can’t get it to work! …i’m in production mode, with the
ActionController::Base.perform_caching = true
and it still isn’t working! …any ideas to what i am doing wrong? oh,
and also how do i ‘abort’ the caching for ‘get_groups’ and overide that
in order to look for the groups in a new db-query? (something like
expire_get_groups)

much thanks in advance,
s

Wrong kind of caching :slight_smile: Action, page, and fragment caching are for the
views. There isn’t any built-in support for doing database result
caching.
Building your own isn’t that hard, but first decide if it’s really
necessary.

If you use page caching correctly, you can dramatically reduce your
backend
overhead because Rails never gets invoked. Rails makes static pages from
your dynamic content and places those in your public folder. Subsequent
hits
are served from the static pages and Rails doesn’t even notice. (The web
server is usually configured to only invoke Rails if the request is a
404
Page Not Found.)

If you use fragment caching, you can reduce a lot of database hits for
things like dropdown boxes, etc.

I know that there are a couple of folks out there experimenting with
database result caching. I am not familiar with it because I’ve been
content
with page and action caching.

Brian H. wrote:

Building your own isn’t that hard, but first decide if it’s really
necessary.

…i was kind of hoping to release the tension off of the db hits seeing
that the ‘groups’ table will hardly be used, and so it would be
unneccerary to look for all of the groups all the time (unless the admin
would enter a new group which would happen rarely, and then i would
envoke something simillar to expire__…)
so where would i start if i wanted to build my own method for this?
where do i store it?

If you use fragment caching, you can reduce a lot of database hits for
things like dropdown boxes, etc.

and if the fragment caching doesn’t involve db queries, how would i save
the time? …i happen to be fairly new to this subject, so thanks for
the patience and time(and at this time i could possibly throw in a
general thanks for the reply)

s

Fragment caching works well for things like select boxes… say for
example
you have ‘locations’ in your database. When you create a new inventory
item,
you might specify the location by picking it from a dropdown box.

You can build that select box of ‘locations’ using fragment caching. The
DB
gets hit once, the list is built, and then the fragment is used next
time
instead of the db. You need to expire the fragment when you add,
modify,
or delete a location. You do that manually, either directly or by using
a
sweeper. (See Agile Web D. for a good example of sweepers.)

As for how to cache your own results, first be sure you’re not doing any
premature optimizing. Databases are meant to handle queries. Depending
on
how often the table is used, you can do tuning on the db side too, such
as
applying proper indexes.

I would write the app first and see how it performs under your expected
load. If it performs poorly, then come back and ask for some specific
options.

If you absolutely feel that you have to do this functionality right
now,
I think that the BackgroundDRB plugin might help you (
http://backgroundrb.rubyforge.org/).

On Jun 28, 2006, at 10:16 AM, newbie wrote:

would enter a new group which would happen rarely, and then i would
envoke something simillar to expire__…)
so where would i start if i wanted to build my own method for this?
where do i store it?

This is generally done with a class variable. To use your earlier
example, write code that looks similar to this:

def get_groups
@@groups ||= Group.find(:all)
end

If @@groups isn’t set yet it will execute the Group.find call. If
@@groups is set, it just returns itself. If the code looks strange,
it is functionally equivalent to:

def get_groups
if @@groups.nil?
@@groups = Group.find(:all)
end
return @@groups
end

If you use fragment caching, you can reduce a lot of database hits
for
things like dropdown boxes, etc.

and if the fragment caching doesn’t involve db queries, how would i
save
the time?

I haven’t really experimented with fragment caching yet, so I’ll let
someone else answer this one…

cr

so where would i start if i wanted to build my own method for this?
set, it just returns itself. If the code looks strange, it is functionally
equivalent to:

Just keep in mind that this is per rails process… so if you’re not
careful about updating this when the database changes, you could have an
instance where from “request to request” you would see different output
for @@groups depending on what rails process your request was processed
by.

-p

Lastly, use “expire_fragment(url_params_for_fragment)” after any
controller action that changes the groups table. This way you always
have the speed of caching, but as soon as anything changes you wipe the
cache and let the next request generate the up to date content.

Thanks! …u’v been really helpful.
last and may be least question; where do i put the expire_fragment? in a
helper? appcontoller?

newbie wrote:

i was hoping there was something as simple as

MyController
#here
caches_action :get_groups

def index
get_groups
end…

def another_action
get_groups
end


private

#and here
def get_groups
@groups = Group.find(:all)
end

Get rid of caches_action, since that won’t do what you want. Try
something more like:

#view
<% cache do %>
<%= #bunch of complicated stuff to build groups page %>
<% end %>

#controller
def get_groups
unless read_fragment(params) #uses the url_for helper to read the
#cached fragment for this page.
@groups = Group.find(:all)
end
end

So the action gets called the first time, read_fragment does not find
any cached fragment, so it sets @groups to the DB result.

Then, the view kicks in and the cache method doesn’t find the fragment,
so it executed the code, and caches the result using the params of
whatever page it’s on.

The next time the action gets called, read_fragment finds the cached
framgent, and skips the db call, and skips assigning the @groups
variable. This is fine because as long as all the view data that uses
the @groups variable is cached, the object will never be used.

Lastly, use “expire_fragment(url_params_for_fragment)” after any
controller action that changes the groups table. This way you always
have the speed of caching, but as soon as anything changes you wipe the
cache and let the next request generate the up to date content.

Summary: Use “cache do” or “cache(some_url_params) do” in your view, and
check to see if you can read that fragment from your controller before
you do any DB calls. Then after any change that would alter what was
cached use expire_fragment to clear it.