Skip to content

Commit a874ea7

Browse files
committed
Replace Session.last_bookmark with Session.last_bookmarks
Renamed the attribute and made it return a list of strings instead of an optional string. Furthermore, it will return *all* initial bookmarks (if not updated by a server message since session creation) instead of only the last bookmark, which not necessarily would be the latest bookmark.
1 parent 4a01b98 commit a874ea7

File tree

8 files changed

+70
-34
lines changed

8 files changed

+70
-34
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
was silently ignored.
5555
- `Result.single` now raises `ResultNotSingleError` if not exactly one result is
5656
available.
57+
- `Session.last_bookmark` was renamed to `Session.last_bookmarks`.
58+
It returns a list of strings (previously string or None).
5759

5860
## Version 4.4
5961

neo4j/_async/work/session.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ async def _connect(self, access_mode):
110110

111111
def _collect_bookmark(self, bookmark):
112112
if bookmark:
113-
self._bookmarks = [bookmark]
113+
self._bookmarks = bookmark,
114114

115115
async def _result_closed(self):
116116
if self._auto_result:
@@ -222,11 +222,28 @@ async def run(self, query, parameters=None, **kwargs):
222222

223223
return self._auto_result
224224

225-
async def last_bookmark(self):
226-
"""Return the bookmark received following the last completed transaction.
227-
Note: For auto-transaction (Session.run) this will trigger an consume for the current result.
225+
async def last_bookmarks(self):
226+
"""Return most recent bookmarks of the session.
228227
229-
:returns: :class:`neo4j.Bookmark` object
228+
Bookmarks can be used to causally chain sessions. For example,
229+
if a session (``session1``) wrote something, that another session
230+
(``session2``) needs to read, use
231+
``session2 = driver.session(bookmarks=session1.last_bookmarks())`` to
232+
achieve this.
233+
234+
A session automatically manages bookmarks, so this method is rarely
235+
needed. If you need causal consistency, try to run the relevant queries
236+
in the same session.
237+
238+
"Most recent bookmarks" are either the bookmarks passed to the session
239+
or creation, or the last bookmark the session received after committing
240+
a transaction to the server.
241+
242+
Note: For auto-transaction (Session.run) this will trigger a
243+
``consume`` for the current result.
244+
245+
:returns: list of bookmarks
246+
:rtype: list[str]
230247
"""
231248
# The set of bookmarks to be passed into the next transaction.
232249

@@ -237,9 +254,7 @@ async def last_bookmark(self):
237254
self._collect_bookmark(self._transaction._bookmark)
238255
self._transaction = None
239256

240-
if len(self._bookmarks):
241-
return self._bookmarks[len(self._bookmarks)-1]
242-
return None
257+
return list(self._bookmarks)
243258

244259
async def _transaction_closed_handler(self):
245260
if self._transaction:

neo4j/_sync/work/session.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def _connect(self, access_mode):
110110

111111
def _collect_bookmark(self, bookmark):
112112
if bookmark:
113-
self._bookmarks = [bookmark]
113+
self._bookmarks = bookmark,
114114

115115
def _result_closed(self):
116116
if self._auto_result:
@@ -222,11 +222,28 @@ def run(self, query, parameters=None, **kwargs):
222222

223223
return self._auto_result
224224

225-
def last_bookmark(self):
226-
"""Return the bookmark received following the last completed transaction.
227-
Note: For auto-transaction (Session.run) this will trigger an consume for the current result.
225+
def last_bookmarks(self):
226+
"""Return most recent bookmarks of the session.
228227
229-
:returns: :class:`neo4j.Bookmark` object
228+
Bookmarks can be used to causally chain sessions. For example,
229+
if a session (``session1``) wrote something, that another session
230+
(``session2``) needs to read, use
231+
``session2 = driver.session(bookmarks=session1.last_bookmarks())`` to
232+
achieve this.
233+
234+
A session automatically manages bookmarks, so this method is rarely
235+
needed. If you need causal consistency, try to run the relevant queries
236+
in the same session.
237+
238+
"Most recent bookmarks" are either the bookmarks passed to the session
239+
or creation, or the last bookmark the session received after committing
240+
a transaction to the server.
241+
242+
Note: For auto-transaction (Session.run) this will trigger a
243+
``consume`` for the current result.
244+
245+
:returns: list of bookmarks
246+
:rtype: list[str]
230247
"""
231248
# The set of bookmarks to be passed into the next transaction.
232249

@@ -237,9 +254,7 @@ def last_bookmark(self):
237254
self._collect_bookmark(self._transaction._bookmark)
238255
self._transaction = None
239256

240-
if len(self._bookmarks):
241-
return self._bookmarks[len(self._bookmarks)-1]
242-
return None
257+
return list(self._bookmarks)
243258

244259
def _transaction_closed_handler(self):
245260
if self._transaction:

testkitbackend/_async/requests.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,7 @@ async def RetryableNegative(backend, data):
323323
async def SessionLastBookmarks(backend, data):
324324
key = data["sessionId"]
325325
session = backend.sessions[key].session
326-
bookmark = await session.last_bookmark()
327-
bookmarks = []
328-
if bookmark:
329-
bookmarks.append(bookmark)
326+
bookmarks = await session.last_bookmarks()
330327
await backend.send_response("Bookmarks", {"bookmarks": bookmarks})
331328

332329

testkitbackend/_sync/requests.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,7 @@ def RetryableNegative(backend, data):
323323
def SessionLastBookmarks(backend, data):
324324
key = data["sessionId"]
325325
session = backend.sessions[key].session
326-
bookmark = session.last_bookmark()
327-
bookmarks = []
328-
if bookmark:
329-
bookmarks.append(bookmark)
326+
bookmarks = session.last_bookmarks()
330327
backend.send_response("Bookmarks", {"bookmarks": bookmarks})
331328

332329

tests/integration/examples/test_pass_bookmarks_example.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,13 @@ def main(self):
7676
with self.driver.session() as session_a:
7777
session_a.write_transaction(self.create_person, "Alice")
7878
session_a.write_transaction(self.employ, "Alice", "Wayne Enterprises")
79-
saved_bookmarks.append(session_a.last_bookmark())
79+
saved_bookmarks.extend(session_a.last_bookmarks())
8080

8181
# Create the second person and employment relationship.
8282
with self.driver.session() as session_b:
8383
session_b.write_transaction(self.create_person, "Bob")
8484
session_b.write_transaction(self.employ, "Bob", "LexCorp")
85-
saved_bookmarks.append(session_b.last_bookmark())
85+
saved_bookmarks.extend(session_b.last_bookmarks())
8686

8787
# Create a friendship between the two people created above.
8888
with self.driver.session(bookmarks=saved_bookmarks) as session_c:

tests/unit/async_/work/test_session.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,21 @@ async def test_closes_connection_after_tx_commit(pool, test_run_args):
169169
assert session._connection is None
170170

171171

172-
@pytest.mark.parametrize("bookmarks", (None, [], ["abc"], ["foo", "bar"]))
172+
@pytest.mark.parametrize(
173+
"bookmarks",
174+
(None, [], ["abc"], ["foo", "bar"], {"a", "b"}, ("1", "two"))
175+
)
173176
@mark_async_test
174177
async def test_session_returns_bookmark_directly(pool, bookmarks):
175178
async with AsyncSession(
176179
pool, SessionConfig(bookmarks=bookmarks)
177180
) as session:
178-
if bookmarks:
179-
assert await session.last_bookmark() == bookmarks[-1]
181+
if bookmarks is None:
182+
assert await session.last_bookmarks() == []
183+
elif isinstance(bookmarks, set):
184+
assert sorted(await session.last_bookmarks()) == sorted(bookmarks)
180185
else:
181-
assert await session.last_bookmark() is None
186+
assert await session.last_bookmarks() == list(bookmarks)
182187

183188

184189
@pytest.mark.parametrize(("query", "error_type"), (

tests/unit/sync/work/test_session.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,21 @@ def test_closes_connection_after_tx_commit(pool, test_run_args):
169169
assert session._connection is None
170170

171171

172-
@pytest.mark.parametrize("bookmarks", (None, [], ["abc"], ["foo", "bar"]))
172+
@pytest.mark.parametrize(
173+
"bookmarks",
174+
(None, [], ["abc"], ["foo", "bar"], {"a", "b"}, ("1", "two"))
175+
)
173176
@mark_sync_test
174177
def test_session_returns_bookmark_directly(pool, bookmarks):
175178
with Session(
176179
pool, SessionConfig(bookmarks=bookmarks)
177180
) as session:
178-
if bookmarks:
179-
assert session.last_bookmark() == bookmarks[-1]
181+
if bookmarks is None:
182+
assert session.last_bookmarks() == []
183+
elif isinstance(bookmarks, set):
184+
assert sorted(session.last_bookmarks()) == sorted(bookmarks)
180185
else:
181-
assert session.last_bookmark() is None
186+
assert session.last_bookmarks() == list(bookmarks)
182187

183188

184189
@pytest.mark.parametrize(("query", "error_type"), (

0 commit comments

Comments
 (0)