Skip to content
This repository was archived by the owner on Dec 28, 2017. It is now read-only.

Commit 2c98d4e

Browse files
author
Greg Taylor
committed
A handful of optimizations, now that boto's dynamodb support has evolved somewhat. Reduced the number of read/write ops needed by a bit.
1 parent ed94310 commit 2c98d4e

File tree

1 file changed

+57
-21
lines changed

1 file changed

+57
-21
lines changed

dynamodb_sessions/backends/dynamodb.py

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
import boto
1+
import os
2+
import time
23
import logging
3-
from boto.dynamodb.exceptions import DynamoDBKeyNotFoundError
4+
5+
import boto
46
from django.conf import settings
5-
from django.contrib.sessions.backends.base import SessionBase, CreateError
7+
from django.contrib.sessions.backends.base import SessionBase, CreateError, randrange, MAX_SESSION_KEY
8+
from django.utils.hashcompat import md5_constructor
9+
from boto.dynamodb.exceptions import DynamoDBKeyNotFoundError
10+
from boto.exception import DynamoDBResponseError
611

712
TABLE_NAME = getattr(
813
settings, 'DYNAMODB_SESSIONS_TABLE_NAME', 'sessions')
@@ -52,6 +57,25 @@ def __init__(self, session_key=None):
5257
super(SessionStore, self).__init__(session_key)
5358
self.table = dynamodb_connection_factory().get_table(TABLE_NAME)
5459

60+
def _get_new_session_key(self):
61+
"""
62+
Returns session key.
63+
"""
64+
# The random module is seeded when this Apache child is created.
65+
# Use settings.SECRET_KEY as added salt.
66+
try:
67+
pid = os.getpid()
68+
except AttributeError:
69+
# No getpid() in Jython, for example
70+
pid = 1
71+
72+
# Unlike the method this overrides, assume that the hash is good
73+
# to use. save(must_create=True) will make sure that this is
74+
# unique.
75+
return md5_constructor("%s%s%s%s"
76+
% (randrange(0, MAX_SESSION_KEY), pid, time.time(),
77+
settings.SECRET_KEY)).hexdigest()
78+
5579
def _get_or_create_session_key(self):
5680
"""
5781
This was added in Django 1.4 to SessionBase, but is simple enough to
@@ -107,8 +131,6 @@ def create(self):
107131
"""
108132
logger.debug("Creating a new session")
109133
while True:
110-
self.session_key = self._get_new_session_key()
111-
logger.debug(" - New session key: %s" % self.session_key)
112134
try:
113135
# Save immediately to ensure we have a unique entry in the
114136
# database.
@@ -131,26 +153,40 @@ def save(self, must_create=False):
131153
:raises: ``CreateError`` if ``must_create`` is ``True`` and a session
132154
with the current session key already exists.
133155
"""
134-
session_key = self._get_or_create_session_key()
135-
logger.debug("Saving session: %s" % session_key)
156+
# This base64 encodes session data.
157+
data = self.encode(self._get_session(no_load=must_create))
158+
136159
if must_create:
137-
logger.debug(" - Must create is True, checking for key first.")
138-
key_already_exists = self.table.has_item(
139-
session_key,
140-
consistent_read=ALWAYS_CONSISTENT
160+
# Force the generation of a new session key.
161+
self._session_key = self._get_new_session_key()
162+
logger.debug(" - Saving new session: %s" % self._session_key)
163+
item = self.table.new_item(
164+
self._session_key,
165+
# Stuff the base64 encoded stuff into the 'data' attrib.
166+
attrs={
167+
'data': data,
168+
# This will be used for session expiration.
169+
'created': int(time.time()),
170+
}
141171
)
142-
if key_already_exists:
172+
try:
173+
# We expect the 'data' attribute to not exist.
174+
item.put(expected_value={'data': False})
175+
except DynamoDBResponseError:
143176
# There's already an item with this key.
144177
raise CreateError
145-
146-
# This base64 encodes session data.
147-
data = self.encode(self._get_session(no_load=must_create))
148-
item = self.table.new_item(
149-
session_key,
150-
# Stuff the base64 encoded stuff into the 'data' attrib.
151-
attrs={'data': data}
152-
)
153-
item.put()
178+
else:
179+
self._session_key = self._get_or_create_session_key()
180+
logger.debug("Saving existing session: %s" % self._session_key)
181+
# This isn't really creating a new item, just a container for
182+
# us to use put_attribute to queue an attrib update to.
183+
item = self.table.new_item(self._session_key)
184+
# Queue up a PUT operation for UpdateItem, which preserves the
185+
# existing 'created' attribute.
186+
item.put_attribute('data', data)
187+
# Commits the PUT UpdateItem for the 'data' attrib, meanwhile
188+
# leaving the 'created' attrib un-touched.
189+
item.save()
154190

155191
def delete(self, session_key=None):
156192
"""

0 commit comments

Comments
 (0)