Skip to content

Home db cache: add tests for when cache is re-enabled #655

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions tests/stub/homedb/scripts/mixed/reader_5x8_no_ssr.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
!: BOLT 5.8

C: HELLO {"{}": "*"}
S: SUCCESS {"connection_id": "bolt-1", "server": "Neo4j/5.26.0"}
A: LOGON {"{}": "*"}
*: RESET

{+
C: RUN {"U": "*"} {} {"db": "homedb1", "[bookmarks]": {"[]": "*"}, "mode": "r"}
S: SUCCESS {"fields": ["n"]}
{{
C: PULL {"n": {"Z": "*"}, "[qid]": -1}
S: RECORD [1]
----
C: DISCARD {"n": {"Z": "*"}, "[qid]": -1}
}}
S: SUCCESS {}
*: RESET
+}

?: GOODBYE
9 changes: 7 additions & 2 deletions tests/stub/homedb/scripts/mixed/reader_5x8_ssr.script
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ A: LOGON {"{}": "*"}
*: RESET

{+
C: RUN {"U": "*"} {} {"db": "homedb1", "[bookmarks]": {"[]": "*"}, "mode": "r"}
S: SUCCESS {"fields": ["n"]}
{{
C: RUN "RESOLVED" {} {"db": "homedb1", "[bookmarks]": {"[]": "*"}, "mode": "r"}
S: SUCCESS {"fields": ["n"]}
----
C: RUN "UNRESOLVED" {} {"[db]": null, "[bookmarks]": {"[]": "*"}, "mode": "r"}
S: SUCCESS {"fields": ["n"], "db": "homedb1"}
}}
{{
C: PULL {"n": {"Z": "*"}, "[qid]": -1}
S: RECORD [1]
Expand Down
24 changes: 24 additions & 0 deletions tests/stub/homedb/scripts/mixed/router.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
!: BOLT 5.8

A: HELLO {"{}": "*"}
A: LOGON {"{}": "*"}
*: RESET

{*
C: ROUTE "*" "*" {"db": "homedb1"}
S: SUCCESS { "rt": { "ttl": 1000000, "db": "homedb1", "servers": [{"addresses": ["#HOST#:9000"], "role": "ROUTE"}, {"addresses": ["#HOST#:9010"], "role": "READ"}, {"addresses": ["#HOST#:9020"], "role": "WRITE"}]}}
*: RESET
*}

# allow for resolving the home database exactly once
C: ROUTE "*" "*" {"[db]": ""}
S: SUCCESS { "rt": { "ttl": 1000000, "db": "homedb1", "servers": [{"addresses": ["#HOST#:9000"], "role": "ROUTE"}, {"addresses": ["#HOST#:9010"], "role": "READ"}, {"addresses": ["#HOST#:9020"], "role": "WRITE"}]}}
*: RESET

{*
C: ROUTE "*" "*" {"db": "homedb1"}
S: SUCCESS { "rt": { "ttl": 1000000, "db": "homedb1", "servers": [{"addresses": ["#HOST#:9000"], "role": "ROUTE"}, {"addresses": ["#HOST#:9010"], "role": "READ"}, {"addresses": ["#HOST#:9020"], "role": "WRITE"}]}}
*: RESET
*}

?: GOODBYE
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
!: BOLT 5.8
!: ALLOW CONCURRENT

A: HELLO {"{}": "*"}
A: LOGON {"{}": "*"}
Expand Down
1 change: 0 additions & 1 deletion tests/stub/homedb/scripts/mixed/router_keep_warm.script
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
!: BOLT #BOLT_VERSION#
!: ALLOW CONCURRENT

A: HELLO {"{}": "*"}
A: LOGON {"{}": "*"}
Expand Down
26 changes: 26 additions & 0 deletions tests/stub/homedb/scripts/mixed/router_multi.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
!: BOLT 5.8
!: ALLOW CONCURRENT

A: HELLO {"{}": "*"}
A: LOGON {"{}": "*"}
*: RESET

{*
C: ROUTE "*" "*" {"db": "homedb1"}
S: SUCCESS { "rt": { "ttl": 1000000, "db": "homedb1", "servers": [{"addresses": ["#HOST#:9000"], "role": "ROUTE"}, {"addresses": ["#HOST#:9010"], "role": "READ"}, {"addresses": ["#HOST#:9020"], "role": "WRITE"}]}}
*: RESET
*}

{+
C: ROUTE "*" "*" {"[db]": ""}
S: SUCCESS { "rt": { "ttl": 1000000, "db": "homedb1", "servers": [{"addresses": ["#HOST#:9000"], "role": "ROUTE"}, {"addresses": ["#HOST#:9010"], "role": "READ"}, {"addresses": ["#HOST#:9020"], "role": "WRITE"}]}}
*: RESET

{*
C: ROUTE "*" "*" {"db": "homedb1"}
S: SUCCESS { "rt": { "ttl": 1000000, "db": "homedb1", "servers": [{"addresses": ["#HOST#:9000"], "role": "ROUTE"}, {"addresses": ["#HOST#:9010"], "role": "READ"}, {"addresses": ["#HOST#:9020"], "role": "WRITE"}]}}
*: RESET
*}
+}

?: GOODBYE
25 changes: 25 additions & 0 deletions tests/stub/homedb/scripts/mixed/writer_multi.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
!: BOLT 5.8

A: HELLO {"{}": "*"}
A: LOGON {"{}": "*"}
*: RESET

{*
{{
C: RUN "RESOLVED" {} {"db": "homedb1", "[bookmarks]": {"[]": "*"}, "[mode]": "w"}
S: SUCCESS {"fields": ["n"]}
----
C: RUN "UNRESOLVED" {} {"[bookmarks]": {"[]": "*"}, "[mode]": "w"}
S: SUCCESS {"fields": ["n"], "db": "homedb1"}
}}
{{
C: PULL {"n": {"Z": "*"}, "[qid]": -1}
S: RECORD [1]
----
C: DISCARD {"n": {"Z": "*"}, "[qid]": -1}
}}
S: SUCCESS {}
*: RESET
*}

?: GOODBYE
129 changes: 123 additions & 6 deletions tests/stub/homedb/test_homedb.py
Original file line number Diff line number Diff line change
Expand Up @@ -1320,7 +1320,7 @@ def _test_mixed_cluster(self):
with self.driver() as driver:
# 1st connection => explicit home db resolution (homedb1)
with self.session(driver, "r") as session:
result = session.run("RETURN 1 AS n")
result = session.run("RESOLVED")
result.consume()

# 2nd connection has no ssr support
Expand All @@ -1332,7 +1332,7 @@ def _test_mixed_cluster(self):
# making sure the connection to the reader is still alive after
# the fallback
with self.session(driver, "r", database="homedb1") as session:
result = session.run("RETURN 3 AS n")
result = session.run("RESOLVED")
result.consume()

self._router.done()
Expand All @@ -1341,7 +1341,7 @@ def _test_mixed_cluster(self):

@driver_feature(types.Feature.BOLT_5_7)
def test_home_db_fallback_mixed_bolt_versions(self):
self.start_server(self._router, "router_5x8.script")
self.start_server(self._router, "router_fallback.script")
self.start_server(self._reader, "reader_5x8_ssr.script")
self.start_server(
self._writer1,
Expand All @@ -1364,7 +1364,7 @@ def test_home_db_fallback_mixed_bolt_versions(self):
}

def test_home_db_fallback_no_ssr_hint(self):
self.start_server(self._router, "router_5x8.script")
self.start_server(self._router, "router_fallback.script")
self.start_server(self._reader, "reader_5x8_ssr.script")
self.start_server(
self._writer1,
Expand All @@ -1382,7 +1382,7 @@ def test_home_db_fallback_no_ssr_hint(self):
def test_connection_acquisition_timeout_during_fallback(self):
self.start_server(
self._router,
"router_5x8.script",
"router_fallback.script",
vars_={
"#WRITER_2#": self._writer2.port,
},
Expand Down Expand Up @@ -1410,7 +1410,7 @@ def test_connection_acquisition_timeout_during_fallback(self):
) as driver:
# set-up driver to have home db cache enabled
with self.session(driver, "r") as session:
result = session.run("RETURN 1 AS n")
result = session.run("RESOLVED")
result.consume()

# detecting server without SSR => fallback to explicit home db
Expand Down Expand Up @@ -1471,3 +1471,120 @@ def test_warm_cache_during_cluster_upgrade(self):
result = session.run("RETURN 3 as n")
result.consume()
self._reader.done()

def test_re_enabling_cache(self):
self.start_server(self._router, "router.script")
self.start_server(self._reader, "reader_5x8_no_ssr.script")
self.start_server(self._writer1, "writer_multi.script")

with self.driver() as driver:

# GIVEN: 1 router connection with SSR, 1 reader connection without
with self.session(driver, "r") as session:
result = session.run("RETURN 1 AS n")
result.consume()

# WHEN: reader connection is killed
self._reader.done()
with self.session(driver, "r", database="homedb1") as session:
with self.assertRaises(types.DriverError):
result = session.run("NEVER REACHES THE SERVER")
result.consume()

# AND: new reader appears with SSR support
self.start_server(self._reader, "reader_5x8_ssr.script")
with self.session(driver, "r", database="homedb1") as session:
result = session.run("RESOLVED")
result.consume()

# THEN: cache is re-enabled and has an entry from the first session
with self.session(driver, "w") as session:
result = session.run("UNRESOLVED")
result.consume()

self._router.done()
self._reader.done()
self._writer1.done()

def test_re_enabling_cache_after_disabling(self):
self.start_server(self._router, "router_multi.script")
self.start_server(self._reader, "reader_5x8_ssr.script")
self.start_server(self._writer1, "writer_multi.script")

route_count = 0

with self.driver() as driver:

# GIVEN: The driver has already used the cache at least once
# - session 1 SSR is enabled, but cache miss
with self.session(driver, "r") as session:
result = session.run("RESOLVED")
result.consume()

new_route_count = self._router.count_requests("ROUTE")
self.assertEqual(route_count + 1, new_route_count)
route_count = new_route_count

# - session 2 SSR is enabled, cache hit
with self.session(driver, "r") as session:
result = session.run("UNRESOLVED")
result.consume()

# AND: The cache gets disabled
# - a new connection is made with SSR disabled
# - old reader dies
self._reader.done()
with self.session(driver, "r", database="homedb1") as session:
with self.assertRaises(types.DriverError):
result = session.run("NEVER REACHES THE SERVER")
result.consume()

# - new reader (without SSR) appears
self.start_server(self._reader, "reader_5x8_no_ssr.script")
with self.session(driver, "r", database="homedb1") as session:
result = session.run("NEW READER NO SSR")
result.consume()

# - routing table is depleted of readers => fetching new one
new_route_count = self._router.count_requests("ROUTE")
self.assertEqual(route_count + 1, new_route_count)
route_count = new_route_count

# - double check that the cache is disabled:
with self.session(driver, "r") as session:
result = session.run("EXPLICITLY RESOLVED")
result.consume()

# - new ROUTE request for explicit home db resolution
new_route_count = self._router.count_requests("ROUTE")
self.assertEqual(route_count + 1, new_route_count)
route_count = new_route_count

# AND: The cache gets enabled again
# - a new connection is made with SSR enabled
# - old reader dies
self._reader.done()
with self.session(driver, "r", database="homedb1") as session:
with self.assertRaises(types.DriverError):
result = session.run("NEVER REACHES THE SERVER")
result.consume()

# - new reader (with SSR) appears
self.start_server(self._reader, "reader_5x8_ssr.script")
with self.session(driver, "r", database="homedb1") as session:
result = session.run("RESOLVED")
result.consume()

# - routing table is depleted of readers => fetching new one
new_route_count = self._router.count_requests("ROUTE")
self.assertEqual(route_count + 1, new_route_count)
route_count = new_route_count

# THEN: cache is enabled and has an entry from the first session
with self.session(driver, "w") as session:
result = session.run("UNRESOLVED")
result.consume()

self._router.done()
self._reader.done()
self._writer1.done()