# How best to handle non-serializable session data?

**URL:** <https://www.ruby-forum.com/t/how-best-to-handle-non-serializable-session-data/93880>\
**Category:** Rails\
**Created:** [April 12, 2006, 10:13pm UTC](https://www.ruby-forum.com/t/how-best-to-handle-non-serializable-session-data/93880 "2006-04-12T22:13:12Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![weyus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/w/7ab992/32.png) [@weyus](https://www.ruby-forum.com/u/weyus)\
**Post date:** [April 12, 2006, 10:13pm UTC](https://www.ruby-forum.com/t/how-best-to-handle-non-serializable-session-data/93880/1 "2006-04-12T22:13:12Z")

</div>

I have a piece of data that needs to persist across requests that is not  
serializable. It’s a Rubyful soup parse tree and it’s very expensive to  
instantiate and I need it for a while in my app.

Therefore, by default, it can’t be stored in the session since the  
default session storage mechanism is pstore.

One option I have to is change the session storage mechanism to  
in-memory only.

Otherwise, if I want to use the regular session storage (pstore) and  
still have this object available across requests, then it seems like I  
would have to implement my own little in-memory cache.

Has anyone got any better ideas than this?

Thanks,  
Wes

---

<div class="post-metadata">

**Author:** ![weyus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/w/7ab992/32.png) [@weyus](https://www.ruby-forum.com/u/weyus)\
**Post date:** [January 18, 2008, 7:43pm UTC](https://www.ruby-forum.com/t/how-best-to-handle-non-serializable-session-data/93880/2 "2008-01-18T19:43:19Z")

</div>

Someone responded to me off list about this issue, so I thought I would  
update this thread with what I ended up having to do.

You can’t use a solution like memcached to store this type of data,  
since it is not serializable.

The best solution that I could come up with was an in-memory cache. The  
problem with this of course, is that Rails apps. are running in separate  
Ruby processes, so implementing an in-memory cache immediate implies  
that each process has its own cache (this is why Rails is into  
“shared-nothing”). Obviously, if you want the state of these stored  
objects to be represented consistently in the app., then you have to  
figure out how to manage the caches which may or may not be present  
across all running Ruby processes.

If each Ruby process has its own cache, then there is a significant  
probability that you may hit process A on one request, establish a cache  
entry, then hit process B on another request, and have to regenerate the  
same cache entry, etc. So this approach may only make sense when the  
number of potential cache requests is \> the number of Ruby processes on  
your back end. Then, worst case, you only do your expensive cache-entry  
generation N times (where N is the number of Ruby processes) to service  
some X (where X \> N) number of requests. Of course, perhaps the same  
process gets hit on every request and you reap the maximum benefit from  
one in-process cache (1 cache-entry generation for X requests).

Here’s what I ended up with:

I created a custom Cache object show below:

class WesCache::Cache \< Hash  
REFRESH\_TIME\_KEY\_PART = “\_last\_refresh\_time”

def needs\_refreshing?(key, time\_to\_refresh)  
self.refresh\_time(key) \< time\_to\_refresh  
end

def refresh\_time(key)  
self.set\_refresh\_time(key, Time.now) unless  
self.has\_key?("#{key}#{REFRESH\_TIME\_KEY\_PART}")  
self["#{key}#{REFRESH\_TIME\_KEY\_PART}"]  
end

def set\_refresh\_time(key, refresh\_time)  
self["#{key}#{REFRESH\_TIME\_KEY\_PART}"] = refresh\_time  
end

def delete(key)  
super("#{key}#{REFRESH\_TIME\_KEY\_PART}")  
super  
end  
end

The key values embed the session id somewhere so that each in-process  
cache may be holding data related to any number of sessions. This cache  
also holds a key within itself (also implicitly on a per session basis)  
that represents the last time that this local cache values was refreshed  
(which in my case, means deleted - my objects are read-only so they  
either exist or they don’t, so “refereshing” doesn’t mean update, it  
means removed).

Then there is a concept of “global last refresh time” which is managed  
_globally_ for the application. The unified value of the last refresh  
time for a given _cache and key within it_ is stored in a memcache. So,  
to summarize, each Ruby process has its own “smart hash” cache that can  
keep track of when the last refresh was done for _a given key_ for  
itself. Then, it can compare the local refresh time against the global  
refresh time and know to remove its local entry (and thus cause it to be  
regenerated by the caller).

The object that makes use of all of this is the CacheManager - in  
retrospect, I might have moved all of this logic into the Cache object  
itself. I might refactor this in the future.

Here’s the CacheManager:

require ‘wes\_cache/cache’

#The refresh times are stored in the _memcache_ cache which is referred  
to through “Cache”.  
#DO NOT CONFUSE our caches (which are WesCaches) with the memcache.  
#The parse\_tree\_cache and the list\_data\_cache are WesCaches (local to  
the Ruby process).  
#The parse\_tree\_refresh\_times and the list\_data\_refresh\_times are  
memcaches (“global” to all Ruby processes).  
class CacheManager  
@@logger = RAILS\_DEFAULT\_LOGGER

#Local process based caches (effectively hashes)  
@@parse\_tree\_cache = WesCache::Cache.new  
@@list\_data\_cache = WesCache::Cache.new

#“Global” memcaches (for access by any process)  
Cache.put(“parse\_tree\_refresh\_times”, Hash.new) if  
Cache.get(“parse\_tree\_refresh\_times”).nil?  
Cache.put(“list\_data\_refresh\_times”, Hash.new) if  
Cache.get(“list\_data\_refresh\_times”).nil?

def self.parse\_tree\_cache(key)  
get\_cache(“parse\_tree\_refresh\_times”, @@parse\_tree\_cache, key)  
end

def self.remove\_from\_parse\_tree\_cache(key)  
remove\_from\_cache(“parse\_tree\_refresh\_times”, @@parse\_tree\_cache,  
key)  
end

def self.list\_data\_cache(key)  
get\_cache(“list\_data\_refresh\_times”, @@list\_data\_cache, key)  
end

def self.remove\_from\_list\_data\_cache(key)  
remove\_from\_cache(“list\_data\_refresh\_times”, @@list\_data\_cache, key)  
end

private  
def self.remove\_from\_cache(refresh\_times\_cache\_name, cache, key)  
refresh\_times\_cache = Cache.get(refresh\_times\_cache\_name)  
refresh\_times\_cache[key] = Time.now  
Cache.put(refresh\_times\_cache\_name, refresh\_times\_cache)  
cache.delete(key)  
end

def self.get\_cache(refresh\_times\_cache\_name, cache, key)  
refresh\_times\_cache = Cache.get(refresh\_times\_cache\_name)  
last\_global\_refresh\_time = refresh\_times\_cache[key] || Time.now  
@@logger.debug("\tLast global refresh time is:  
#{last\_global\_refresh\_time}")  
@@logger.debug("\tLast time this cache was refreshed:  
#{cache.refresh\_time(key)}")  
if cache.needs\_refreshing?(key, last\_global\_refresh\_time)  
@@logger.info(“Need to refresh list data cache for key #{key}”)  
cache.set\_refresh\_time(key, Time.now)  
cache.delete(key)  
else  
@@logger.info(“Don’t need to refresh list data cache for key  
#{key}”)  
end

```
cache

```

end  
end

I realize that all of this may be confusing. If anyone find it useful,  
I’m happy to answer any questions.

Wes
