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.
- ZODB/ActivityMonitor.py +109 -0
- ZODB/BaseStorage.py +391 -0
- ZODB/ConflictResolution.py +323 -0
- ZODB/ConflictResolution.rst +579 -0
- ZODB/Connection.py +1366 -0
- ZODB/DB.py +1136 -0
- ZODB/DemoStorage.py +451 -0
- ZODB/DemoStorage.test +468 -0
- ZODB/ExportImport.py +214 -0
- ZODB/FileStorage/FileStorage.py +2237 -0
- ZODB/FileStorage/__init__.py +11 -0
- ZODB/FileStorage/format.py +297 -0
- ZODB/FileStorage/fsdump.py +136 -0
- ZODB/FileStorage/fsoids.py +213 -0
- ZODB/FileStorage/fspack.py +673 -0
- ZODB/FileStorage/interfaces.py +79 -0
- ZODB/FileStorage/iterator.test +185 -0
- ZODB/FileStorage/tests.py +329 -0
- ZODB/FileStorage/zconfig.txt +208 -0
- ZODB/MappingStorage.py +378 -0
- ZODB/POSException.py +369 -0
- ZODB/UndoLogCompatible.py +37 -0
- ZODB/__init__.py +30 -0
- ZODB/_compat.py +98 -0
- ZODB/blob.py +1020 -0
- ZODB/broken.py +366 -0
- ZODB/component.xml +342 -0
- ZODB/config.py +292 -0
- ZODB/config.xml +8 -0
- ZODB/conversionhack.py +42 -0
- ZODB/cross-database-references.rst +200 -0
- ZODB/event.py +18 -0
- ZODB/fsIndex.py +282 -0
- ZODB/fsrecover.py +401 -0
- ZODB/fstools.py +150 -0
- ZODB/historical_connections.rst +332 -0
- ZODB/interfaces.py +1467 -0
- ZODB/loglevels.py +48 -0
- ZODB/mvccadapter.py +313 -0
- ZODB/persistentclass.py +229 -0
- ZODB/persistentclass.rst +290 -0
- ZODB/scripts/README.txt +107 -0
- ZODB/scripts/__init__.py +1 -0
- ZODB/scripts/analyze.py +154 -0
- ZODB/scripts/checkbtrees.py +127 -0
- ZODB/scripts/fsoids.py +81 -0
- ZODB/scripts/fsrefs.py +179 -0
- ZODB/scripts/fsstats.py +212 -0
- ZODB/scripts/fstail.py +53 -0
- ZODB/scripts/fstest.py +237 -0
- ZODB/scripts/manual_tests/test-checker.fs +0 -0
- ZODB/scripts/manual_tests/testfstest.py +176 -0
- ZODB/scripts/migrate.py +359 -0
- ZODB/scripts/migrateblobs.py +81 -0
- ZODB/scripts/netspace.py +133 -0
- ZODB/scripts/referrers.py +28 -0
- ZODB/scripts/repozo.py +832 -0
- ZODB/scripts/space.py +64 -0
- ZODB/scripts/tests/__init__.py +0 -0
- ZODB/scripts/tests/fstail.txt +41 -0
- ZODB/scripts/tests/referrers.txt +43 -0
- ZODB/scripts/tests/test_doc.py +52 -0
- ZODB/scripts/tests/test_fsdump_fsstats.py +59 -0
- ZODB/scripts/tests/test_fstest.py +50 -0
- ZODB/scripts/tests/test_repozo.py +1289 -0
- ZODB/scripts/zodbload.py +851 -0
- ZODB/serialize.py +721 -0
- ZODB/storage.xml +4 -0
- ZODB/tests/BasicStorage.py +394 -0
- ZODB/tests/ConflictResolution.py +183 -0
- ZODB/tests/Corruption.py +75 -0
- ZODB/tests/HistoryStorage.py +58 -0
- ZODB/tests/IExternalGC.test +130 -0
- ZODB/tests/IteratorStorage.py +264 -0
- ZODB/tests/MTStorage.py +248 -0
- ZODB/tests/MVCCMappingStorage.py +127 -0
- ZODB/tests/MinPO.py +35 -0
- ZODB/tests/PackableStorage.py +806 -0
- ZODB/tests/PersistentStorage.py +52 -0
- ZODB/tests/ReadOnlyStorage.py +59 -0
- ZODB/tests/RecoveryStorage.py +241 -0
- ZODB/tests/RevisionStorage.py +179 -0
- ZODB/tests/StorageTestBase.py +194 -0
- ZODB/tests/Synchronization.py +120 -0
- ZODB/tests/TransactionalUndoStorage.py +772 -0
- ZODB/tests/__init__.py +1 -0
- ZODB/tests/blob_basic.txt +197 -0
- ZODB/tests/blob_connection.txt +88 -0
- ZODB/tests/blob_consume.txt +132 -0
- ZODB/tests/blob_importexport.txt +70 -0
- ZODB/tests/blob_layout.txt +302 -0
- ZODB/tests/blob_packing.txt +112 -0
- ZODB/tests/blob_tempdir.txt +53 -0
- ZODB/tests/blob_transaction.txt +418 -0
- ZODB/tests/blobstorage_packing.txt +163 -0
- ZODB/tests/component.xml +16 -0
- ZODB/tests/dangle.py +69 -0
- ZODB/tests/dbopen.txt +376 -0
- ZODB/tests/fix84.rst +19 -0
- ZODB/tests/hexstorage.py +173 -0
- ZODB/tests/loggingsupport.py +126 -0
- ZODB/tests/multidb.txt +196 -0
- ZODB/tests/racetest.py +637 -0
- ZODB/tests/sampledm.py +409 -0
- ZODB/tests/speed.py +147 -0
- ZODB/tests/synchronizers.txt +96 -0
- ZODB/tests/testActivityMonitor.py +104 -0
- ZODB/tests/testBroken.py +104 -0
- ZODB/tests/testCache.py +559 -0
- ZODB/tests/testConfig.py +209 -0
- ZODB/tests/testConnection.py +1438 -0
- ZODB/tests/testConnectionSavepoint.py +201 -0
- ZODB/tests/testConnectionSavepoint.txt +195 -0
- ZODB/tests/testDB.py +538 -0
- ZODB/tests/testDemoStorage.py +291 -0
- ZODB/tests/testFileStorage.py +816 -0
- ZODB/tests/testMVCCMappingStorage.py +194 -0
- ZODB/tests/testMappingStorage.py +88 -0
- ZODB/tests/testPersistentList.py +203 -0
- ZODB/tests/testPersistentMapping.py +134 -0
- ZODB/tests/testPersistentWeakref.py +276 -0
- ZODB/tests/testRecover.py +220 -0
- ZODB/tests/testSerialize.py +255 -0
- ZODB/tests/testThreadedShutdown.py +58 -0
- ZODB/tests/testUtils.py +154 -0
- ZODB/tests/testZODB.py +486 -0
- ZODB/tests/test_TransactionMetaData.py +121 -0
- ZODB/tests/test_cache.py +249 -0
- ZODB/tests/test_doctest_files.py +55 -0
- ZODB/tests/test_fsdump.py +95 -0
- ZODB/tests/test_mvccadapter.py +60 -0
- ZODB/tests/test_prefetch.py +58 -0
- ZODB/tests/test_racetest.py +110 -0
- ZODB/tests/test_storage.py +154 -0
- ZODB/tests/testblob.py +848 -0
- ZODB/tests/testconflictresolution.py +409 -0
- ZODB/tests/testcrossdatabasereferences.py +208 -0
- ZODB/tests/testdocumentation.py +57 -0
- ZODB/tests/testfsIndex.py +237 -0
- ZODB/tests/testfsoids.py +195 -0
- ZODB/tests/testhistoricalconnections.py +27 -0
- ZODB/tests/testmvcc.py +424 -0
- ZODB/tests/testpersistentclass.py +103 -0
- ZODB/tests/util.py +407 -0
- ZODB/transact.py +66 -0
- ZODB/utils.py +376 -0
- ZODB/utils.rst +198 -0
- ZODB/valuedoc.py +13 -0
- ZODB-6.0.dist-info/COPYING +5 -0
- ZODB-6.0.dist-info/LICENSE.txt +44 -0
- ZODB-6.0.dist-info/METADATA +709 -0
- ZODB-6.0.dist-info/RECORD +155 -0
- ZODB-6.0.dist-info/WHEEL +5 -0
- ZODB-6.0.dist-info/entry_points.txt +6 -0
- ZODB-6.0.dist-info/top_level.txt +1 -0
ZODB/ActivityMonitor.py
ADDED
|
@@ -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
|