ZODB 6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. ZODB/ActivityMonitor.py +109 -0
  2. ZODB/BaseStorage.py +391 -0
  3. ZODB/ConflictResolution.py +323 -0
  4. ZODB/ConflictResolution.rst +579 -0
  5. ZODB/Connection.py +1366 -0
  6. ZODB/DB.py +1136 -0
  7. ZODB/DemoStorage.py +451 -0
  8. ZODB/DemoStorage.test +468 -0
  9. ZODB/ExportImport.py +214 -0
  10. ZODB/FileStorage/FileStorage.py +2237 -0
  11. ZODB/FileStorage/__init__.py +11 -0
  12. ZODB/FileStorage/format.py +297 -0
  13. ZODB/FileStorage/fsdump.py +136 -0
  14. ZODB/FileStorage/fsoids.py +213 -0
  15. ZODB/FileStorage/fspack.py +673 -0
  16. ZODB/FileStorage/interfaces.py +79 -0
  17. ZODB/FileStorage/iterator.test +185 -0
  18. ZODB/FileStorage/tests.py +329 -0
  19. ZODB/FileStorage/zconfig.txt +208 -0
  20. ZODB/MappingStorage.py +378 -0
  21. ZODB/POSException.py +369 -0
  22. ZODB/UndoLogCompatible.py +37 -0
  23. ZODB/__init__.py +30 -0
  24. ZODB/_compat.py +98 -0
  25. ZODB/blob.py +1020 -0
  26. ZODB/broken.py +366 -0
  27. ZODB/component.xml +342 -0
  28. ZODB/config.py +292 -0
  29. ZODB/config.xml +8 -0
  30. ZODB/conversionhack.py +42 -0
  31. ZODB/cross-database-references.rst +200 -0
  32. ZODB/event.py +18 -0
  33. ZODB/fsIndex.py +282 -0
  34. ZODB/fsrecover.py +401 -0
  35. ZODB/fstools.py +150 -0
  36. ZODB/historical_connections.rst +332 -0
  37. ZODB/interfaces.py +1467 -0
  38. ZODB/loglevels.py +48 -0
  39. ZODB/mvccadapter.py +313 -0
  40. ZODB/persistentclass.py +229 -0
  41. ZODB/persistentclass.rst +290 -0
  42. ZODB/scripts/README.txt +107 -0
  43. ZODB/scripts/__init__.py +1 -0
  44. ZODB/scripts/analyze.py +154 -0
  45. ZODB/scripts/checkbtrees.py +127 -0
  46. ZODB/scripts/fsoids.py +81 -0
  47. ZODB/scripts/fsrefs.py +179 -0
  48. ZODB/scripts/fsstats.py +212 -0
  49. ZODB/scripts/fstail.py +53 -0
  50. ZODB/scripts/fstest.py +237 -0
  51. ZODB/scripts/manual_tests/test-checker.fs +0 -0
  52. ZODB/scripts/manual_tests/testfstest.py +176 -0
  53. ZODB/scripts/migrate.py +359 -0
  54. ZODB/scripts/migrateblobs.py +81 -0
  55. ZODB/scripts/netspace.py +133 -0
  56. ZODB/scripts/referrers.py +28 -0
  57. ZODB/scripts/repozo.py +832 -0
  58. ZODB/scripts/space.py +64 -0
  59. ZODB/scripts/tests/__init__.py +0 -0
  60. ZODB/scripts/tests/fstail.txt +41 -0
  61. ZODB/scripts/tests/referrers.txt +43 -0
  62. ZODB/scripts/tests/test_doc.py +52 -0
  63. ZODB/scripts/tests/test_fsdump_fsstats.py +59 -0
  64. ZODB/scripts/tests/test_fstest.py +50 -0
  65. ZODB/scripts/tests/test_repozo.py +1289 -0
  66. ZODB/scripts/zodbload.py +851 -0
  67. ZODB/serialize.py +721 -0
  68. ZODB/storage.xml +4 -0
  69. ZODB/tests/BasicStorage.py +394 -0
  70. ZODB/tests/ConflictResolution.py +183 -0
  71. ZODB/tests/Corruption.py +75 -0
  72. ZODB/tests/HistoryStorage.py +58 -0
  73. ZODB/tests/IExternalGC.test +130 -0
  74. ZODB/tests/IteratorStorage.py +264 -0
  75. ZODB/tests/MTStorage.py +248 -0
  76. ZODB/tests/MVCCMappingStorage.py +127 -0
  77. ZODB/tests/MinPO.py +35 -0
  78. ZODB/tests/PackableStorage.py +806 -0
  79. ZODB/tests/PersistentStorage.py +52 -0
  80. ZODB/tests/ReadOnlyStorage.py +59 -0
  81. ZODB/tests/RecoveryStorage.py +241 -0
  82. ZODB/tests/RevisionStorage.py +179 -0
  83. ZODB/tests/StorageTestBase.py +194 -0
  84. ZODB/tests/Synchronization.py +120 -0
  85. ZODB/tests/TransactionalUndoStorage.py +772 -0
  86. ZODB/tests/__init__.py +1 -0
  87. ZODB/tests/blob_basic.txt +197 -0
  88. ZODB/tests/blob_connection.txt +88 -0
  89. ZODB/tests/blob_consume.txt +132 -0
  90. ZODB/tests/blob_importexport.txt +70 -0
  91. ZODB/tests/blob_layout.txt +302 -0
  92. ZODB/tests/blob_packing.txt +112 -0
  93. ZODB/tests/blob_tempdir.txt +53 -0
  94. ZODB/tests/blob_transaction.txt +418 -0
  95. ZODB/tests/blobstorage_packing.txt +163 -0
  96. ZODB/tests/component.xml +16 -0
  97. ZODB/tests/dangle.py +69 -0
  98. ZODB/tests/dbopen.txt +376 -0
  99. ZODB/tests/fix84.rst +19 -0
  100. ZODB/tests/hexstorage.py +173 -0
  101. ZODB/tests/loggingsupport.py +126 -0
  102. ZODB/tests/multidb.txt +196 -0
  103. ZODB/tests/racetest.py +637 -0
  104. ZODB/tests/sampledm.py +409 -0
  105. ZODB/tests/speed.py +147 -0
  106. ZODB/tests/synchronizers.txt +96 -0
  107. ZODB/tests/testActivityMonitor.py +104 -0
  108. ZODB/tests/testBroken.py +104 -0
  109. ZODB/tests/testCache.py +559 -0
  110. ZODB/tests/testConfig.py +209 -0
  111. ZODB/tests/testConnection.py +1438 -0
  112. ZODB/tests/testConnectionSavepoint.py +201 -0
  113. ZODB/tests/testConnectionSavepoint.txt +195 -0
  114. ZODB/tests/testDB.py +538 -0
  115. ZODB/tests/testDemoStorage.py +291 -0
  116. ZODB/tests/testFileStorage.py +816 -0
  117. ZODB/tests/testMVCCMappingStorage.py +194 -0
  118. ZODB/tests/testMappingStorage.py +88 -0
  119. ZODB/tests/testPersistentList.py +203 -0
  120. ZODB/tests/testPersistentMapping.py +134 -0
  121. ZODB/tests/testPersistentWeakref.py +276 -0
  122. ZODB/tests/testRecover.py +220 -0
  123. ZODB/tests/testSerialize.py +255 -0
  124. ZODB/tests/testThreadedShutdown.py +58 -0
  125. ZODB/tests/testUtils.py +154 -0
  126. ZODB/tests/testZODB.py +486 -0
  127. ZODB/tests/test_TransactionMetaData.py +121 -0
  128. ZODB/tests/test_cache.py +249 -0
  129. ZODB/tests/test_doctest_files.py +55 -0
  130. ZODB/tests/test_fsdump.py +95 -0
  131. ZODB/tests/test_mvccadapter.py +60 -0
  132. ZODB/tests/test_prefetch.py +58 -0
  133. ZODB/tests/test_racetest.py +110 -0
  134. ZODB/tests/test_storage.py +154 -0
  135. ZODB/tests/testblob.py +848 -0
  136. ZODB/tests/testconflictresolution.py +409 -0
  137. ZODB/tests/testcrossdatabasereferences.py +208 -0
  138. ZODB/tests/testdocumentation.py +57 -0
  139. ZODB/tests/testfsIndex.py +237 -0
  140. ZODB/tests/testfsoids.py +195 -0
  141. ZODB/tests/testhistoricalconnections.py +27 -0
  142. ZODB/tests/testmvcc.py +424 -0
  143. ZODB/tests/testpersistentclass.py +103 -0
  144. ZODB/tests/util.py +407 -0
  145. ZODB/transact.py +66 -0
  146. ZODB/utils.py +376 -0
  147. ZODB/utils.rst +198 -0
  148. ZODB/valuedoc.py +13 -0
  149. ZODB-6.0.dist-info/COPYING +5 -0
  150. ZODB-6.0.dist-info/LICENSE.txt +44 -0
  151. ZODB-6.0.dist-info/METADATA +709 -0
  152. ZODB-6.0.dist-info/RECORD +155 -0
  153. ZODB-6.0.dist-info/WHEEL +5 -0
  154. ZODB-6.0.dist-info/entry_points.txt +6 -0
  155. ZODB-6.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,109 @@
1
+ ##############################################################################
2
+ #
3
+ # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4
+ # All Rights Reserved.
5
+ #
6
+ # This software is subject to the provisions of the Zope Public License,
7
+ # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8
+ # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
+ # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
+ # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
+ # FOR A PARTICULAR PURPOSE
12
+ #
13
+ ##############################################################################
14
+ """ZODB transfer activity monitoring
15
+ """
16
+
17
+ import time
18
+
19
+ from . import utils
20
+
21
+
22
+ class ActivityMonitor:
23
+ """ZODB load/store activity monitor
24
+
25
+ This simple implementation just keeps a small log in memory
26
+ and iterates over the log when getActivityAnalysis() is called.
27
+
28
+ It assumes that log entries are added in chronological sequence.
29
+ """
30
+
31
+ def __init__(self, history_length=3600):
32
+ self.history_length = history_length # Number of seconds
33
+ self.log = [] # [(time, loads, stores)]
34
+ self.trim_lock = utils.Lock()
35
+
36
+ def closedConnection(self, conn):
37
+ log = self.log
38
+ now = time.time()
39
+ loads, stores = conn.getTransferCounts(1)
40
+ log.append((now, loads, stores))
41
+ self.trim(now)
42
+
43
+ def trim(self, now):
44
+ with self.trim_lock:
45
+ log = self.log
46
+ cutoff = now - self.history_length
47
+ n = 0
48
+ loglen = len(log)
49
+ while n < loglen and log[n][0] < cutoff:
50
+ n = n + 1
51
+ if n:
52
+ del log[:n]
53
+
54
+ def setHistoryLength(self, history_length):
55
+ self.history_length = history_length
56
+ self.trim(time.time())
57
+
58
+ def getHistoryLength(self):
59
+ return self.history_length
60
+
61
+ def getActivityAnalysis(self, start=0, end=0, divisions=10):
62
+ res = []
63
+ now = time.time()
64
+ if start == 0:
65
+ start = now - self.history_length
66
+ if end == 0:
67
+ end = now
68
+ for n in range(divisions):
69
+ res.append({
70
+ 'start': start + (end - start) * n / divisions,
71
+ 'end': start + (end - start) * (n + 1) / divisions,
72
+ 'loads': 0,
73
+ 'stores': 0,
74
+ 'connections': 0,
75
+ })
76
+
77
+ div = res[0]
78
+ div_end = div['end']
79
+ div_index = 0
80
+ connections = 0
81
+ total_loads = 0
82
+ total_stores = 0
83
+ for t, loads, stores in self.log:
84
+ if t < start:
85
+ # We could use a binary search to find the start.
86
+ continue
87
+ elif t > end:
88
+ # We could use a binary search to find the end also.
89
+ break
90
+ while t > div_end:
91
+ div['loads'] = total_loads
92
+ div['stores'] = total_stores
93
+ div['connections'] = connections
94
+ total_loads = 0
95
+ total_stores = 0
96
+ connections = 0
97
+ div_index = div_index + 1
98
+ if div_index < divisions:
99
+ div = res[div_index]
100
+ div_end = div['end']
101
+ connections = connections + 1
102
+ total_loads = total_loads + loads
103
+ total_stores = total_stores + stores
104
+
105
+ div['stores'] = div['stores'] + total_stores
106
+ div['loads'] = div['loads'] + total_loads
107
+ div['connections'] = div['connections'] + connections
108
+
109
+ return res
ZODB/BaseStorage.py ADDED
@@ -0,0 +1,391 @@
1
+ ##############################################################################
2
+ #
3
+ # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4
+ # All Rights Reserved.
5
+ #
6
+ # This software is subject to the provisions of the Zope Public License,
7
+ # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8
+ # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
+ # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
+ # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
+ # FOR A PARTICULAR PURPOSE
12
+ #
13
+ ##############################################################################
14
+ """Storage base class that is mostly a mistake
15
+
16
+ The base class here is tightly coupled with its subclasses and
17
+ its use is not recommended. It's still here for historical reasons.
18
+ """
19
+
20
+ import logging
21
+ import time
22
+ from struct import pack as _structpack
23
+ from struct import unpack as _structunpack
24
+
25
+ import zope.interface
26
+ from persistent.TimeStamp import TimeStamp
27
+
28
+ import ZODB.interfaces
29
+
30
+ from . import POSException
31
+ from . import utils
32
+ from .Connection import TransactionMetaData
33
+ from .UndoLogCompatible import UndoLogCompatible
34
+ from .utils import byte_chr
35
+ from .utils import byte_ord
36
+ from .utils import load_current
37
+ from .utils import oid_repr
38
+ from .utils import z64
39
+
40
+
41
+ log = logging.getLogger("ZODB.BaseStorage")
42
+
43
+
44
+ class BaseStorage(UndoLogCompatible):
45
+ """Base class that supports storage implementations.
46
+
47
+ XXX Base classes like this are an attractive nuisance. They often
48
+ introduce more complexity than they save. While important logic
49
+ is implemented here, we should consider exposing it as utility
50
+ functions or as objects that can be used through composition.
51
+
52
+ A subclass must define the following methods:
53
+ load()
54
+ store()
55
+ close()
56
+ cleanup()
57
+ lastTransaction()
58
+
59
+ It must override these hooks:
60
+ _begin()
61
+ _vote()
62
+ _abort()
63
+ _finish()
64
+ _clear_temp()
65
+
66
+ If it stores multiple revisions, it should implement
67
+ loadSerial()
68
+ loadBefore()
69
+
70
+ Each storage will have two locks that are accessed via lock
71
+ acquire and release methods bound to the instance. (Yuck.)
72
+ _lock_acquire / _lock_release (reentrant)
73
+ _commit_lock_acquire / _commit_lock_release
74
+
75
+ The commit lock is acquired in tpc_begin() and released in
76
+ tpc_abort() and tpc_finish(). It is never acquired with the other
77
+ lock held.
78
+
79
+ The other lock appears to protect _oid and _transaction and
80
+ perhaps other things. It is always held when load() is called, so
81
+ presumably the load() implementation should also acquire the lock.
82
+ """
83
+ _transaction = None # Transaction that is being committed
84
+ _tstatus = ' ' # Transaction status, used for copying data
85
+ _is_read_only = False
86
+
87
+ def __init__(self, name, base=None):
88
+ self.__name__ = name
89
+ log.debug("create storage %s", self.__name__)
90
+
91
+ # Allocate locks:
92
+ self._lock = utils.RLock()
93
+ self._commit_lock = utils.Lock()
94
+
95
+ # Needed by external storages that use this dumb api :(
96
+ self._lock_acquire = self._lock.acquire
97
+ self._lock_release = self._lock.release
98
+ self._commit_lock_acquire = self._commit_lock.acquire
99
+ self._commit_lock_release = self._commit_lock.release
100
+
101
+ t = time.time()
102
+ t = self._ts = TimeStamp(*(time.gmtime(t)[:5] + (t % 60,)))
103
+ self._tid = t.raw()
104
+
105
+ # ._oid is the highest oid in use (0 is always in use -- it's
106
+ # a reserved oid for the root object). Our new_oid() method
107
+ # increments it by 1, and returns the result. It's really a
108
+ # 64-bit integer stored as an 8-byte big-endian string.
109
+ oid = getattr(base, '_oid', None)
110
+ if oid is None:
111
+ self._oid = z64
112
+ else:
113
+ self._oid = oid
114
+ # In case that conflicts are resolved during store,
115
+ # this collects oids to be returned by tpc_vote.
116
+ self._resolved = []
117
+
118
+ def sortKey(self):
119
+ """Return a string that can be used to sort storage instances.
120
+
121
+ The key must uniquely identify a storage and must be the same
122
+ across multiple instantiations of the same storage.
123
+ """
124
+ # name may not be sufficient, e.g. ZEO has a user-definable name.
125
+ return self.__name__
126
+
127
+ def getName(self):
128
+ return self.__name__
129
+
130
+ def getSize(self):
131
+ return len(self)*300 # WAG!
132
+
133
+ def history(self, oid, version, length=1, filter=None):
134
+ return ()
135
+
136
+ def new_oid(self):
137
+ if self._is_read_only:
138
+ raise POSException.ReadOnlyError()
139
+
140
+ with self._lock:
141
+ last = self._oid
142
+ d = byte_ord(last[-1])
143
+ if d < 255: # fast path for the usual case
144
+ last = last[:-1] + byte_chr(d+1)
145
+ else: # there's a carry out of the last byte
146
+ last_as_long, = _structunpack(">Q", last)
147
+ last = _structpack(">Q", last_as_long + 1)
148
+ self._oid = last
149
+ return last
150
+
151
+ # Update the maximum oid in use, under protection of a lock. The
152
+ # maximum-in-use attribute is changed only if possible_new_max_oid is
153
+ # larger than its current value.
154
+ def set_max_oid(self, possible_new_max_oid):
155
+ with self._lock:
156
+ if possible_new_max_oid > self._oid:
157
+ self._oid = possible_new_max_oid
158
+
159
+ def registerDB(self, db):
160
+ pass # we don't care
161
+
162
+ def isReadOnly(self):
163
+ return self._is_read_only
164
+
165
+ def tpc_abort(self, transaction):
166
+ with self._lock:
167
+
168
+ if transaction is not self._transaction:
169
+ return
170
+
171
+ try:
172
+ self._abort()
173
+ self._clear_temp()
174
+ self._transaction = None
175
+ finally:
176
+ self._commit_lock_release()
177
+
178
+ def _abort(self):
179
+ """Subclasses should redefine this to supply abort actions"""
180
+ pass
181
+
182
+ def tpc_begin(self, transaction, tid=None, status=' '):
183
+ if self._is_read_only:
184
+ raise POSException.ReadOnlyError()
185
+
186
+ with self._lock:
187
+ if self._transaction is transaction:
188
+ raise POSException.StorageTransactionError(
189
+ "Duplicate tpc_begin calls for same transaction")
190
+
191
+ self._commit_lock.acquire()
192
+
193
+ with self._lock:
194
+ self._transaction = transaction
195
+ self._clear_temp()
196
+
197
+ user = transaction.user
198
+ desc = transaction.description
199
+ ext = transaction.extension_bytes
200
+
201
+ self._ude = user, desc, ext
202
+
203
+ if tid is None:
204
+ now = time.time()
205
+ t = TimeStamp(*(time.gmtime(now)[:5] + (now % 60,)))
206
+ self._ts = t = t.laterThan(self._ts)
207
+ self._tid = t.raw()
208
+ else:
209
+ self._ts = TimeStamp(tid)
210
+ self._tid = tid
211
+
212
+ del self._resolved[:]
213
+ self._tstatus = status
214
+ self._begin(self._tid, user, desc, ext)
215
+
216
+ def tpc_transaction(self):
217
+ return self._transaction
218
+
219
+ def _begin(self, tid, u, d, e):
220
+ """Subclasses should redefine this to supply transaction start actions.
221
+ """
222
+ pass
223
+
224
+ def tpc_vote(self, transaction):
225
+ with self._lock:
226
+ if transaction is not self._transaction:
227
+ raise POSException.StorageTransactionError(
228
+ "tpc_vote called with wrong transaction")
229
+ return self._vote()
230
+
231
+ def _vote(self):
232
+ """Subclasses should redefine this to supply transaction vote actions.
233
+ """
234
+ return self._resolved
235
+
236
+ def tpc_finish(self, transaction, f=None):
237
+ # It's important that the storage calls the function we pass
238
+ # while it still has its lock. We don't want another thread
239
+ # to be able to read any updated data until we've had a chance
240
+ # to send an invalidation message to all of the other
241
+ # connections!
242
+
243
+ with self._lock:
244
+ if transaction is not self._transaction:
245
+ raise POSException.StorageTransactionError(
246
+ "tpc_finish called with wrong transaction")
247
+ try:
248
+ if f is not None:
249
+ f(self._tid)
250
+ u, d, e = self._ude
251
+ self._finish(self._tid, u, d, e)
252
+ self._clear_temp()
253
+ finally:
254
+ self._ude = None
255
+ self._transaction = None
256
+ self._commit_lock.release()
257
+ return self._tid
258
+
259
+ def _finish(self, tid, u, d, e):
260
+ """Subclasses should redefine this to supply transaction finish actions
261
+ """
262
+ pass
263
+
264
+ def lastTransaction(self):
265
+ with self._lock:
266
+ return self._ltid
267
+
268
+ def getTid(self, oid):
269
+ with self._lock:
270
+ return load_current(self, oid)[1]
271
+
272
+ def loadSerial(self, oid, serial):
273
+ raise POSException.Unsupported(
274
+ "Retrieval of historical revisions is not supported")
275
+
276
+ def loadBefore(self, oid, tid):
277
+ """Return most recent revision of oid before tid committed."""
278
+ return None
279
+
280
+ def copyTransactionsFrom(self, other, verbose=0):
281
+ """Copy transactions from another storage.
282
+
283
+ This is typically used for converting data from one storage to
284
+ another. `other` must have an .iterator() method.
285
+ """
286
+ copy(other, self, verbose)
287
+
288
+
289
+ def copy(source, dest, verbose=0):
290
+ """Copy transactions from a source to a destination storage
291
+
292
+ This is typically used for converting data from one storage to
293
+ another. `source` must have an .iterator() method.
294
+ """
295
+ _ts = None
296
+ ok = 1
297
+ preindex = {}
298
+ preget = preindex.get
299
+ # restore() is a new storage API method which has an identical
300
+ # signature to store() except that it does not return anything.
301
+ # Semantically, restore() is also identical to store() except that it
302
+ # doesn't do the ConflictError or VersionLockError consistency
303
+ # checks. The reason to use restore() over store() in this method is
304
+ # that store() cannot be used to copy transactions spanning a version
305
+ # commit or abort, or over transactional undos.
306
+ #
307
+ # We'll use restore() if it's available, otherwise we'll fall back to
308
+ # using store(). However, if we use store, then
309
+ # copyTransactionsFrom() may fail with VersionLockError or
310
+ # ConflictError.
311
+ try:
312
+ getattr(dest, 'restore')
313
+ except: # noqa: E722 do not use bare 'except'
314
+ restoring = False
315
+ else:
316
+ restoring = True
317
+ fiter = source.iterator()
318
+ for transaction in fiter:
319
+ tid = transaction.tid
320
+ if _ts is None:
321
+ _ts = TimeStamp(tid)
322
+ else:
323
+ t = TimeStamp(tid)
324
+ if t <= _ts:
325
+ if ok:
326
+ print('Time stamps out of order {}, {}'.format(_ts, t))
327
+ ok = 0
328
+ _ts = t.laterThan(_ts)
329
+ tid = _ts.raw()
330
+ else:
331
+ _ts = t
332
+ if not ok:
333
+ print('Time stamps back in order %s' % (t))
334
+ ok = 1
335
+
336
+ if verbose:
337
+ print(_ts)
338
+
339
+ dest.tpc_begin(transaction, tid, transaction.status)
340
+ for r in transaction:
341
+ oid = r.oid
342
+ if verbose:
343
+ print(oid_repr(oid), r.version, len(r.data))
344
+ if restoring:
345
+ dest.restore(oid, r.tid, r.data, r.version,
346
+ r.data_txn, transaction)
347
+ else:
348
+ pre = preget(oid, None)
349
+ dest.store(oid, pre, r.data, r.version, transaction)
350
+ preindex[oid] = tid
351
+
352
+ dest.tpc_vote(transaction)
353
+ dest.tpc_finish(transaction)
354
+
355
+
356
+ # defined outside of BaseStorage to facilitate independent reuse.
357
+ # just depends on _transaction attr and getTid method.
358
+ def checkCurrentSerialInTransaction(self, oid, serial, transaction):
359
+ if transaction is not self._transaction:
360
+ raise POSException.StorageTransactionError(self, transaction)
361
+
362
+ committed_tid = self.getTid(oid)
363
+ if committed_tid != serial:
364
+ raise POSException.ReadConflictError(
365
+ oid=oid, serials=(committed_tid, serial))
366
+
367
+
368
+ BaseStorage.checkCurrentSerialInTransaction = checkCurrentSerialInTransaction
369
+
370
+
371
+ @zope.interface.implementer(ZODB.interfaces.IStorageTransactionInformation)
372
+ class TransactionRecord(TransactionMetaData):
373
+ """Abstract base class for iterator protocol"""
374
+
375
+ def __init__(self, tid, status, user, description, extension):
376
+ self.tid = tid
377
+ self.status = status
378
+ TransactionMetaData.__init__(self, user, description, extension)
379
+
380
+
381
+ @zope.interface.implementer(ZODB.interfaces.IStorageRecordInformation)
382
+ class DataRecord:
383
+ """Abstract base class for iterator protocol"""
384
+
385
+ version = ''
386
+
387
+ def __init__(self, oid, tid, data, prev):
388
+ self.oid = oid
389
+ self.tid = tid
390
+ self.data = data
391
+ self.data_txn = prev