Josh, Thank you, I will investigate and report back. Neil On Jul 25, 2011, at 1:23 PM, Joshua Ballanco wrote: > Have you considered the possibility that memcache might be recycling keys on you? > > We had an issue a while back where we were using memcache for both fragment caching and session storage. Occasionally, we would get exceptions in retrieving a session, and looking at the error message it was clear that what was retrieved from memcache was a fragment and not a session. Unfortunately, we moved back to a cookie-based session store before we had a chance to look deeper into the issue. What I can say is that based on the key generation scheme we were using for sessions and cache keys, there was effectively 0 chance that we were duplicating keys. Instead, it seemed like memcache was re-using slots for different keys when we started exhausting free slots. > > Hope that helps! > > - Joshua Ballanco > On Monday, July 25, 2011 at 2:22 PM, Neil wrote: > >> While it's entirely possible that this issue is caused by some other >> factor, but we are getting session collisions as well as an issue >> where one user is getting another user's session. This is clearly >> bad, but I cannot for the life of me figure out how this could even >> happen in the first place. The code looks thread safe to me, and a >> quick discussion on #ruby-lang seems to support that. >> >> Thoughts: >> 1. Session IDs are being generated in the same sequence (uses >> securerandom -> openssl which does not have a static seed) >> 2. Threads. Looks good to me. >> 3. Maybe memcached is returning something other than "STORED/ >> NOT_STORED" for @pool.add(sid, session), but the operation still >> succeeded? >> 4. Gnomes. >> >> Any input is GREATLY appreciated. Please don't say "it's an RC, what >> do you expect?" :) >> >> >> From https://github.com/rack/rack/blob/master/lib/rack/session/memcache.rb >> def generate_sid >> loop do >> sid = super >> break sid unless @pool.get(sid, true) >> end >> end >> >> def get_session(env, sid) >> with_lock(env, [nil, {}]) do >> unless sid and session = @pool.get(sid) >> sid, session = generate_sid, {} >> unless /^STORED/ =~ @pool.add(sid, session) >> raise "Session collision on '#{sid.inspect}'" >> end >> end >> [sid, session] >> end >> end >> >> def set_session(env, session_id, new_session, options) >> expiry = options[:expire_after] >> expiry = expiry.nil? ? 0 : expiry + 1 >> >> with_lock(env, false) do >> @pool.set session_id, new_session, expiry >> session_id >> end >> end >