Add Redis cache.
authorrogelio <rogelio@rsrv.(none)>
Sat, 1 Oct 2011 05:48:21 +0000 (00:48 -0500)
committerJoshua Roesslein <jroesslein@gmail.com>
Sat, 1 Oct 2011 06:01:52 +0000 (01:01 -0500)
tweepy/cache.py

index f906d4b6535b3be5634e6be410c6226d861875ec..6630c49d5df0c6f5e9b3512719948cdb93fe59a4 100644 (file)
@@ -301,4 +301,83 @@ class MemCacheCache(Cache):
 
     def flush(self):
         """Delete all cached entries. NO-OP"""
-        pass
+        raise NotImplementedError
+
+class RedisCache(Cache):
+    '''Cache running in a redis server'''
+
+    def __init__(self, client, timeout=60, keys_container = 'tweepy:keys', pre_identifier = 'tweepy:'):
+        Cache.__init__(self, timeout)
+        self.client = client
+        self.keys_container = keys_container
+        self.pre_identifier = pre_identifier
+
+    def _is_expired(self, entry, timeout):
+        # Returns true if the entry has expired
+        return timeout > 0 and (time.time() - entry[0]) >= timeout
+
+    def store(self, key, value):
+        '''Store the key, value pair in our redis server'''
+        # Prepend tweepy to our key, this makes it easier to identify tweepy keys in our redis server
+        key = self.pre_identifier + key
+        # Get a pipe (to execute several redis commands in one step)
+        pipe = self.client.pipeline()
+        # Set our values in a redis hash (similar to python dict)
+        pipe.set(key, pickle.dumps((time.time(), value)))
+        # Set the expiration
+        pipe.expire(key, self.timeout)
+        # Add the key to a set containing all the keys
+        pipe.sadd(self.keys_container, key)
+        # Execute the instructions in the redis server
+        pipe.execute()
+
+    def get(self, key, timeout=None):
+        '''Given a key, returns an element from the redis table'''
+        key = self.pre_identifier + key
+        # Check to see if we have this key
+        unpickled_entry = self.client.get(key)
+        if not unpickled_entry:
+            # No hit, return nothing
+            return None
+
+        entry = pickle.loads(unpickled_entry)
+        # Use provided timeout in arguments if provided
+        # otherwise use the one provided during init.
+        if timeout is None:
+            timeout = self.timeout
+
+        # Make sure entry is not expired
+        if self._is_expired(entry, timeout):
+            # entry expired, delete and return nothing
+            self.delete_entry(key)
+            return None
+        # entry found and not expired, return it
+        return entry[1]
+
+    def count(self):
+        '''Note: This is not very efficient, since it retreives all the keys from the redis
+        server to know how many keys we have'''
+        return len(self.client.smembers(self.keys_container))
+
+    def delete_entry(self, key):
+        '''Delete an object from the redis table'''
+        pipe = self.client.pipeline()
+        pipe.srem(self.keys_container, key)
+        pipe.delete(key)
+        pipe.execute()
+
+    def cleanup(self):
+        '''Cleanup all the expired keys'''
+        keys = self.client.smembers(self.keys_container)
+        for key in keys:
+            entry = self.client.get(key)
+            if entry:
+                entry = pickle.loads(entry)
+                if self._is_expired(entry, self.timeout):
+                    self.delete_entry(key)
+
+    def flush(self):
+        '''Delete all entries from the cache'''
+        keys = self.client.smembers(self.keys_container)
+        for key in keys:
+            self.delete_entry(key)