[Zodb-checkins] CVS: StandaloneZODB/ZEO - ClientCache.py:1.20 ClientStorage.py:1.37 StorageServer.py:1.34 smac.py:1.13 start.py:1.28 ClientStub.py:NONE Exceptions.py:NONE ServerStub.py:NONE TransactionBuffer.py:NONE zrpc2.py:NONE
Jeremy Hylton
jeremy@zope.com
Fri, 11 Jan 2002 14:56:03 -0500
Update of /cvs-repository/StandaloneZODB/ZEO
In directory cvs.zope.org:/tmp/cvs-serv3118
Modified Files:
ClientCache.py ClientStorage.py StorageServer.py smac.py
start.py
Removed Files:
ClientStub.py Exceptions.py ServerStub.py TransactionBuffer.py
zrpc2.py
Log Message:
Undo merge of ZEO-ZRPC-Dev branch into the trunk (for now).
=== StandaloneZODB/ZEO/ClientCache.py 1.19 => 1.20 ===
from struct import pack, unpack
from thread import allocate_lock
-
-import sys
import zLOG
-def log(msg, level=zLOG.INFO):
- zLOG.LOG("ZEC", level, msg)
-
magic='ZEC0'
+def LOG(msg, level=zLOG.BLATHER):
+ zLOG.LOG("ZEC", level, msg)
+
class ClientCache:
def __init__(self, storage='', size=20000000, client=None, var=None):
@@ -213,14 +211,16 @@
f[0].write(magic)
current=0
- log("cache opened. current = %s" % current)
-
self._limit=size/2
self._current=current
+ def close(self):
+ try:
+ self._f[self._current].close()
+ except (os.error, ValueError):
+ pass
+
def open(self):
- # XXX open is overloaded to perform two tasks for
- # optimization reasons
self._acquire()
try:
self._index=index={}
@@ -235,19 +235,6 @@
return serial.items()
finally: self._release()
- def close(self):
- for f in self._f:
- if f is not None:
- f.close()
-
- def verify(self, verifyFunc):
- """Call the verifyFunc on every object in the cache.
-
- verifyFunc(oid, serialno, version)
- """
- for oid, (s, vs) in self.open():
- verifyFunc(oid, s, vs)
-
def invalidate(self, oid, version):
self._acquire()
try:
@@ -386,6 +373,8 @@
self._f[current]=open(self._p[current],'w+b')
else:
# Temporary cache file:
+ if self._f[current] is not None:
+ self._f[current].close()
self._f[current] = tempfile.TemporaryFile(suffix='.zec')
self._f[current].write(magic)
self._pos=pos=4
@@ -394,57 +383,55 @@
def store(self, oid, p, s, version, pv, sv):
self._acquire()
- try:
- self._store(oid, p, s, version, pv, sv)
- finally:
- self._release()
+ try: self._store(oid, p, s, version, pv, sv)
+ finally: self._release()
def _store(self, oid, p, s, version, pv, sv):
if not s:
- p = ''
- s = '\0\0\0\0\0\0\0\0'
- tlen = 31 + len(p)
+ p=''
+ s='\0\0\0\0\0\0\0\0'
+ tlen=31+len(p)
if version:
- tlen = tlen + len(version) + 12 + len(pv)
- vlen = len(version)
+ tlen=tlen+len(version)+12+len(pv)
+ vlen=len(version)
else:
- vlen = 0
+ vlen=0
- stlen = pack(">I", tlen)
- # accumulate various data to write into a list
- l = [oid, 'v', stlen, pack(">HI", vlen, len(p)), s]
- if p:
- l.append(p)
+ pos=self._pos
+ current=self._current
+ f=self._f[current]
+ f.seek(pos)
+ stlen=pack(">I",tlen)
+ write=f.write
+ write(oid+'v'+stlen+pack(">HI", vlen, len(p))+s)
+ if p: write(p)
if version:
- l.extend([version,
- pack(">I", len(pv)),
- pv, sv])
- l.append(stlen)
- f = self._f[self._current]
- f.seek(self._pos)
- f.write("".join(l))
+ write(version)
+ write(pack(">I", len(pv)))
+ write(pv)
+ write(sv)
- if self._current:
- self._index[oid] = - self._pos
- else:
- self._index[oid] = self._pos
+ write(stlen)
+
+ if current: self._index[oid]=-pos
+ else: self._index[oid]=pos
- self._pos += tlen
+ self._pos=pos+tlen
def read_index(index, serial, f, current):
+ LOG("read_index(%s)" % f.name)
seek=f.seek
read=f.read
pos=4
- seek(0,2)
- size=f.tell()
while 1:
- f.seek(pos)
+ seek(pos)
h=read(27)
-
+
if len(h)==27 and h[8] in 'vni':
tlen, vlen, dlen = unpack(">iHi", h[9:19])
- else: tlen=-1
+ else:
+ break
if tlen <= 0 or vlen < 0 or dlen < 0 or vlen+dlen > tlen:
break
@@ -479,3 +466,15 @@
except: pass
return pos
+
+def main(files):
+ for file in files:
+ print file
+ index = {}
+ serial = {}
+ read_index(index, serial, open(file), 0)
+ print index.keys()
+
+if __name__ == "__main__":
+ import sys
+ main(sys.argv[1:])
=== StandaloneZODB/ZEO/ClientStorage.py 1.36 => 1.37 === (868/968 lines abridged)
##############################################################################
"""Network ZODB storage client
-
-XXX support multiple outstanding requests up until the vote?
-XXX is_connected() vis ClientDisconnected error
"""
-__version__='$Revision$'[11:-2]
-import cPickle
-import os
-import socket
-import string
-import struct
-import sys
-import tempfile
-import thread
-import threading
-import time
-from types import TupleType, StringType
-from struct import pack, unpack
+__version__='$Revision$'[11:-2]
-import ExtensionClass, Sync, ThreadLock
-import ClientCache
-import zrpc2
-import ServerStub
-from TransactionBuffer import TransactionBuffer
+import struct, time, os, socket, string, Sync, zrpc, ClientCache
+import tempfile, Invalidator, ExtensionClass, thread
+import ThreadedAsync
-from ZODB import POSException
+now=time.time
+from struct import pack, unpack
+from ZODB import POSException, BaseStorage
from ZODB.TimeStamp import TimeStamp
-from zLOG import LOG, PROBLEM, INFO, BLATHER
-from Exceptions import Disconnected
+from zLOG import LOG, PROBLEM, INFO
-def log2(type, msg, subsys="ClientStorage %d" % os.getpid()):
- LOG(subsys, type, msg)
+try: from ZODB.ConflictResolution import ResolvedSerial
+except: ResolvedSerial='rs'
-try:
- from ZODB.ConflictResolution import ResolvedSerial
-except ImportError:
- ResolvedSerial = 'rs'
+TupleType=type(())
[-=- -=- -=- 868 lines omitted -=- -=- -=-]
- def begin(self):
- self._tfile = tempfile.TemporaryFile()
- self._pickler = cPickle.Pickler(self._tfile, 1)
- self._pickler.fast = 1 # Don't use the memo
-
- def invalidate(self, args):
- if self._pickler is None:
- return
- self._pickler.dump(args)
-
- def end(self):
- if self._pickler is None:
- return
- self._pickler.dump((0,0))
-## self._pickler.dump = None
- self._tfile.seek(0)
- unpick = cPickle.Unpickler(self._tfile)
- self._tfile = None
-
- while 1:
- oid, version = unpick.load()
- if not oid:
- break
- self._cache.invalidate(oid, version=version)
- self._db.invalidate(oid, version=version)
-
- def Invalidate(self, args):
- # XXX _db could be None
- for oid, version in args:
- self._cache.invalidate(oid, version=version)
- try:
- self._db.invalidate(oid, version=version)
- except AttributeError, msg:
- log2(PROBLEM,
- "Invalidate(%s, %s) failed for _db: %s" % (repr(oid),
- repr(version),
- msg))
-
+ self._lock_acquire()
+ try: return self._call('versions', max)
+ finally: self._lock_release()
+
+ def sync(self): self._call.sync()
+
+def getWakeup(_w=[]):
+ if _w: return _w[0]
+ import trigger
+ t=trigger.trigger().pull_trigger
+ _w.append(t)
+ return t
=== StandaloneZODB/ZEO/StorageServer.py 1.33 => 1.34 === (750/850 lines abridged)
+#############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
@@ -59,7 +59,7 @@
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
-#
+#
#
# Disclaimer
#
@@ -82,394 +82,527 @@
# attributions are listed in the accompanying credits file.
#
##############################################################################
-"""Network ZODB storage server
-This server acts as a front-end for one or more real storages, like
-file storage or Berkeley storage.
+__version__ = "$Revision$"[11:-2]
-XXX Need some basic access control-- a declaration of the methods
-exported for invocation by the server.
-"""
-
-import asyncore
+import asyncore, socket, string, sys, os
+from smac import SizedMessageAsyncConnection
+from ZODB import POSException
import cPickle
-import os
-import sys
-import threading
-import types
-
-import ClientStub
-import zrpc2
-import zLOG
-
-from zrpc2 import Dispatcher, Handler, ManagedServerConnection, Delay
-from ZODB.POSException import StorageError, StorageTransactionError, \
- TransactionError, ReadOnlyError
-from ZODB.referencesf import referencesf
+from cPickle import Unpickler
+from ZODB.POSException import TransactionError, UndoError, VersionCommitError
from ZODB.Transaction import Transaction
+import traceback
+from zLOG import LOG, INFO, ERROR, TRACE, BLATHER
[-=- -=- -=- 750 lines omitted -=- -=- -=-]
- assert self.__storage._transaction is None
-
- if not self._handle_waiting():
- self._transaction = None
- self.__invalidated = []
-
- def _restart_delayed_transaction(self, delay, trans):
- self._transaction = trans
- self.__storage.tpc_begin(trans)
- self.__invalidated = []
- assert self._transaction.id == self.__storage._transaction.id
- delay.reply(None)
-
- def _handle_waiting(self):
- if self.__storage.__waiting:
- delay, proxy, trans = self.__storage.__waiting.pop(0)
- proxy._restart_delayed_transaction(delay, trans)
- if self is proxy:
- return 1
-
- def new_oids(self, n=100):
- """Return a sequence of n new oids, where n defaults to 100"""
- if n < 0:
- n = 1
- return [self.__storage.new_oid() for i in range(n)]
-
-def fixup_storage(storage):
- # backwards compatibility hack
- if not hasattr(storage,'tpc_vote'):
- storage.tpc_vote = lambda *args: None
+ self.__server.invalidate(self, self.__storage_id,
+ self.__invalidated,
+ self.get_size_info())
+ self.__invalidated=[]
+
+def init_storage(storage):
+ if not hasattr(storage,'tpc_vote'): storage.tpc_vote=lambda *args: None
+
+if __name__=='__main__':
+ import ZODB.FileStorage
+ name, port = sys.argv[1:3]
+ blather(name, port)
+ try:
+ port='', int(port)
+ except:
+ pass
+
+ d = {'1': ZODB.FileStorage.FileStorage(name)}
+ StorageServer(port, d)
+ asyncwrap.loop()
=== StandaloneZODB/ZEO/smac.py 1.12 => 1.13 ===
__version__ = "$Revision$"[11:-2]
-import asyncore, struct
-from Exceptions import Disconnected
-from zLOG import LOG, TRACE, ERROR, INFO, BLATHER
-from types import StringType
-
+import asyncore, string, struct, zLOG, sys, Acquisition
import socket, errno
+from zLOG import LOG, TRACE, ERROR, INFO
# Use the dictionary to make sure we get the minimum number of errno
# entries. We expect that EWOULDBLOCK == EAGAIN on most systems --
@@ -112,101 +109,81 @@
expected_socket_write_errors = tuple(tmp_dict.keys())
del tmp_dict
-class SizedMessageAsyncConnection(asyncore.dispatcher):
- __super_init = asyncore.dispatcher.__init__
- __super_close = asyncore.dispatcher.close
-
- __closed = 1 # Marker indicating that we're closed
+class SizedMessageAsyncConnection(Acquisition.Explicit, asyncore.dispatcher):
- socket = None # to outwit Sam's getattr
+ __append=None # Marker indicating that we're closed
- READ_SIZE = 8096
+ socket=None # to outwit Sam's getattr
def __init__(self, sock, addr, map=None, debug=None):
- self.__super_init(sock, map)
- self.addr = addr
+ SizedMessageAsyncConnection.inheritedAttribute(
+ '__init__')(self, sock, map)
+ self.addr=addr
if debug is not None:
- self._debug = debug
+ self._debug=debug
elif not hasattr(self, '_debug'):
- self._debug = __debug__ and 'smac'
- self.__state = None
- self.__inp = None # None, a single String, or a list
- self.__input_len = 0
- self.__msg_size = 4
- self.__output = []
- self.__closed = None
-
- # XXX avoid expensive getattr calls?
- def __nonzero__(self):
- return 1
-
- def handle_read(self):
- # Use a single __inp buffer and integer indexes to make this
- # fast.
+ self._debug=__debug__ and 'smac'
+ self.__state=None
+ self.__inp=None
+ self.__inpl=0
+ self.__l=4
+ self.__output=output=[]
+ self.__append=output.append
+ self.__pop=output.pop
+
+ def handle_read(self,
+ join=string.join, StringType=type(''), _type=type,
+ _None=None):
+
try:
d=self.recv(8096)
except socket.error, err:
if err[0] in expected_socket_read_errors:
return
raise
- if not d:
- return
+ if not d: return
- input_len = self.__input_len + len(d)
- msg_size = self.__msg_size
- state = self.__state
-
- inp = self.__inp
- if msg_size > input_len:
- if inp is None:
- self.__inp = d
- elif type(self.__inp) is StringType:
- self.__inp = [self.__inp, d]
- else:
- self.__inp.append(d)
- self.__input_len = input_len
- return # keep waiting for more input
-
- # load all previous input and d into single string inp
- if isinstance(inp, StringType):
- inp = inp + d
- elif inp is None:
- inp = d
+ inp=self.__inp
+ if inp is _None:
+ inp=d
+ elif _type(inp) is StringType:
+ inp=[inp,d]
else:
inp.append(d)
- inp = "".join(inp)
- offset = 0
- while (offset + msg_size) <= input_len:
- msg = inp[offset:offset + msg_size]
- offset = offset + msg_size
- if state is None:
- # waiting for message
- msg_size = struct.unpack(">i", msg)[0]
- state = 1
+ inpl=self.__inpl+len(d)
+ l=self.__l
+
+ while 1:
+
+ if l <= inpl:
+ # Woo hoo, we have enough data
+ if _type(inp) is not StringType: inp=join(inp,'')
+ d=inp[:l]
+ inp=inp[l:]
+ inpl=inpl-l
+ if self.__state is _None:
+ # waiting for message
+ l=struct.unpack(">i",d)[0]
+ self.__state=1
+ else:
+ l=4
+ self.__state=_None
+ self.message_input(d)
else:
- msg_size = 4
- state = None
- self.message_input(msg)
-
- self.__state = state
- self.__msg_size = msg_size
- self.__inp = inp[offset:]
- self.__input_len = input_len - offset
+ break # not enough data
+
+ self.__l=l
+ self.__inp=inp
+ self.__inpl=inpl
- def readable(self):
- return 1
-
- def writable(self):
- if len(self.__output) == 0:
- return 0
- else:
- return 1
+ def readable(self): return 1
+ def writable(self): return not not self.__output
def handle_write(self):
- output = self.__output
+ output=self.__output
while output:
- v = output[0]
+ v=output[0]
try:
n=self.send(v)
except socket.error, err:
@@ -214,33 +191,42 @@
break # we couldn't write anything
raise
if n < len(v):
- output[0] = v[n:]
+ output[0]=v[n:]
break # we can't write any more
else:
del output[0]
+ #break # waaa
+
def handle_close(self):
self.close()
- def message_output(self, message):
- if __debug__:
- if self._debug:
- if len(message) > 40:
- m = message[:40]+' ...'
- else:
- m = message
- LOG(self._debug, TRACE, 'message_output %s' % `m`)
+ def message_output(self, message,
+ pack=struct.pack, len=len):
+ if self._debug:
+ if len(message) > 40: m=message[:40]+' ...'
+ else: m=message
+ LOG(self._debug, TRACE, 'message_output %s' % `m`)
+
+ append=self.__append
+ if append is None:
+ raise Disconnected("This action is temporarily unavailable.<p>")
+
+ append(pack(">i",len(message))+message)
+
+ def log_info(self, message, type='info'):
+ if type=='error': type=ERROR
+ else: type=INFO
+ LOG('ZEO', type, message)
- if self.__closed is not None:
- raise Disconnected, (
- "This action is temporarily unavailable."
- "<p>"
- )
- # do two separate appends to avoid copying the message string
- self.__output.append(struct.pack(">i", len(message)))
- self.__output.append(message)
+ log=log_info
def close(self):
- if self.__closed is None:
- self.__closed = 1
- self.__super_close()
+ if self.__append is not None:
+ self.__append=None
+ SizedMessageAsyncConnection.inheritedAttribute('close')(self)
+
+class Disconnected(Exception):
+ """The client has become disconnected from the server
+ """
+
=== StandaloneZODB/ZEO/start.py 1.27 => 1.28 ===
import sys, os, getopt, string
-import StorageServer
-import asyncore
-
def directory(p, n=1):
d=p
while n:
@@ -118,11 +115,9 @@
def main(argv):
me=argv[0]
+ sys.path[:]==filter(None, sys.path)
sys.path.insert(0, directory(me, 2))
- # XXX hack for profiling support
- global unix, storages, zeo_pid, asyncore
-
args=[]
last=''
for a in argv[1:]:
@@ -135,13 +130,25 @@
args.append(a)
last=a
- INSTANCE_HOME=os.environ.get('INSTANCE_HOME', directory(me, 4))
+ if os.environ.has_key('INSTANCE_HOME'):
+ INSTANCE_HOME=os.environ['INSTANCE_HOME']
+ elif os.path.isdir(os.path.join(directory(me, 4),'var')):
+ INSTANCE_HOME=directory(me, 4)
+ else:
+ INSTANCE_HOME=os.getcwd()
+
+ if os.path.isdir(os.path.join(INSTANCE_HOME, 'var')):
+ var=os.path.join(INSTANCE_HOME, 'var')
+ else:
+ var=INSTANCE_HOME
zeo_pid=os.environ.get('ZEO_SERVER_PID',
- os.path.join(INSTANCE_HOME, 'var', 'ZEO_SERVER.pid')
+ os.path.join(var, 'ZEO_SERVER.pid')
)
- fs=os.path.join(INSTANCE_HOME, 'var', 'Data.fs')
+ opts, args = getopt.getopt(args, 'p:Ddh:U:sS:u:')
+
+ fs=os.path.join(var, 'Data.fs')
usage="""%s [options] [filename]
@@ -149,14 +156,17 @@
-D -- Run in debug mode
+ -d -- Generate detailed debug logging without running
+ in the foreground.
+
-U -- Unix-domain socket file to listen on
-u username or uid number
The username to run the ZEO server as. You may want to run
the ZEO server as 'nobody' or some other user with limited
- resouces. The only works under Unix, and if ZServer is
- started by root.
+ resouces. The only works under Unix, and if the storage
+ server is started by root.
-p port -- port to listen on
@@ -179,42 +189,23 @@
attr_name -- This is the name to which the storage object
is assigned in the module.
- -P file -- Run under profile and dump output to file. Implies the
- -s flag.
-
if no file name is specified, then %s is used.
""" % (me, fs)
- try:
- opts, args = getopt.getopt(args, 'p:Dh:U:sS:u:P:')
- except getopt.error, msg:
- print usage
- print msg
- sys.exit(1)
-
port=None
- debug=0
+ debug=detailed=0
host=''
unix=None
Z=1
UID='nobody'
- prof = None
for o, v in opts:
if o=='-p': port=string.atoi(v)
elif o=='-h': host=v
elif o=='-U': unix=v
elif o=='-u': UID=v
elif o=='-D': debug=1
+ elif o=='-d': detailed=1
elif o=='-s': Z=0
- elif o=='-P': prof = v
-
- if prof:
- Z = 0
-
- try:
- from ZServer.medusa import asyncore
- sys.modules['asyncore']=asyncore
- except: pass
if port is None and unix is None:
print usage
@@ -228,9 +219,10 @@
sys.exit(1)
fs=args[0]
- __builtins__.__debug__=debug
if debug: os.environ['Z_DEBUG_MODE']='1'
+ if detailed: os.environ['STUPID_LOG_SEVERITY']='-99999'
+
from zLOG import LOG, INFO, ERROR
# Try to set uid to "-u" -provided uid.
@@ -271,54 +263,71 @@
import zdaemon
zdaemon.run(sys.argv, '')
- storages={}
- for o, v in opts:
- if o=='-S':
- n, m = string.split(v,'=')
- if string.find(m,':'):
- # we got an attribute name
- m, a = string.split(m,':')
- else:
- # attribute name must be same as storage name
- a=n
- storages[n]=get_storage(m,a)
-
- if not storages:
- import ZODB.FileStorage
- storages['1']=ZODB.FileStorage.FileStorage(fs)
-
- # Try to set up a signal handler
try:
- import signal
- signal.signal(signal.SIGTERM,
- lambda sig, frame, s=storages: shutdown(s)
- )
- signal.signal(signal.SIGINT,
- lambda sig, frame, s=storages: shutdown(s, 0)
- )
- signal.signal(signal.SIGHUP, rotate_logs_handler)
-
- finally: pass
-
- items=storages.items()
- items.sort()
- for kv in items:
- LOG('ZEO Server', INFO, 'Serving %s:\t%s' % kv)
-
- if not unix: unix=host, port
-
- if prof:
- cmds = \
- "StorageServer.StorageServer(unix, storages);" \
- 'open(zeo_pid,"w").write("%s %s" % (os.getppid(), os.getpid()));' \
- "asyncore.loop()"
- import profile
- profile.run(cmds, prof)
- else:
- StorageServer.StorageServer(unix, storages)
- open(zeo_pid,'w').write("%s %s" % (os.getppid(), os.getpid()))
- asyncore.loop()
+ import ZEO.StorageServer, asyncore
+
+ storages={}
+ for o, v in opts:
+ if o=='-S':
+ n, m = string.split(v,'=')
+ if string.find(m,':'):
+ # we got an attribute name
+ m, a = string.split(m,':')
+ else:
+ # attribute name must be same as storage name
+ a=n
+ storages[n]=get_storage(m,a)
+
+ if not storages:
+ import ZODB.FileStorage
+ storages['1']=ZODB.FileStorage.FileStorage(fs)
+
+ # Try to set up a signal handler
+ try:
+ import signal
+
+ signal.signal(signal.SIGTERM,
+ lambda sig, frame, s=storages: shutdown(s)
+ )
+ signal.signal(signal.SIGINT,
+ lambda sig, frame, s=storages: shutdown(s, 0)
+ )
+ try: signal.signal(signal.SIGHUP, rotate_logs_handler)
+ except: pass
+
+ except: pass
+
+ items=storages.items()
+ items.sort()
+ for kv in items:
+ LOG('ZEO Server', INFO, 'Serving %s:\t%s' % kv)
+
+ if not unix: unix=host, port
+
+ ZEO.StorageServer.StorageServer(unix, storages)
+
+ try: ppid, pid = os.getppid(), os.getpid()
+ except: pass # getpid not supported
+ else: open(zeo_pid,'w').write("%s %s" % (ppid, pid))
+
+ except:
+ # Log startup exception and tell zdaemon not to restart us.
+ info=sys.exc_info()
+ try:
+ import zLOG
+ zLOG.LOG("z2", zLOG.PANIC, "Startup exception",
+ error=info)
+ except:
+ pass
+
+ import traceback
+ apply(traceback.print_exception, info)
+
+ sys.exit(0)
+
+ asyncore.loop()
+
def rotate_logs():
import zLOG
@@ -326,7 +335,10 @@
zLOG.log_write.reinitialize()
else:
# Hm, lets at least try to take care of the stupid logger:
- zLOG._stupid_dest=None
+ if hasattr(zLOG, '_set_stupid_dest'):
+ zLOG._set_stupid_dest(None)
+ else:
+ zLOG._stupid_dest = None
def rotate_logs_handler(signum, frame):
rotate_logs()
@@ -347,7 +359,7 @@
for storage in storages.values():
try: storage.close()
- finally: pass
+ except: pass
try:
from zLOG import LOG, INFO
=== Removed File StandaloneZODB/ZEO/ClientStub.py ===
=== Removed File StandaloneZODB/ZEO/Exceptions.py ===
=== Removed File StandaloneZODB/ZEO/ServerStub.py ===
=== Removed File StandaloneZODB/ZEO/TransactionBuffer.py ===
=== Removed File StandaloneZODB/ZEO/zrpc2.py ===