Skip to content

Commit f8e3f88

Browse files
authored
Bookmark manager no longer tracks per database (#859)
* The experimental bookmark manager feature was changed to no longer track bookmarks per database. This effectively changes the signature of almost all bookmark manager related methods: - `neo4j.BookmarkManger` and `neo4j.AsyncBookmarkManger` abstract base classes: - ``update_bookmarks`` has no longer a ``database`` argument. - ``get_bookmarks`` has no longer a ``database`` argument. - The ``get_all_bookmarks`` method was removed. - The ``forget`` method was removed. - `neo4j.GraphDatabase.bookmark_manager` and `neo4j.AsyncGraphDatabase.bookmark_manager` factory methods: - ``initial_bookmarks`` is no longer a mapping from database name to bookmarks but plain bookmarks. - ``bookmarks_supplier`` no longer receives the database name as an argument. - ``bookmarks_consumer`` no longer receives the database name as an argument. * Fix logging the wrong variable
1 parent 2486240 commit f8e3f88

File tree

20 files changed

+395
-709
lines changed

20 files changed

+395
-709
lines changed

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
11
# Neo4j Driver Change Log (breaking/major changes only)
22

3+
See also https://github.com/neo4j/neo4j-python-driver/wiki for more details.
4+
35
## Version 5.3
46
- Python 3.11 support added
57
- Removed undocumented, unused `neo4j.data.map_type`
68
- Query strings are now typed `LiteralString` instead of `str` to help mitigate
79
accidental Cypher injections. There are rare use-cases where a computed
810
string is necessary. Please use `# type: ignore`, or `typing.cast` to
911
suppress the type checking in those cases.
12+
- The experimental bookmark manager feature was changed to no longer track
13+
bookmarks per database.
14+
This effectively changes the signature of almost all bookmark
15+
manager related methods:
16+
- `neo4j.BookmarkManger` and `neo4j.AsyncBookmarkManger` abstract base
17+
classes:
18+
- ``update_bookmarks`` has no longer a ``database`` argument.
19+
- ``get_bookmarks`` has no longer a ``database`` argument.
20+
- The ``get_all_bookmarks`` method was removed.
21+
- The ``forget`` method was removed.
22+
- `neo4j.GraphDatabase.bookmark_manager` and
23+
`neo4j.AsyncGraphDatabase.bookmark_manager` factory methods:
24+
- ``initial_bookmarks`` is no longer a mapping from database name
25+
to bookmarks but plain bookmarks.
26+
- ``bookmarks_supplier`` no longer receives the database name as
27+
an argument.
28+
- ``bookmarks_consumer`` no longer receives the database name as
29+
an argument.
1030

1131

1232
## Version 5.2

src/neo4j/_async/bookmark_manager.py

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from __future__ import annotations
2020

2121
import typing as t
22-
from collections import defaultdict
2322

2423
from .._async_compat.concurrency import AsyncCooperativeLock
2524
from .._async_compat.util import AsyncUtil
@@ -29,9 +28,8 @@
2928
)
3029

3130

32-
TBmSupplier = t.Callable[[t.Optional[str]],
33-
t.Union[Bookmarks, t.Awaitable[Bookmarks]]]
34-
TBmConsumer = t.Callable[[str, Bookmarks], t.Union[None, t.Awaitable[None]]]
31+
TBmSupplier = t.Callable[[], t.Union[Bookmarks, t.Awaitable[Bookmarks]]]
32+
TBmConsumer = t.Callable[[Bookmarks], t.Union[None, t.Awaitable[None]]]
3533

3634

3735
def _bookmarks_to_set(
@@ -45,62 +43,41 @@ def _bookmarks_to_set(
4543
class AsyncNeo4jBookmarkManager(AsyncBookmarkManager):
4644
def __init__(
4745
self,
48-
initial_bookmarks: t.Optional[t.Mapping[str, t.Union[Bookmarks,
49-
t.Iterable[str]]]] = None,
46+
initial_bookmarks: t.Union[None, Bookmarks, t.Iterable[str]] = None,
5047
bookmarks_supplier: t.Optional[TBmSupplier] = None,
5148
bookmarks_consumer: t.Optional[TBmConsumer] = None
5249
) -> None:
5350
super().__init__()
5451
self._bookmarks_supplier = bookmarks_supplier
5552
self._bookmarks_consumer = bookmarks_consumer
56-
if initial_bookmarks is None:
57-
initial_bookmarks = {}
58-
self._bookmarks = defaultdict(
59-
set, ((k, _bookmarks_to_set(v))
60-
for k, v in initial_bookmarks.items())
61-
)
53+
if not initial_bookmarks:
54+
self._bookmarks = set()
55+
else:
56+
self._bookmarks = set(getattr(
57+
initial_bookmarks, "raw_values",
58+
t.cast(t.Iterable[str], initial_bookmarks)
59+
))
6260
self._lock = AsyncCooperativeLock()
6361

6462
async def update_bookmarks(
65-
self, database: str, previous_bookmarks: t.Collection[str],
63+
self, previous_bookmarks: t.Collection[str],
6664
new_bookmarks: t.Collection[str]
6765
) -> None:
6866
if not new_bookmarks:
6967
return
7068
with self._lock:
71-
curr_bms = self._bookmarks[database]
72-
curr_bms.difference_update(previous_bookmarks)
73-
curr_bms.update(new_bookmarks)
69+
self._bookmarks.difference_update(previous_bookmarks)
70+
self._bookmarks.update(new_bookmarks)
7471
if self._bookmarks_consumer:
75-
curr_bms_snapshot = Bookmarks.from_raw_values(curr_bms)
72+
curr_bms_snapshot = Bookmarks.from_raw_values(self._bookmarks)
7673
if self._bookmarks_consumer:
77-
await AsyncUtil.callback(
78-
self._bookmarks_consumer, database, curr_bms_snapshot
79-
)
74+
await AsyncUtil.callback(self._bookmarks_consumer,
75+
curr_bms_snapshot)
8076

81-
async def get_bookmarks(self, database: str) -> t.Set[str]:
77+
async def get_bookmarks(self) -> t.Set[str]:
8278
with self._lock:
83-
bms = set(self._bookmarks[database])
79+
bms = set(self._bookmarks)
8480
if self._bookmarks_supplier:
85-
extra_bms = await AsyncUtil.callback(
86-
self._bookmarks_supplier, database
87-
)
81+
extra_bms = await AsyncUtil.callback(self._bookmarks_supplier)
8882
bms.update(extra_bms.raw_values)
8983
return bms
90-
91-
async def get_all_bookmarks(self) -> t.Set[str]:
92-
bms: t.Set[str] = set()
93-
with self._lock:
94-
for database in self._bookmarks.keys():
95-
bms.update(self._bookmarks[database])
96-
if self._bookmarks_supplier:
97-
extra_bms = await AsyncUtil.callback(
98-
self._bookmarks_supplier, None
99-
)
100-
bms.update(extra_bms.raw_values)
101-
return bms
102-
103-
async def forget(self, databases: t.Iterable[str]) -> None:
104-
with self._lock:
105-
for database in databases:
106-
self._bookmarks.pop(database, None)

src/neo4j/_async/driver.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,7 @@ def driver(cls, uri, *, auth=None, **config) -> AsyncDriver:
220220
)
221221
def bookmark_manager(
222222
cls,
223-
initial_bookmarks: t.Optional[t.Mapping[str, t.Union[Bookmarks,
224-
t.Iterable[str]]]] = None,
223+
initial_bookmarks: t.Union[None, Bookmarks, t.Iterable[str]] = None,
225224
bookmarks_supplier: t.Optional[_TBmSupplier] = None,
226225
bookmarks_consumer: t.Optional[_TBmConsumer] = None
227226
) -> AsyncBookmarkManager:
@@ -261,31 +260,40 @@ def bookmark_manager(
261260
262261
:param initial_bookmarks:
263262
The initial set of bookmarks. The returned bookmark manager will
264-
use this to initialize its internal bookmarks per database.
265-
If present, this parameter must be a mapping of database names
266-
to :class:`.Bookmarks` or an iterable of raw bookmark values (str).
263+
use this to initialize its internal bookmarks.
267264
:param bookmarks_supplier:
268265
Function which will be called every time the default bookmark
269266
manager's method :meth:`.AsyncBookmarkManager.get_bookmarks`
270-
or :meth:`.AsyncBookmarkManager.get_all_bookmarks` gets called.
271-
The function will be passed the name of the database (``str``) if
272-
``.get_bookmarks`` is called or ``None`` if ``.get_all_bookmarks``
273-
is called. The function must return a :class:`.Bookmarks` object.
274-
The result of ``bookmarks_supplier`` will then be concatenated with
275-
the internal set of bookmarks and used to configure the session in
276-
creation.
267+
gets called.
268+
The function takes no arguments and must return a
269+
:class:`.Bookmarks` object. The result of ``bookmarks_supplier``
270+
will then be concatenated with the internal set of bookmarks and
271+
used to configure the session in creation. It will, however, not
272+
update the internal set of bookmarks.
277273
:param bookmarks_consumer:
278274
Function which will be called whenever the set of bookmarks
279275
handled by the bookmark manager gets updated with the new
280-
internal bookmark set. It will receive the name of the database
281-
and the new set of bookmarks.
276+
internal bookmark set. It will receive the new set of bookmarks
277+
as a :class:`.Bookmarks` object and return :const:`None`.
282278
283279
:returns: A default implementation of :class:`AsyncBookmarkManager`.
284280
285281
**This is experimental.** (See :ref:`filter-warnings-ref`)
286282
It might be changed or removed any time even without prior notice.
287283
288284
.. versionadded:: 5.0
285+
286+
.. versionchanged:: 5.3
287+
The bookmark manager no longer tracks bookmarks per database.
288+
This effectively changes the signature of almost all bookmark
289+
manager related methods:
290+
291+
* ``initial_bookmarks`` is no longer a mapping from database name
292+
to bookmarks but plain bookmarks.
293+
* ``bookmarks_supplier`` no longer receives the database name as
294+
an argument.
295+
* ``bookmarks_consumer`` no longer receives the database name as
296+
an argument.
289297
"""
290298
return AsyncNeo4jBookmarkManager(
291299
initial_bookmarks=initial_bookmarks,

src/neo4j/_async/io/_pool.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,11 +704,11 @@ async def ensure_routing_table_is_fresh(
704704
# Remove unused databases in the routing table
705705
# Remove the routing table after a timeout = TTL + 30s
706706
log.debug("[#0000] _: <POOL> routing aged?, database=%s",
707-
database)
707+
database_)
708708
routing_table = self.routing_tables[database_]
709709
if routing_table.should_be_purged_from_memory():
710710
log.debug("[#0000] _: <POOL> dropping routing table for "
711-
"database=%s", database)
711+
"database=%s", database_)
712712
del self.routing_tables[database_]
713713

714714
routing_table = await self.get_or_create_routing_table(database)

src/neo4j/_async/work/session.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,7 @@ def _handle_cancellation(self, message="General"):
144144

145145
async def _result_closed(self):
146146
if self._auto_result:
147-
await self._update_bookmark(self._auto_result._database,
148-
self._auto_result._bookmark)
147+
await self._update_bookmark(self._auto_result._bookmark)
149148
self._auto_result = None
150149
await self._disconnect()
151150

@@ -177,7 +176,6 @@ async def close(self) -> None:
177176
try:
178177
await self._auto_result.consume()
179178
await self._update_bookmark(
180-
self._auto_result._database,
181179
self._auto_result._bookmark
182180
)
183181
except Exception as error:
@@ -286,7 +284,7 @@ async def run(
286284
cx, self._config.fetch_size, self._result_closed,
287285
self._result_error
288286
)
289-
bookmarks = await self._get_all_bookmarks()
287+
bookmarks = await self._get_bookmarks()
290288
parameters = dict(parameters or {}, **kwargs)
291289
await self._auto_result._run(
292290
query, parameters, self._config.database,
@@ -322,8 +320,7 @@ async def last_bookmark(self) -> t.Optional[str]:
322320
await self._auto_result.consume()
323321

324322
if self._transaction and self._transaction._closed():
325-
await self._update_bookmark(self._transaction._database,
326-
self._transaction._bookmark)
323+
await self._update_bookmark(self._transaction._bookmark)
327324
self._transaction = None
328325

329326
if self._bookmarks:
@@ -364,16 +361,14 @@ async def last_bookmarks(self) -> Bookmarks:
364361
await self._auto_result.consume()
365362

366363
if self._transaction and self._transaction._closed():
367-
await self._update_bookmark(self._transaction._database,
368-
self._transaction._bookmark)
364+
await self._update_bookmark(self._transaction._bookmark)
369365
self._transaction = None
370366

371367
return Bookmarks.from_raw_values(self._bookmarks)
372368

373369
async def _transaction_closed_handler(self):
374370
if self._transaction:
375-
await self._update_bookmark(self._transaction._database,
376-
self._transaction._bookmark)
371+
await self._update_bookmark(self._transaction._bookmark)
377372
self._transaction = None
378373
await self._disconnect()
379374

@@ -397,7 +392,7 @@ async def _open_transaction(
397392
self._transaction_error_handler,
398393
self._transaction_cancel_handler
399394
)
400-
bookmarks = await self._get_all_bookmarks()
395+
bookmarks = await self._get_bookmarks()
401396
await self._transaction._begin(
402397
self._config.database, self._config.impersonated_user,
403398
bookmarks, access_mode, metadata, timeout

src/neo4j/_async/work/workspace.py

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -102,63 +102,33 @@ def _initialize_bookmarks(self, bookmarks):
102102
"iterable of raw bookmarks (deprecated).")
103103
self._initial_bookmarks = self._bookmarks = prepared_bookmarks
104104

105-
async def _get_bookmarks(self, database):
105+
async def _get_bookmarks(self,):
106106
if self._bookmark_manager is None:
107107
return self._bookmarks
108108

109-
# For 4.3- support: the server will not send the resolved home
110-
# database back. To avoid confusion between `None` as in "all
111-
# database" and `None` as in "home database" we re-write the
112-
# home database to `""`, which otherwise is an invalid database
113-
# name. It will not work properly either way, as the home database
114-
# can change (server config change or client side user change).
115-
if database is None:
116-
database = ""
117109
self._last_from_bookmark_manager = tuple({
118-
*await AsyncUtil.callback(
119-
self._bookmark_manager.get_bookmarks, database
120-
),
110+
*await AsyncUtil.callback(self._bookmark_manager.get_bookmarks),
121111
*self._initial_bookmarks
122112
})
123113
return self._last_from_bookmark_manager
124114

125-
async def _get_all_bookmarks(self):
126-
if self._bookmark_manager is None:
127-
return self._bookmarks
128-
129-
self._last_from_bookmark_manager = tuple({
130-
*await AsyncUtil.callback(
131-
self._bookmark_manager.get_all_bookmarks,
132-
),
133-
*self._initial_bookmarks
134-
})
135-
return self._last_from_bookmark_manager
136-
137-
async def _update_bookmarks(self, database, new_bookmarks):
115+
async def _update_bookmarks(self, new_bookmarks):
138116
if not new_bookmarks:
139117
return
140118
self._initial_bookmarks = ()
141119
self._bookmarks = new_bookmarks
142120
if self._bookmark_manager is None:
143121
return
144122
previous_bookmarks = self._last_from_bookmark_manager
145-
# For 4.3- support: the server will not send the resolved home
146-
# database back. To avoid confusion between `None` as in "all
147-
# database" and `None` as in "home database" we re-write the home
148-
# database to `""`, which otherwise is an invalid database name.
149-
if database is None:
150-
database = ""
151123
await AsyncUtil.callback(
152124
self._bookmark_manager.update_bookmarks,
153-
database, previous_bookmarks, new_bookmarks
125+
previous_bookmarks, new_bookmarks
154126
)
155127

156-
async def _update_bookmark(self, database, bookmark):
128+
async def _update_bookmark(self, bookmark):
157129
if not bookmark:
158130
return
159-
if not database:
160-
database = self._config.database
161-
await self._update_bookmarks(database, (bookmark,))
131+
await self._update_bookmarks((bookmark,))
162132

163133
async def _connect(self, access_mode, **acquire_kwargs):
164134
acquisition_timeout = self._config.connection_acquisition_timeout
@@ -183,15 +153,15 @@ async def _connect(self, access_mode, **acquire_kwargs):
183153
await self._pool.update_routing_table(
184154
database=self._config.database,
185155
imp_user=self._config.impersonated_user,
186-
bookmarks=await self._get_bookmarks("system"),
156+
bookmarks=await self._get_bookmarks(),
187157
acquisition_timeout=acquisition_timeout,
188158
database_callback=self._set_cached_database
189159
)
190160
acquire_kwargs_ = {
191161
"access_mode": access_mode,
192162
"timeout": acquisition_timeout,
193163
"database": self._config.database,
194-
"bookmarks": await self._get_bookmarks("system"),
164+
"bookmarks": await self._get_bookmarks(),
195165
"liveness_check_timeout": None,
196166
}
197167
acquire_kwargs_.update(acquire_kwargs)

0 commit comments

Comments
 (0)