initial commit
This commit is contained in:
commit
d4f400855f
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
*.py[co]
|
||||
*.pid
|
||||
*.log
|
||||
django_cache
|
28
LICENCE.txt
Normal file
28
LICENCE.txt
Normal file
@ -0,0 +1,28 @@
|
||||
Copyright (c) 2010, Ray Slakinski
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of the author nor the names of other
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
38
README.markdown
Normal file
38
README.markdown
Normal file
@ -0,0 +1,38 @@
|
||||
cassandra-sessions
|
||||
==================
|
||||
|
||||
This is a session backend for Django that stores sessions in Cassandra
|
||||
|
||||
The advantage to using this over other solutions is that your data is persistent
|
||||
unlike memcached, and Cassandra is designed to store key-value data like
|
||||
this, so performance is much closer to that of memcached than with a database.
|
||||
|
||||
Installing cassandra-sessions
|
||||
-----------------------------
|
||||
|
||||
1. Either download the tarball and run 'python setup.py install', or simply
|
||||
use easy install or pip like so 'easy_install cassandra-sessions'.
|
||||
|
||||
|
||||
2. Set 'cassandra-sessions' as your session engine, like so:
|
||||
|
||||
SESSION_ENGINE = 'cassandra-sessions'
|
||||
|
||||
|
||||
3. Add settings describing where to connect to Cassandra:
|
||||
|
||||
CASSANDRA_POOL = ('127.0.0.1:9160',)
|
||||
CASSANDRA_SESSION_KEYSPACE = 'Testing'
|
||||
|
||||
|
||||
4. Create the Keyspace and Column Family in Cassandra:
|
||||
|
||||
<Keyspace Name="Testing">
|
||||
<ColumnFamily Name="Sessions" CompareWith="UTF8Type"/>
|
||||
<ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy>
|
||||
<ReplicationFactor>1</ReplicationFactor>
|
||||
<EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch>
|
||||
</Keyspace>
|
||||
|
||||
That's it. Hopefully this backend gives you all the better performance while
|
||||
still not sacrificing persistence.
|
105
cassandra_sessions/__init__.py
Normal file
105
cassandra_sessions/__init__.py
Normal file
@ -0,0 +1,105 @@
|
||||
import lazyboy
|
||||
import time
|
||||
import logging
|
||||
from lazyboy.key import Key
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.encoding import force_unicode
|
||||
from django.contrib.sessions.backends.base import SessionBase, CreateError
|
||||
|
||||
CASSANDRA_POOL = getattr(settings, 'CASSANDRA_POOL', None)
|
||||
CASSANDRA_SESSION_KEYSPACE = getattr(settings, 'CASSANDRA_SESSION_KEYSPACE', None)
|
||||
|
||||
if CASSANDRA_POOL is None or CASSANDRA_SESSION_KEYSPACE is None:
|
||||
raise ImproperlyConfigured(u'To use cassandra-sessions, you must first set the CASSANDRA_SESSION_KEYSPACE and CASSANDRA_POOL settings in your settings.py')
|
||||
else:
|
||||
try:
|
||||
lazyboy.connection.get_pool(CASSANDRA_SESSION_KEYSPACE)
|
||||
except lazyboy.exceptions.ErrorCassandraClientNotFound:
|
||||
lazyboy.connection.add_pool(CASSANDRA_SESSION_KEYSPACE, CASSANDRA_POOL)
|
||||
|
||||
class SessionStore(SessionBase):
|
||||
"""
|
||||
A Cassandra-based session store.
|
||||
"""
|
||||
|
||||
def get_session(self, session_key):
|
||||
logging.debug('Getting session: %s' % session_key)
|
||||
session = None
|
||||
if session_key:
|
||||
try:
|
||||
key = Key(
|
||||
keyspace=CASSANDRA_SESSION_KEYSPACE,
|
||||
column_family="Sessions",
|
||||
key=session_key
|
||||
)
|
||||
record = lazyboy.record.Record()
|
||||
session_record = record.load(key)
|
||||
session_values = session_record.values()
|
||||
if session_values:
|
||||
session = session_values[0]
|
||||
except lazyboy.exceptions.ErrorNoSuchRecord:
|
||||
pass
|
||||
return session
|
||||
|
||||
def load(self):
|
||||
session_data = self.get_session(self.session_key)
|
||||
if session_data is not None:
|
||||
expiry, data = int(session_data[:15]), session_data[15:]
|
||||
if expiry < time.time():
|
||||
return {}
|
||||
else:
|
||||
return self.decode(force_unicode(data))
|
||||
self.create()
|
||||
return {}
|
||||
|
||||
def create(self):
|
||||
while True:
|
||||
self.session_key = self._get_new_session_key()
|
||||
try:
|
||||
self.save()
|
||||
except CreateError:
|
||||
continue
|
||||
self.modified = True
|
||||
return
|
||||
|
||||
def save(self, must_create=True):
|
||||
data = self.encode(self._get_session(no_load=True))
|
||||
encoded = '%15d%s' % (int(time.time()) + self.get_expiry_age(), data)
|
||||
key = Key(
|
||||
keyspace=CASSANDRA_SESSION_KEYSPACE,
|
||||
column_family="Sessions",
|
||||
key=self.session_key
|
||||
)
|
||||
record = lazyboy.record.Record()
|
||||
record.key = key
|
||||
record[self.session_key] = encoded
|
||||
record.save()
|
||||
|
||||
def exists(self, session_key):
|
||||
session_data = self.get_session(session_key)
|
||||
if session_data is not None:
|
||||
expiry, data = int(session_data[:15]), session_data[15:]
|
||||
if expiry < time.time():
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
def delete(self, session_key=None):
|
||||
if session_key is None:
|
||||
if self._session_key is None:
|
||||
return
|
||||
session_key = self._session_key
|
||||
key = Key(
|
||||
keyspace=CASSANDRA_SESSION_KEYSPACE,
|
||||
column_family="Sessions",
|
||||
key=session_key
|
||||
)
|
||||
# make this session expire right now
|
||||
data = self.encode({})
|
||||
encoded = '%15d%s' % (int(time.time()), data)
|
||||
record = lazyboy.record.Record()
|
||||
record.key = key
|
||||
record[self.session_key] = encoded
|
||||
record.save()
|
13
cassandra_sessions/settings.py
Normal file
13
cassandra_sessions/settings.py
Normal file
@ -0,0 +1,13 @@
|
||||
import lazyboy
|
||||
import logging
|
||||
|
||||
logging.basicConfig(
|
||||
level = logging.DEBUG,
|
||||
format = '[%(asctime)s] %(levelname)s: "%(message)s"',
|
||||
datefmt = '%d/%b/%Y %I:%M:%S'
|
||||
)
|
||||
|
||||
CASSANDRA_POOL = ('127.0.0.1:9160',)
|
||||
CASSANDRA_SESSION_KEYSPACE = 'Testing'
|
||||
|
||||
lazyboy.connection.add_pool(CASSANDRA_SESSION_KEYSPACE, CASSANDRA_POOL)
|
46
cassandra_sessions/test.py
Normal file
46
cassandra_sessions/test.py
Normal file
@ -0,0 +1,46 @@
|
||||
"""
|
||||
>>> from django.conf import settings
|
||||
>>> from cassandra_sessions import SessionStore as CassandraSession
|
||||
>>> cassandra_session = CassandraSession()
|
||||
>>> cassandra_session.modified
|
||||
False
|
||||
>>> cassandra_session.get('cat')
|
||||
>>> cassandra_session['cat'] = "dog"
|
||||
>>> cassandra_session.modified
|
||||
True
|
||||
>>> cassandra_session.pop('cat')
|
||||
'dog'
|
||||
>>> cassandra_session.pop('some key', 'does not exist')
|
||||
'does not exist'
|
||||
>>> cassandra_session.save()
|
||||
>>> cassandra_session.exists(cassandra_session.session_key)
|
||||
True
|
||||
>>> cassandra_session.delete(cassandra_session.session_key)
|
||||
>>> cassandra_session['foo'] = 'bar'
|
||||
>>> cassandra_session.save()
|
||||
>>> cassandra_session.exists(cassandra_session.session_key)
|
||||
True
|
||||
>>> prev_key = cassandra_session.session_key
|
||||
>>> cassandra_session.flush()
|
||||
>>> cassandra_session.exists(prev_key)
|
||||
False
|
||||
>>> cassandra_session.session_key == prev_key
|
||||
False
|
||||
>>> cassandra_session.modified, cassandra_session.accessed
|
||||
(True, True)
|
||||
>>> cassandra_session['a'], cassandra_session['b'] = 'c', 'd'
|
||||
>>> cassandra_session.save()
|
||||
>>> prev_key = cassandra_session.session_key
|
||||
>>> prev_data = cassandra_session.items()
|
||||
>>> cassandra_session.cycle_key()
|
||||
>>> cassandra_session.session_key == prev_key
|
||||
False
|
||||
>>> cassandra_session.items() == prev_data
|
||||
True
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
|
||||
import doctest
|
||||
doctest.testmod()
|
67
setup.py
Normal file
67
setup.py
Normal file
@ -0,0 +1,67 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
version = '0.1.0'
|
||||
|
||||
LONG_DESCRIPTION = """
|
||||
cassandra-sessions
|
||||
==================
|
||||
|
||||
This is a session backend for Django that stores sessions in Cassandra
|
||||
|
||||
The advantage to using this over other solutions is that your data is persistent
|
||||
unlike memcached, and Cassandra is designed to store key-value data like
|
||||
this, so performance is much closer to that of memcached than with a database.
|
||||
|
||||
Installing cassandra-sessions
|
||||
-----------------------------
|
||||
|
||||
1. Either download the tarball and run ``python setup.py install``, or simply
|
||||
use easy install or pip like so ``easy_install cassandra-sessions``.
|
||||
|
||||
|
||||
2. Set ``cassandra-sessions`` as your session engine, like so::
|
||||
|
||||
SESSION_ENGINE = 'cassandra-sessions'
|
||||
|
||||
|
||||
3. Add settings describing where to connect to Cassandra::
|
||||
|
||||
CASSANDRA_POOL = ('127.0.0.1:9160',)
|
||||
CASSANDRA_SESSION_KEYSPACE = 'Testing'
|
||||
|
||||
|
||||
4. Create the Keyspace and Column Family in Cassandra::
|
||||
|
||||
<Keyspace Name="Testing">
|
||||
<ColumnFamily Name="Sessions" CompareWith="UTF8Type"/>
|
||||
<ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy>
|
||||
<ReplicationFactor>1</ReplicationFactor>
|
||||
<EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch>
|
||||
</Keyspace>
|
||||
|
||||
That's it. Hopefully this backend gives you all the better performance while
|
||||
still not sacrificing persistence.
|
||||
"""
|
||||
|
||||
setup(
|
||||
name='cassandra-sessions',
|
||||
version=version,
|
||||
description="This is a session backend for Django that stores sessions in Cassandra.",
|
||||
long_description=LONG_DESCRIPTION,
|
||||
classifiers=[
|
||||
"Programming Language :: Python",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Framework :: Django",
|
||||
"Environment :: Web Environment",
|
||||
],
|
||||
keywords='cassandra,session,lazyboy,django',
|
||||
author='Ray Slakinski',
|
||||
author_email='ray.slakinski@gmail.com',
|
||||
url='http://github.com/rays/cassandra-sessions/tree/master',
|
||||
license='BSD',
|
||||
packages=find_packages(),
|
||||
zip_safe=False,
|
||||
install_requires=['setuptools'],
|
||||
include_package_data=True,
|
||||
setup_requires=['setuptools_git'],
|
||||
)
|
Reference in New Issue
Block a user