diff roundup/backends/rdbms_common.py @ 1173:58f1a2c174ed

simple LRU cache for SQL databases
author Richard Jones <richard@users.sourceforge.net>
date Thu, 19 Sep 2002 05:30:25 +0000
parents e7e56ad506de
children 8e318dfaf479
line wrap: on
line diff
--- a/roundup/backends/rdbms_common.py	Thu Sep 19 03:56:20 2002 +0000
+++ b/roundup/backends/rdbms_common.py	Thu Sep 19 05:30:25 2002 +0000
@@ -1,4 +1,4 @@
-# $Id: rdbms_common.py,v 1.5 2002-09-19 03:56:20 richard Exp $
+# $Id: rdbms_common.py,v 1.6 2002-09-19 05:30:25 richard Exp $
 
 # standard python modules
 import sys, os, time, re, errno, weakref, copy
@@ -13,7 +13,16 @@
 from roundup.indexer import Indexer
 from sessions import Sessions
 
+# number of rows to keep in memory
+ROW_CACHE_SIZE = 100
+
 class Database(FileStorage, hyperdb.Database, roundupdb.Database):
+    ''' Wrapper around an SQL database that presents a hyperdb interface.
+
+        - some functionality is specific to the actual SQL database, hence
+          the sql_* methods that are NotImplemented
+        - we keep a cache of the latest ROW_CACHE_SIZE row fetches.
+    '''
     # flag to set on retired entries
     RETIRED_FLAG = '__hyperdb_retired'
 
@@ -30,6 +39,11 @@
         # additional transaction support for external files and the like
         self.transactions = []
 
+        # keep a cache of the N most recently retrieved rows of any kind
+        # (classname, nodeid) = row
+        self.cache = {}
+        self.cache_lru = []
+
         # open a connection to the database, creating the "conn" attribute
         self.open_connection()
 
@@ -453,6 +467,13 @@
                 if not node.has_key(col):
                     node[col] = None
 
+        # clear this node out of the cache if it's in there
+        key = (classname, nodeid)
+        if self.cache.has_key(key):
+            del self.cache[key]
+            self.cache_lru.remove(key)
+
+        # make the node data safe for the DB
         node = self.serialise(classname, node)
 
         # make sure the ordering is correct for column name -> column value
@@ -483,6 +504,13 @@
         '''
         if __debug__:
             print >>hyperdb.DEBUG, 'setnode', (self, classname, nodeid, node)
+
+        # clear this node out of the cache if it's in there
+        key = (classname, nodeid)
+        if self.cache.has_key(key):
+            del self.cache[key]
+            self.cache_lru.remove(key)
+
         node = self.serialise(classname, node)
 
         cl = self.classes[classname]
@@ -531,6 +559,16 @@
         '''
         if __debug__:
             print >>hyperdb.DEBUG, 'getnode', (self, classname, nodeid)
+
+        # see if we have this node cached
+        key = (classname, nodeid)
+        if self.cache.has_key(key):
+            # push us back to the top of the LRU
+            self.cache_lru.remove(key)
+            self.cache_lry.insert(0, key)
+            # return the cached information
+            return self.cache[key]
+
         # figure the columns we're fetching
         cl = self.classes[classname]
         cols, mls = self.determine_columns(cl.properties.items())
@@ -559,7 +597,17 @@
             # extract the first column from the result
             node[col] = [x[0] for x in cursor.fetchall()]
 
-        return self.unserialise(classname, node)
+        # un-dbificate the node data
+        node = self.unserialise(classname, node)
+
+        # save off in the cache
+        key = (classname, nodeid)
+        self.cache[key] = node
+	# update the LRU
+	self.cache_lru.insert(0, key)
+	del self.cache[self.cache_lru.pop()]
+
+        return node
 
     def destroynode(self, classname, nodeid):
         '''Remove a node from the database. Called exclusively by the
@@ -572,6 +620,10 @@
         if not self.hasnode(classname, nodeid):
             raise IndexError, '%s has no node %s'%(classname, nodeid)
 
+        # see if we have this node cached
+        if self.cache.has_key((classname, nodeid)):
+            del self.cache[(classname, nodeid)]
+
         # see if there's any obvious commit actions that we should get rid of
         for entry in self.transactions[:]:
             if entry[1][:2] == (classname, nodeid):
@@ -1636,6 +1688,8 @@
                     if '-1' in v:
                         v.remove('-1')
                         xtra = ' or _%s is NULL'%k
+                    else:
+                        xtra = ''
                     s = ','.join([a for x in v])
                     where.append('(_%s in (%s)%s)'%(k, s, xtra))
                     args = args + v

Roundup Issue Tracker: http://roundup-tracker.org/