SHARE
TWEET

Redis sessions in Nagare

Crumble Jun 18th, 2013 80 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import redis
  2.  
  3. from nagare import local
  4. from nagare.sessions import ExpirationError, common
  5.  
  6. KEY_PREFIX = 'nagare_'
  7.  
  8.  
  9. class Sessions(common.Sessions):
  10.     """Sessions manager for sessions kept in an external redis server
  11.    """
  12.     spec = dict(
  13.         host='string(default="127.0.0.1")',
  14.         port='integer(default=6379)',
  15.         db='integer(default=0)',
  16.         ttl='integer(default=0)',
  17.         lock_ttl='integer(default=0)',
  18.         lock_poll_time='float(default=0.1)',
  19.         reset='boolean(default=True)'
  20.     )
  21.     spec.update(common.Sessions.spec)
  22.  
  23.     def __init__(
  24.         self,
  25.         host='127.0.0.1',
  26.         port=6379,
  27.         db=0,
  28.         ttl=0,
  29.         lock_ttl=5,
  30.         lock_poll_time=0.1,
  31.         reset=False,
  32.         **kw
  33.     ):
  34.         """Initialization
  35.  
  36.        In:
  37.          - ``host`` -- address of the redis server
  38.          - ``port`` -- port of the redis server
  39.          - ``db`` -- redis database id
  40.        """
  41.         super(Sessions, self).__init__(**kw)
  42.  
  43.         self.host = host
  44.         self.port = port
  45.         self.db = db
  46.         self.ttl = ttl
  47.         self.lock_ttl = lock_ttl
  48.         self.lock_poll_time = lock_poll_time
  49.  
  50.         if reset:
  51.             self.flush_all()
  52.  
  53.     def set_config(self, filename, conf, error):
  54.         """Read the configuration parameters
  55.  
  56.        In:
  57.          - ``filename`` -- the path to the configuration file
  58.          - ``conf`` -- the ``ConfigObj`` object, created from the
  59.                        configuration file
  60.          - ``error`` -- the function to call in case of configuration errors
  61.        """
  62.         # Let's the super class validate the configuration file
  63.         conf = super(Sessions, self).set_config(filename, conf, error)
  64.  
  65.         for arg_name in (
  66.             'host', 'port', 'db', 'ttl', 'lock_ttl',
  67.                             'lock_poll_time', 'lock_max_wait_time',
  68.                             'min_compress_len', 'debug'
  69.         ):
  70.             setattr(self, arg_name, conf[arg_name])
  71.  
  72.         if conf['reset']:
  73.             self.flush_all()
  74.  
  75.     def _get_connection(self):
  76.         """Get the connection to the redis server
  77.  
  78.        Return:
  79.          - the connection
  80.        """
  81.         # The connection objects are local to the workers
  82.         connection = getattr(local.worker, 'redis_connection', None)
  83.  
  84.         if connection is None:
  85.             connection = redis.Redis(self.host, self.port, self.db)
  86.             local.worker.redis_connection = connection
  87.  
  88.         return connection
  89.  
  90.     def flush_all(self):
  91.         """Delete all the contents in the redis server
  92.        """
  93.         connection = self._get_connection()
  94.         connection.flushdb()
  95.  
  96.     def _create(self, session_id, secure_id):
  97.         """Create a new session
  98.  
  99.        In:
  100.          - ``session_id`` -- id of the session
  101.          - ``secure_id`` -- the secure number associated to the session
  102.  
  103.        Return:
  104.          - the tuple:
  105.            - id of this state,
  106.            - session lock
  107.        """
  108.  
  109.         connection, lock = self._get_lock(session_id)
  110.  
  111.         connection = connection.pipeline()
  112.  
  113.         connection.hmset(KEY_PREFIX + session_id, {
  114.                          '_sess_id': secure_id,
  115.                          '_sess_data': None,
  116.                          '_state': '0',
  117.                          '00000': {}
  118.                          })
  119.         if self.ttl:
  120.             connection.expire(KEY_PREFIX + session_id, self.ttl)
  121.  
  122.         connection.execute()
  123.  
  124.         return (0, lock)
  125.  
  126.     def _get_lock(self, session_id):
  127.         connection = self._get_connection()
  128.         lock = connection.lock('%slock_%s' % (KEY_PREFIX, session_id),
  129.                                self.lock_ttl,
  130.                                self.lock_poll_time)
  131.         lock.acquire()
  132.         return connection, lock
  133.  
  134.     def _get(self, session_id, state_id, use_same_state):
  135.         """Retrieve the state
  136.  
  137.        In:
  138.          - ``session_id`` -- session id of this state
  139.          - ``state_id`` -- id of this state
  140.          - ``use_same_state`` -- is a copy of this state to create ?
  141.  
  142.        Return:
  143.          - the tuple:
  144.            - id of this state,
  145.            - session lock,
  146.            - secure number associated to the session,
  147.            - data kept into the session
  148.            - data kept into the state
  149.        """
  150.         connection, lock = self._get_lock(session_id)
  151.  
  152.         state_id = state_id.zfill(5)
  153.  
  154.         secure_id, session_data, last_state_id, state_data = connection.hmget(
  155.             KEY_PREFIX + session_id,
  156.             ('_sess_id', '_sess_data', '_state', state_id)
  157.         )
  158.  
  159.         if not (secure_id and session_data and last_state_id and state_data):
  160.             raise ExpirationError()
  161.  
  162.         if not use_same_state:
  163.             state_id = last_state_id
  164.  
  165.         return (int(state_id), lock, secure_id, session_data, state_data)
  166.  
  167.     def _set(self, session_id, state_id, secure_id, use_same_state,
  168.              session_data, state_data):
  169.         """Store the state
  170.  
  171.        In:
  172.          - ``session_id`` -- session id of this state
  173.          - ``state_id`` -- id of this state
  174.          - ``secure_id`` -- the secure number associated to the session
  175.          - ``use_same_state`` -- is this state to be stored in the
  176.                                  previous snapshot ?
  177.          - ``session_data`` -- data keept into the session
  178.          - ``state_data`` -- data keept into the state
  179.        """
  180.         connection = self._get_connection()
  181.  
  182.         connection = connection.pipeline(True)
  183.  
  184.         if not use_same_state:
  185.             connection.hincrby(KEY_PREFIX + session_id, '_state', 1)
  186.  
  187.         connection.hmset(KEY_PREFIX + session_id, {
  188.             '_sess_id': secure_id,
  189.             '_sess_data': session_data,
  190.             '%05d' % state_id: state_data
  191.         })
  192.  
  193.         if self.ttl:
  194.             connection.expire(KEY_PREFIX + session_id, self.ttl)
  195.  
  196.         connection.execute()
  197.  
  198.     def _delete(self, session_id):
  199.         """Delete the session
  200.  
  201.        In:
  202.          - ``session_id`` -- id of the session to delete
  203.        """
  204.         self._get_connection().delete(KEY_PREFIX + session_id)
  205.  
  206.     def serialize(self, data):
  207.         """Pickle an objects graph
  208.  
  209.        In:
  210.          - ``data`` -- the objects graphs
  211.  
  212.        Return:
  213.          - the tuple:
  214.            - data to keep into the session
  215.            - data to keep into the state
  216.        """
  217.         return self.pickle(data)
  218.  
  219.     def deserialize(self, session_data, state_data):
  220.         """Unpickle an objects graph
  221.  
  222.        In:
  223.          - ``session_data`` -- data from the session
  224.          - ``state_data`` -- data from the state
  225.  
  226.        Out:
  227.          - the objects graph
  228.        """
  229.         return self.unpickle(session_data, state_data)
RAW Paste Data
Top