The session manager implementation, (see repoze.session.manager.SessionDataManager) stores session data objects (aka “session objects”, see repoze.session.data.SessionData) within a ZODB database in a singly-linked list (see repoze.session.linkedlist) of “buckets”, where each bucket is a tuple representing the creation timestamp of the tuple, and the OOBTree containing the session objects accessed during that time period:
+======================+ +============+ ?
| SessionDataManager | head | ListNode |------+
| |-----------| | |
| | 1 | |<-----+
+======================+ +============+ next
|
ob | 1
+=============+
| tuple |
| |
| |
+=============+
|
+----------+---------+
| |
+=============+ +=============+
| timestamp | | OOBTree |
| | | |
| | | |
+=============+ +=============+
When searching for a session object, the session first gets the current head bucket. If that head is older than the session manager’s period, the session manager creates a new bucket and pushes it onto the list as the new head (its ‘next’ is the old head).
The session manager then iterates through the list of buckets, until one of the following is true:
- The OOBTree in the bucket contains the key. In this case, the corresponding SessionData object is copied forward into the “head” bucket and returned.
- The timestamp in the current bucket is older than the session manager’s timeout; in this case, the session manager truncates the list, and then performs housekeeping (see “Linked-List Housekeeping” below) on the truncated tail of the list.
- The list ends before finding the key.
In either of the last two cases, the session manager creates a new session object for the key. If the session manager is marked as lazy, it sets a callback to copy the new SessionData object into the “head” bucket’s OOBTree when the transaction commits, if the SessionData object has been modified. If the session manager is not lazy, it copies the new SessionData object into the head bucket immediately. In either case, the session manager also sends an ISessionBeginEvent, passing the new SessionData object.
After discovering that a given bucket in its list represents an “expired” timeslice, the session manager truncates the list, setting the ‘next’ attribute of the oldest valid node to None. The session manager then processes the expired remainder as follows:
Near-simultaneous transactions may attempt to modify the linked list, either by pushing a new head bucket, or else by truncating the list at its oldest “valid” bucket.
In order to minimize the size of the pickle storing the linked list, the list class serializes itself as a native Python list of pointers to the objects.