1515# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717#
18- #$Id: back_anydbm.py,v 1.11 2001-11-21 02:34:18 richard Exp $
18+ #$Id: back_anydbm.py,v 1.12 2001-12-01 07:17:50 richard Exp $
1919
2020import anydbm , os , marshal
2121from roundup import hyperdb , date , password
2424# Now the database
2525#
2626class Database (hyperdb .Database ):
27- """A database for storing records containing flexible data types."""
27+ """A database for storing records containing flexible data types.
28+
29+ Transaction stuff TODO:
30+ . check the timestamp of the class file and nuke the cache if it's
31+ modified. Do some sort of conflict checking on the dirty stuff.
32+ . perhaps detect write collisions (related to above)?
33+
34+ """
2835
2936 def __init__ (self , storagelocator , journaltag = None ):
3037 """Open a hyperdatabase given a specifier to some storage.
@@ -41,8 +48,10 @@ def __init__(self, storagelocator, journaltag=None):
4148 """
4249 self .dir , self .journaltag = storagelocator , journaltag
4350 self .classes = {}
51+ self .cache = {} # cache of nodes loaded or created
52+ self .dirtynodes = {} # keep track of the dirty nodes by class
53+ self .newnodes = {} # keep track of the new nodes by class
4454 self .transactions = []
45-
4655 #
4756 # Classes
4857 #
@@ -95,39 +104,70 @@ def getclassdb(self, classname, mode='r'):
95104 def addnode (self , classname , nodeid , node ):
96105 ''' add the specified node to its class's db
97106 '''
98- db = self .getclassdb (classname , 'c' )
99- # now save the marshalled data
100- db [nodeid ] = marshal .dumps (node )
101- db .close ()
102- setnode = addnode
107+ self .newnodes .setdefault (classname , {})[nodeid ] = 1
108+ self .cache .setdefault (classname , {})[nodeid ] = node
109+ self .savenode (classname , nodeid , node )
110+
111+ def setnode (self , classname , nodeid , node ):
112+ ''' change the specified node
113+ '''
114+ self .dirtynodes .setdefault (classname , {})[nodeid ] = 1
115+ # can't set without having already loaded the node
116+ self .cache [classname ][nodeid ] = node
117+ self .savenode (classname , nodeid , node )
118+
119+ def savenode (self , classname , nodeid , node ):
120+ ''' perform the saving of data specified by the set/addnode
121+ '''
122+ self .transactions .append ((self ._doSaveNode , (classname , nodeid , node )))
103123
104124 def getnode (self , classname , nodeid , cldb = None ):
105125 ''' add the specified node to its class's db
106126 '''
127+ # try the cache
128+ cache = self .cache .setdefault (classname , {})
129+ if cache .has_key (nodeid ):
130+ return cache [nodeid ]
131+
132+ # get from the database and save in the cache
107133 db = cldb or self .getclassdb (classname )
108134 if not db .has_key (nodeid ):
109135 raise IndexError , nodeid
110136 res = marshal .loads (db [nodeid ])
111137 if not cldb : db .close ()
138+ cache [nodeid ] = res
112139 return res
113140
114141 def hasnode (self , classname , nodeid , cldb = None ):
115142 ''' add the specified node to its class's db
116143 '''
144+ # try the cache
145+ cache = self .cache .setdefault (classname , {})
146+ if cache .has_key (nodeid ):
147+ return 1
148+
149+ # not in the cache - check the database
117150 db = cldb or self .getclassdb (classname )
118151 res = db .has_key (nodeid )
119152 if not cldb : db .close ()
120153 return res
121154
122155 def countnodes (self , classname , cldb = None ):
156+ # include the new nodes not saved to the DB yet
157+ count = len (self .newnodes .get (classname , {}))
158+
159+ # and count those in the DB
123160 db = cldb or self .getclassdb (classname )
124- return len (db .keys ())
161+ count = count + len (db .keys ())
125162 if not cldb : db .close ()
126- return res
163+ return count
127164
128165 def getnodeids (self , classname , cldb = None ):
166+ # start off with the new nodes
167+ res = self .newnodes .get (classname , {}).keys ()
168+
129169 db = cldb or self .getclassdb (classname )
130- res = db .keys ()
170+ res = res + db .keys ()
131171 if not cldb : db .close ()
132172 return res
133173
@@ -142,17 +182,8 @@ def addjournal(self, classname, nodeid, action, params):
142182 'link' or 'unlink' -- 'params' is (classname, nodeid, propname)
143183 'retire' -- 'params' is None
144184 '''
145- entry = (nodeid , date .Date ().get_tuple (), self .journaltag , action ,
146- params )
147- db = anydbm .open (os .path .join (self .dir , 'journals.%s' % classname ), 'c' )
148- if db .has_key (nodeid ):
149- s = db [nodeid ]
150- l = marshal .loads (db [nodeid ])
151- l .append (entry )
152- else :
153- l = [entry ]
154- db [nodeid ] = marshal .dumps (l )
155- db .close ()
185+ self .transactions .append ((self ._doSaveJournal , (classname , nodeid ,
186+ action , params )))
156187
157188 def getjournal (self , classname , nodeid ):
158189 ''' get the journal for id
@@ -175,9 +206,10 @@ def getjournal(self, classname, nodeid):
175206 return res
176207
177208 def close (self ):
178- ''' Close the Database - we must release the circular refs so that
179- we can be del'ed and the underlying anydbm connections closed
180- cleanly.
209+ ''' Close the Database.
210+
211+ Commit all data to the database and release circular refs so
212+ the database is closed cleanly.
181213 '''
182214 self .classes = {}
183215
@@ -189,18 +221,50 @@ def commit(self):
189221 ''' Commit the current transactions.
190222 '''
191223 # lock the DB
192- for action , classname , entry in self .transactions :
193- # write the node, figure what's changed for the journal.
194- pass
224+ for method , args in self .transactions :
225+ print method .__name__ , args
226+ # TODO: optimise this, duh!
227+ method (* args )
195228 # unlock the DB
196229
230+ # all transactions committed, back to normal
231+ self .cache = {}
232+ self .dirtynodes = {}
233+ self .newnodes = {}
234+ self .transactions = []
235+
236+ def _doSaveNode (self , classname , nodeid , node ):
237+ db = self .getclassdb (classname , 'c' )
238+ # now save the marshalled data
239+ db [nodeid ] = marshal .dumps (node )
240+ db .close ()
241+
242+ def _doSaveJournal (self , classname , nodeid , action , params ):
243+ entry = (nodeid , date .Date ().get_tuple (), self .journaltag , action ,
244+ params )
245+ db = anydbm .open (os .path .join (self .dir , 'journals.%s' % classname ), 'c' )
246+ if db .has_key (nodeid ):
247+ s = db [nodeid ]
248+ l = marshal .loads (db [nodeid ])
249+ l .append (entry )
250+ else :
251+ l = [entry ]
252+ db [nodeid ] = marshal .dumps (l )
253+ db .close ()
254+
197255 def rollback (self ):
198256 ''' Reverse all actions from the current transaction.
199257 '''
258+ self .cache = {}
259+ self .dirtynodes = {}
260+ self .newnodes = {}
200261 self .transactions = []
201262
202263#
203264#$Log: not supported by cvs2svn $
265+ #Revision 1.11 2001/11/21 02:34:18 richard
266+ #Added a target version field to the extended issue schema
267+ #
204268#Revision 1.10 2001/10/09 23:58:10 richard
205269#Moved the data stringification up into the hyperdb.Class class' get, set
206270#and create methods. This means that the data is also stringified for the
0 commit comments