Ionitor has asked for the wisdom of the Perl Monks concerning the following question:

I am currently working on an application for Catalyst. One set of pages (the meat of the application) will direct users differently depending on a site wide state, i.e., everyone on the site starts at state 1 (roughly simultaneously), and when a certain amount of time or a certain event has taken place, they are moved onto state 2 (again, as simultaneously as possible). The issue is that state 2 depends on a somewhat high load set of calculations and database hits, so I would only like to perform that calculation once. How would one typically deal with this problem?

The best answer that I came up with is that I should use a cache, such as Catalyst::Plugin::Cache, to store the results of the state change. The problem is that I expect the change from state 1 to 2 to be triggered by a user. If user #1 triggers the state change, I may still be performing the calculations necessary when user #2 starts looking at the cache. Of course, since the calculations are still being performed, the cache is still empty, so process dealing with user #2 will start performing the calculations, even though the process dealing with user #1 is already performing these calculations.

In HTML::Mason, the caching mechanism has a "busy_lock" to prevent this problem. However, I have not been able to find any standardized way of doing this in Catalyst. At the moment, I'm thinking I'll have to use something like a semaphore to indicate when the cache is currently being processed (and to avoid race conditions while checking if the cache is being processed). The thing is, I feel like I can hardly be the first person to come up against this problem, so I'm wondering if there is a standard or semi-standard way to deal with these issues, either as a general module or a Catalyst specific one.

  • Comment on Sitewide states in a web application (specifically cache locking in Catalyst)

Replies are listed 'Best First'.
Re: Sitewide states in a web application (specifically cache locking in Catalyst)
by TOD (Friar) on Jun 30, 2007 at 05:40 UTC
    i don't know about Catalyst, but basically there are many ways of storing data between processes. the most performant solution would lie in using shared memory. you create the shmem segment in the startup.pl of your application, and any of the app's pages store the common data on it and read from there.
    for instance startup.pl might look like (assuming all of the application's global data are stored in a hashtable called %appdata):
    use IPC::Shareable ':lock'; [...] no warnings 'untie'; my $knot; my $glue = "SHMEMGLUE"; eval { $knot = tie %tie, 'IPC::Shareable', $appdata{$glue}, { create = +> 0, mode => 0666, size => $appdata{SHMEMSIZE}, exclusive + => 0, destroy => 0 }; }; unless ($@) { $knot->remove; untie %tie; undef $knot; } $knot = tie %tie, 'IPC::Shareable', $appdata{$glue}, { create => 1, mo +de => 0666, size => $appdata{SHMEMSIZE}, exclusive => + 0, destroy => 0 }; untie %tie;
    note: this code first tries to find the shmem segment, and if it exists it destroys it. this prevents you from allocating additional memory with each restart of apache.

    --------------------------------
    masses are the opiate for religion.
      Yeah, I'm aware of IPC::Shareable. Since I'd like to be able to develop this under Win32 (which seems to like the IPC::Semaphore that Shareable depends on) I'd probably end up using a different module with the similar abilities (any recommendations there?). Mainly, modules like that are somewhat low level, and I was hoping I could be lazy and use a module that takes care of issues like locking and memory allocation.
Re: Sitewide states in a web application (specifically cache locking in Catalyst)
by Ionitor (Scribe) on Jul 02, 2007 at 09:46 UTC
    After further investigation, it appears that Cache::FastMmap (and consequently Catalyst::Plugin::Cache::FastMmap) has a get_and_set function that handles the locking issues needed to avoid many race conditions. It allows the programmer to send a sub reference which receives the value of one of the cache items and change it, keeping it locked the whole time. While it isn't quite as convenient as something like a "busy_lock" flag, it is more versatile and may even work better for my needs.