views:

435

answers:

7

Rails comes with a handy session hash into which we can cram stuff to our heart's content. I would, however, like something like ASP's application context, which instead of sharing data only within a single session, will share it with all sessions in the same application. I'm writing a simple dashboard app, and would like to pull data every 5 minutes, rather than every 5 minutes for each session.

I could, of course, store the cache update times in a database, but so far haven't needed to set up a database for this app, and would love to avoid that dependency if possible.

So, is there any way to get (or simulate) this sort of thing? If there's no way to do it without a database, is there any kind of "fake" database engine that comes with Rails, runs in memory, but doesn't bother persisting data between restarts?

+3  A: 

You should have a look at memcached: http://wiki.rubyonrails.org/rails/pages/MemCached

p3t0r
+2  A: 

There is a helpful Railscast on Rails 2.1 caching. It is very useful if you plan on using memcached with Rails.

FlipFlop
A: 

@p3t0r- is right,MemCached is probably the best option, but you could also use the sqlite database that comes with Rails. That won't work over multiple machines though, where MemCached will. Also, sqlite will persist to disk, though I think you can set it up not to if you want. Rails itself has no application-scoped storage since it's being run as one-process-per-request-handler so it has no shared memory space like ASP.NET or a Java server would.

BRH
Does sqlite come with Rails? It looks like it's a separate install here: http://wiki.rubyonrails.org/rails/pages/HowtoUseSQLite
Sean McMains
A: 

So what you are asking is quite impossible in Rails because of the way it is designed. What you ask is a shared object and Rails is strictly single threaded. Memcached or similar tool for sharing data between distributed processes is the only way to go.

Honza
+1  A: 

Using the stock Rails cache is roughly equivalent to this.

hoyhoy
+4  A: 

Right answer: memcached . Fast, clean, supports multiple processes, integrates very cleanly with Rails these days. Not even that bad to set up, but it is one more thing to keep running.

90% Answer: There are probably multiple Rails processes running around -- one for each Mongrel you have, for example. Depending on the specifics of your caching needs, its quite possible that having one cache per Mongrel isn't the worst thing in the world. For example, supposing you were caching the results of a long-running query which

  • gets fresh data every 8 hours
  • is used every page load, 20,000 times a day
  • needs to be accessed in 4 processes (Mongrels)

then you can drop that 20,000 requests down to 12 with about a single line of code

@@arbitrary_name ||= Model.find_by_stupidly_long_query(param)

The double at-mark, a Ruby symbol you might not be familiar with, is a global variable. ||= is the commonly used Ruby idiom to execute the assignment if and only if the variable is currently nil or otherwise evaluates to false. It will stay good until you explicitly empty it OR until the process stops, for any reason -- server restart, explicitly killed, what have you.

And after you go down from 20k calculations a day to 12 in about 15 seconds (OK, two minutes -- you need to wrap it in a trivial if block which stores the cache update time in a different global), you might find that there is no need to spend additional engineering assets on getting it down to 4 a day.

I actually use this in one of my production sites, for caching a few expensive queries which literally only need to be evaluated once in the life of the process (i.e. they change only at deployment time -- I suppose I could precalculate the results and write them to disk or DB but why do that when SQL can do the work for me).

You don't get any magic expiry syntax, reliability is pretty slim, and it can't be shared across processes -- but its 90% of what you need in a line of code.

Patrick McKenzie
In development mode this does not work. Your global variable will be reset on each call to the controller. I guess due to the architecture of rails memcached is the only way. :-(
Sergio Oliveira Jr.
A: 

The Rails.cache freezes the objects it stores. This kind of makes sense for a cache but NOT for an application context. I guess instead of doing a roundtrip to the moon to accomplish that simple task, all you have to do is create a constant inside config/environment.rb

APP_CONTEXT = Hash.new

Pretty simple, ah?

Sergio Oliveira Jr.