Skip to content

Commit b5a6b97

Browse files
committed
Provide loadBeforeEx
loadBeforeEx is like loadBefore, but simpler, provides better information for object delete records and can be more efficiently implemented by many storages: zopefoundation/ZODB#323 On RelStorage loadBefore is currently implemented via 3 SQL queries: 1) check whether object record exists at all 2) retrieve object state 3) retrieve serial of next object revision Compared to that loadBeforeEx is implemented via only one SQL query "2" from the above - "retrieve object state". It is exactly the same query that loadBefore uses and after the patch loadBefore actually invokes loadBeforeEx for step 2. This change was outlined in zopefoundation/ZODB#318 (comment) and zopefoundation/ZODB#318 (comment) and as explained in the first link this patch is also semantically coupled with zodb#484 This patch passes tests with both ZODB5 and with ZODB5+zopefoundation/ZODB#323: - when ran with ZODB5 it verifies that loadBefore implementation does not become broken. - when ran with ZODB5+zopefoundation/ZODB#323 it verifies that loadBeforeEx implementation is correct. For tests to pass with ZODB5+zopefoundation/ZODB#323 we also need zopefoundation/zc.zlibstorage#11 because without that fix zc.zlibstorage does not decompress data on loadBeforeEx.
1 parent f3825ba commit b5a6b97

File tree

2 files changed

+29
-4
lines changed

2 files changed

+29
-4
lines changed

src/relstorage/storage/load.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,30 @@ def loadSerial(self, oid, serial):
200200

201201
raise self.__pke(oid, tid_int=tid_int, state=state)
202202

203+
@stale_aware
204+
@storage_method
205+
@metricmethod_sampled
206+
def loadBeforeEx(self, oid, tid): # -> (state, serial)
207+
"""
208+
Return most recent revision of oid before tid committed.
209+
(see IStorageLoadBeforeEx)
210+
"""
211+
oid_int = bytes8_to_int64(oid)
212+
cursor = self.load_connection.cursor
213+
state, serial_tid = self._loadBeforeEx(cursor, oid_int, tid)
214+
serial = int64_to_8bytes(serial_tid)
215+
return state, serial
216+
217+
def _loadBeforeEx(self, cursor, oid_int, tid): # -> (state, serial_tid)
218+
# serves loadBeforeEx and loadBefore
219+
state, start_tid = self.adapter.mover.load_before(
220+
cursor, oid_int, bytes8_to_int64(tid))
221+
222+
if start_tid is None:
223+
assert state is None
224+
start_tid = 0
225+
226+
return state, start_tid
203227

204228
@stale_aware
205229
@storage_method
@@ -225,6 +249,8 @@ def loadBefore(self, oid, tid):
225249

226250
# TODO: This makes three separate queries, and also bypasses the cache.
227251
# We should be able to fix at least the multiple queries.
252+
# ( this is "fixed" on recent ZODB which calls loadBeforeEx instead of
253+
# loadBefore after https://github.com/zopefoundation/ZODB/pull/323 )
228254

229255
# In the past, we would use the store connection (only if it was already open)
230256
# to "allow leading dato from later transactions for conflict resolution".
@@ -242,10 +268,8 @@ def loadBefore(self, oid, tid):
242268
if not self.adapter.mover.exists(cursor, oid_int):
243269
raise self.__pke(oid, exists=False)
244270

245-
state, start_tid = self.adapter.mover.load_before(
246-
cursor, oid_int, bytes8_to_int64(tid))
247-
248-
if start_tid is None:
271+
state, start_tid = self._loadBeforeEx(cursor, oid_int, tid)
272+
if start_tid == 0:
249273
return None
250274

251275
if state is None:

src/relstorage/tests/reltestbase.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ def func(*args, **kwargs):
164164
return func
165165

166166
for name in (
167+
'loadBeforeEx',
167168
'loadBefore',
168169
'load',
169170
'store',

0 commit comments

Comments
 (0)