#15026: Test failures in django.contrib.sessions on default project when memcached used as CACHE_BACKEND ----------------------------------------------+----------------------------- Reporter: jsdalton | Owner: nobody Status: new | Milestone: 1.3 Component: django.contrib.sessions | Version: SVN Resolution: | Keywords: Stage: Unreviewed | Has_patch: 0 Needs_docs: 0 | Needs_tests: 0 Needs_better_patch: 0 | ----------------------------------------------+----------------------------- Changes (by jsdalton):
* needs_better_patch: => 0 * needs_tests: => 0 * needs_docs: => 0 Comment: I found some time to investigate this more closely, and it would appear that this is a bug in the test code. In a brief nutshell, this is the body of the test method being run: {{{ def test_invalid_key(self): # Submitting an invalid session key (either by guessing, or if the db has # removed the key) results in a new key being generated. session = self.backend('1') session.save() self.assertNotEqual(session.session_key, '1') self.assertEqual(session.get('cat'), None) session.delete() }}} When the `save()` method is called, a new session object is created with that original session_key and saved to the backend. For example, this is what happens in the db store backend: 1. The session with `session_key` of '1' is saved via `save()`. A new `Session` object is then created. The `session_data` attribute of that objects is populated by an encoded version of the underlying session data, which is retrieved via `_get_session()`. `_get_session()` looks for a value in `_session_cache`, and seeing nothing, calls `load()`. `load()` checks the DB to see if an object with that original `session_key` of '1' exists. Seeing that this object does not exit, `load()` calls `create()`. At this point, a new `session_key` is generated, replacing the original value of '1' with an autogenerated string. 1. `save()` is now called again, this time with `must_create` set to `True`. Another `Session` object is now created, this time with the new `session_key` value. The `session_data` attribute is once again set as the encoded version of the underlying session data, again retrieved via `_get_session()`, however this time `_get_session()` is called with `no_load` set to `True`. As a result, `load()` is not called and the `session_cache` is set to `{}`, which is what `_get_session()` ultimately returns. Now we have actually saved a new Session record to the database meaning the original call to `create()` in step 1 has finally returned. That original call was in the `load()` method, which, after having finished the create process, now returns `{}` back to the original `_get_session()` call from way above. `_get_session()` sets the cache once again to `{}` and returns that value to the original original Session object being created when we first called `save()`. This, importantly, is our original object with `session_key` of '1'. That object is now in the database as well. The end result is that we now have *two* new Session objects in the database: one being the Session with key of '1', the other being the Session with the newly generated key. Importantly, only the newly created key exists in the Session object in memory (i.e. this is the value of `_session_key`). The old key of '1' is gone. So....where does that leave us? Well, at the end of the test method above, we call `session.delete()`. This deletes the Session with our newly generated key, but it leaves the Session object with session_key of '1' sitting in the database. Now, thanks to the django test suite, that object is erased from the DB when the db is flushed at the end of each test method, so it never comes back to bite us. For the file-backed session backend, there is a `tearDown()` method which flushes the file cache after each test, which is effectively the same result. For the cache however, we're not so lucky. The cache is not flushed after each test, so the problem is that session "record" with session_key of '1' is still sitting in the cache. In the `save()` sequence described above (which is pretty similar to what happens for the db), there is a call to `load()`which we would expect to find nothing in the cache. Here, it does find the old record hanging around. It returns this record instead of calling `create()`. `create()` is what generates the new session_key, and since it never gets called, a new key is never created. Thus, the tests fails. Being unfamiliar with the sessions application internals for the most part, I'm not certain what to conclude here. One conclusion and easy fix is just to add a `tearDown()` method to the cache-backed session store tests that flushes the cache after each test. This does "solve" the problem and all the tests pass. Essentially, this is the equivalent as what is happening in the db store and the file store test cases. The other conclusion is that perhaps something deeper is amiss? Maybe this is such an edge case that it doesn't matter, but it would appear that two records are being created when a session is saved and that one of them is being orphaned? Honestly my brain hurts too much from putting all of the above together to be sure of this at the moment. But I am fairly certain right now that the result of this test method is that records are being orphaned, and the only reason why this isn't apparent is that the test suite flushes the db and file set. If it's just a matter of fixing the tests by flushing the cache, that's a one-line patch I could upload. Anything more, someone more familiar with the sessions app might want to take a closer look. -- Ticket URL: <http://code.djangoproject.com/ticket/15026#comment:1> Django <http://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-upda...@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.