Skip to content

Commit 60f2ef2

Browse files
author
Richard Jones
committed
Database transactions.
. We now have basic transaction support! Information is only written to the database when the commit() method is called. Only the anydbm backend is modified in this way - neither of the bsddb backends have been. The mail, admin and cgi interfaces all use commit (except the admin tool doesn't have a commit command, so interactive users can't commit...) . Fixed login/registration forwarding the user to the right page (or not, on a failure)
1 parent 7f60ba3 commit 60f2ef2

File tree

8 files changed

+278
-106
lines changed

8 files changed

+278
-106
lines changed

CHANGES.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ Feature:
1010
. Some more flexibility in the mail gateway and more error handling.
1111
. Login now takes you to the page you back to the were denied access to.
1212
. Admin user now can has a user index link on their web interface.
13+
. We now have basic transaction support. Information is only written to
14+
the database when the commit() method is called. Only the anydbm backend
15+
is modified in this way - neither of the bsddb backends have been.
1316

1417
Fixed:
1518
. Lots of bugs, thanks Roch� and others on the devel mailing list!
1619
. login_action and newuser_action return values were being ignored
1720
. Woohoo! Found that bloody re-login bug that was killing the mail
1821
gateway.
22+
. Fixed login/registration forwarding the user to the right page (or not,
23+
on a failure)
1924

2025

2126
2001-11-23 - 0.3.0

roundup-admin

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1717
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1818
#
19-
# $Id: roundup-admin,v 1.48 2001-11-27 22:32:03 richard Exp $
19+
# $Id: roundup-admin,v 1.49 2001-12-01 07:17:50 richard Exp $
2020

2121
import sys
2222
if int(sys.version[0]) < 2:
@@ -882,8 +882,8 @@ Command help:
882882
self.interactive()
883883
else:
884884
ret = self.run_command(args)
885-
if self.db:
886-
self.db.close()
885+
if self.db: self.db.commit()
886+
if self.db: self.db.close()
887887
return ret
888888

889889

@@ -893,6 +893,9 @@ if __name__ == '__main__':
893893

894894
#
895895
# $Log: not supported by cvs2svn $
896+
# Revision 1.48 2001/11/27 22:32:03 richard
897+
# typo
898+
#
896899
# Revision 1.47 2001/11/26 22:55:56 richard
897900
# Feature:
898901
# . Added INSTANCE_NAME to configuration - used in web and email to identify

roundup/backends/back_anydbm.py

Lines changed: 92 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
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

2020
import anydbm, os, marshal
2121
from roundup import hyperdb, date, password
@@ -24,7 +24,14 @@
2424
# Now the database
2525
#
2626
class 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

Comments
 (0)