Skip to content

Commit

Permalink
- Added a connection prefetch method that can be used to request
Browse files Browse the repository at this point in the history
  that a storage prefect data an application will need::

    conn.prefetch(obj, ...)

  Where arguments can be objects, object ids, or iterables of objects
  or object ids.

  Added optional prefetch methods to the storage APIs. If a
  storage doesn't support prefetch, then the connection prefetch
  method is a noop.
  • Loading branch information
Jim Fulton committed Jul 17, 2016
1 parent 61af558 commit 04028a0
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 0 deletions.
15 changes: 15 additions & 0 deletions CHANGES.rst
Expand Up @@ -2,6 +2,21 @@
Change History
================

5.0.0a6 (unreleased)
====================

- Added a connection ``prefetch`` method that can be used to request
that a storage prefect data an application will need::

conn.prefetch(obj, ...)

Where arguments can be objects, object ids, or iterables of objects
or object ids.

Added optional ``prefetch`` methods to the storage APIs. If a
storage doesn't support prefetch, then the connection prefetch
method is a noop.

5.0.0a5 (2016-07-06)
====================

Expand Down
21 changes: 21 additions & 0 deletions src/ZODB/Connection.py
Expand Up @@ -1080,6 +1080,27 @@ def _abort_savepoint(self):
# Savepoint support
#####################################################################

def prefetch(self, *args):
try:
self._storage.prefetch(self._prefetch_flatten(args))
except AttributeError:
if not hasattr(self._storage, 'prefetch'):
self.prefetch = lambda *a: None
else:
raise

def _prefetch_flatten(self, args):
for arg in args:
if isinstance(arg, bytes):
yield arg
elif hasattr(arg, '_p_oid'):
yield arg._p_oid
else:
for ob in arg:
if isinstance(ob, bytes):
yield ob
else:
yield ob._p_oid

@implementer(IDataManagerSavepoint)
class Savepoint:
Expand Down
18 changes: 18 additions & 0 deletions src/ZODB/interfaces.py
Expand Up @@ -567,6 +567,15 @@ def loadBefore(oid, tid):
If the object id isn't in the storage, then POSKeyError is raised.
"""

def prefetch(oids, tid):
"""Prefetch data for the given object ids before the given tid
The oids argument is an iterable that should be iterated no
more than once.
This method is optional. It is an optimization
"""

def loadSerial(oid, serial):
"""Load the object record for the give transaction id
Expand Down Expand Up @@ -1112,6 +1121,15 @@ def load(oid):
A POSKeyError is raised if there is no record for the object id.
"""

def prefetch(oids):
"""Prefetch data for the given object ids
The oids argument is an iterable that should be iterated no
more than once.
This method is optional. It is an optimization
"""


class IStorageCurrentRecordIteration(IStorage):

Expand Down
9 changes: 9 additions & 0 deletions src/ZODB/mvccadapter.py
Expand Up @@ -146,6 +146,15 @@ def load(self, oid):
raise POSException.ReadConflictError(repr(oid))
return r[:2]

def prefetch(self, oids):
try:
self._storage.prefetch(oids, self._start)
except AttributeError:
if not hasattr(self._storage, 'prefetch'):
self.prefetch = lambda *a: None
else:
raise

_modified = None # Used to keep track of oids modified within a
# transaction, so we can invalidate them later.

Expand Down
55 changes: 55 additions & 0 deletions src/ZODB/tests/test_prefetch.py
@@ -0,0 +1,55 @@
import unittest

from ZODB.utils import z64, u64
import ZODB

from .MVCCMappingStorage import MVCCMappingStorage

class PrefetchTests(unittest.TestCase):

def test_prefetch(self):
db = ZODB.DB(None)

fetched = []
def prefetch(oids, tid):
fetched.append((list(map(u64, oids)), tid))

db.storage.prefetch = prefetch

with db.transaction() as conn:
for i in range(10):
conn.root()[i] = conn.root().__class__()

conn = db.open()
conn.prefetch(z64)
conn.prefetch([z64])
conn.prefetch(conn.root())

conn.prefetch(z64, (conn.root()[i] for i in range(3)), conn.root()[3])

self.assertEqual(fetched,
[([0], conn._storage._start),
([0], conn._storage._start),
([0], conn._storage._start),
([0, 1, 2, 3, 4], conn._storage._start),
])

def test_prefetch_optional(self):
conn = ZODB.connection(None)
conn.prefetch(z64)
conn.prefetch([z64])
conn.prefetch(conn.root())
conn.prefetch(z64, [z64])
conn.prefetch(z64, [z64], conn.root())

def test_prefetch_optional_imvcc(self):
conn = ZODB.connection(MVCCMappingStorage())
conn.prefetch(z64)
conn.prefetch([z64])
conn.prefetch(conn.root())
conn.prefetch(z64, [z64])
conn.prefetch(z64, [z64], conn.root())


def test_suite():
return unittest.makeSuite(PrefetchTests)

0 comments on commit 04028a0

Please sign in to comment.