Skip to content

Commit 023c7d8

Browse files
robsdedudeMaxAakeinjectives
authored
HomeDB cache: mixed cluster (#648)
* Mixed cluster tests: * old & new protocol version mixed * with & without connection hint mixed * tests driver maintains cache when ssr is disabled * Feature flag for home database cache Co-authored-by: MaxAake <[email protected]> Co-authored-by: Dmitriy Tverdiakov <[email protected]> Co-authored-by: Dmitriy Tverdiakov <[email protected]>
1 parent d79369a commit 023c7d8

File tree

13 files changed

+382
-35
lines changed

13 files changed

+382
-35
lines changed

nutkit/frontend/driver.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def __init__(self, backend, uri, auth_token, user_agent=None,
1515
connection_timeout_ms=None, fetch_size=None,
1616
max_tx_retry_time_ms=None, encrypted=None,
1717
trusted_certificates=None, liveness_check_timeout_ms=None,
18+
max_connection_lifetime_ms=None,
1819
max_connection_pool_size=None,
1920
connection_acquisition_timeout_ms=None,
2021
notifications_min_severity=None,
@@ -58,6 +59,7 @@ def __init__(self, backend, uri, auth_token, user_agent=None,
5859
fetchSize=fetch_size, maxTxRetryTimeMs=max_tx_retry_time_ms,
5960
encrypted=encrypted, trustedCertificates=trusted_certificates,
6061
liveness_check_timeout_ms=liveness_check_timeout_ms,
62+
max_connection_lifetime_ms=max_connection_lifetime_ms,
6163
max_connection_pool_size=max_connection_pool_size,
6264
connection_acquisition_timeout_ms=connection_acquisition_timeout_ms, # noqa: E501
6365
notifications_min_severity=notifications_min_severity,

nutkit/protocol/feature.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class Feature(Enum):
2727
# The driver offers a method for driver objects to report if they were
2828
# configured with a or without encryption.
2929
API_DRIVER_IS_ENCRYPTED = "Feature:API:Driver.IsEncrypted"
30+
# The driver supports setting a custom max connection lifetime
31+
API_DRIVER_MAX_CONNECTION_LIFETIME = \
32+
"Feature:API:Driver:MaxConnectionLifetime"
3033
# The driver supports notification filters configuration.
3134
API_DRIVER_NOTIFICATIONS_CONFIG = "Feature:API:Driver:NotificationsConfig"
3235
# The driver offers a method for checking if the provided authentication
@@ -169,6 +172,10 @@ class Feature(Enum):
169172
# sending BEGIN but pipelines the RUN and PULL right afterwards and
170173
# consumes three messages after that. This saves 2 full round-trips.
171174
OPT_EXECUTE_QUERY_PIPELINING = "Optimization:ExecuteQueryPipelining"
175+
# The driver implements a cache to match users to their most recently
176+
# resolved home database, routing requests with no set database to this
177+
# cached database if all open connections have an SSR connection hint.
178+
OPT_HOME_DB_CACHE = "Optimization:HomeDatabaseCache"
172179
# The home db cache for optimistic home db resolution treats the principal
173180
# in basic auth the exact same way it treats impersonated users.
174181
OPT_HOME_DB_CACHE_BASIC_PRINCIPAL_IS_IMP_USER = \

nutkit/protocol/requests.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ def __init__(
7171
domainNameResolverRegistered=False, connectionTimeoutMs=None,
7272
fetchSize=None, maxTxRetryTimeMs=None,
7373
encrypted=None, trustedCertificates=None,
74-
liveness_check_timeout_ms=None, max_connection_pool_size=None,
74+
liveness_check_timeout_ms=None, max_connection_lifetime_ms=None,
75+
max_connection_pool_size=None,
7576
connection_acquisition_timeout_ms=None,
7677
notifications_min_severity=None,
7778
notifications_disabled_categories=None,
@@ -92,6 +93,8 @@ def __init__(
9293
self.fetchSize = fetchSize
9394
self.maxTxRetryTimeMs = maxTxRetryTimeMs
9495
self.livenessCheckTimeoutMs = liveness_check_timeout_ms
96+
if max_connection_lifetime_ms is not None:
97+
self.maxConnectionLifetimeMs = max_connection_lifetime_ms
9598
self.maxConnectionPoolSize = max_connection_pool_size
9699
self.connectionAcquisitionTimeoutMs = connection_acquisition_timeout_ms
97100
assert (client_certificate is None

tests/shared.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import os
1717
import re
1818
import socket
19+
import time
1920
import unittest
2021
import warnings
2122
from contextlib import contextmanager
@@ -24,6 +25,7 @@
2425

2526
from nutkit import protocol
2627
from nutkit.backend import Backend
28+
from nutkit.frontend import FakeTime
2729

2830

2931
def get_backend_host_and_port():
@@ -255,3 +257,35 @@ class Potential(enum.Enum):
255257
NO = 0.0
256258
MAYBE = 0.5
257259
# CAN_YOU_REPEAT_THE_QUESTION = "?"
260+
261+
262+
class TimeoutManager:
263+
def __init__(
264+
self, test_case: TestkitTestCase, timeout_ms: int,
265+
use_real_timers: bool = False
266+
):
267+
self._timeout_ms = timeout_ms
268+
self._fake_time = None
269+
if test_case.driver_supports_features(
270+
protocol.Feature.BACKEND_MOCK_TIME
271+
) and not use_real_timers:
272+
self._fake_time = FakeTime(test_case._backend)
273+
274+
def __enter__(self):
275+
if self._fake_time:
276+
self._fake_time.__enter__()
277+
return self
278+
279+
def __exit__(self, exc_type, exc_val, exc_tb):
280+
if self._fake_time:
281+
self._fake_time.__exit__(exc_type, exc_val, exc_tb)
282+
283+
def tick_to_before_timeout(self):
284+
if self._fake_time:
285+
self._fake_time.tick(self._timeout_ms - 1)
286+
287+
def tick_to_after_timeout(self):
288+
if self._fake_time:
289+
self._fake_time.tick(self._timeout_ms + 1)
290+
else:
291+
time.sleep(self._timeout_ms / 1000)

tests/stub/driver_parameters/test_liveness_check.py

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import time
21
from contextlib import contextmanager
32

43
import nutkit.protocol as types
@@ -9,42 +8,11 @@
98
from tests.shared import (
109
driver_feature,
1110
TestkitTestCase,
11+
TimeoutManager,
1212
)
1313
from tests.stub.shared import StubServer
1414

1515

16-
class TimeoutManager:
17-
def __init__(
18-
self, test_case: TestkitTestCase, timeout_ms: int,
19-
use_real_timers: bool = False
20-
):
21-
self._timeout_ms = timeout_ms
22-
self._fake_time = None
23-
if test_case.driver_supports_features(
24-
types.Feature.BACKEND_MOCK_TIME
25-
) and not use_real_timers:
26-
self._fake_time = FakeTime(test_case._backend)
27-
28-
def __enter__(self):
29-
if self._fake_time:
30-
self._fake_time.__enter__()
31-
return self
32-
33-
def __exit__(self, exc_type, exc_val, exc_tb):
34-
if self._fake_time:
35-
self._fake_time.__exit__(exc_type, exc_val, exc_tb)
36-
37-
def tick_to_before_timeout(self):
38-
if self._fake_time:
39-
self._fake_time.tick(self._timeout_ms - 1)
40-
41-
def tick_to_after_timeout(self):
42-
if self._fake_time:
43-
self._fake_time.tick(self._timeout_ms + 1)
44-
else:
45-
time.sleep(self._timeout_ms / 1000)
46-
47-
4816
class TestLivenessCheck(TestkitTestCase):
4917
required_features = (
5018
types.Feature.BOLT_5_4,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
!: BOLT 5.7
2+
3+
A: HELLO {"{}": "*"}
4+
A: LOGON {"{}": "*"}
5+
*: RESET
6+
7+
C: RUN {"U": "*"} {} {"db": "homedb1", "[bookmarks]": {"[]": "*"}, "mode": "r"}
8+
S: SUCCESS {"fields": ["n"]}
9+
{{
10+
C: PULL {"n": {"Z": "*"}, "[qid]": -1}
11+
S: RECORD [1]
12+
----
13+
C: DISCARD {"n": {"Z": "*"}, "[qid]": -1}
14+
}}
15+
S: SUCCESS {}
16+
*: RESET
17+
18+
19+
?: GOODBYE
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
!: BOLT 5.8
2+
3+
A: HELLO {"{}": "*"}
4+
A: LOGON {"{}": "*"}
5+
*: RESET
6+
7+
C: RUN {"U": "*"} {} {"db": "homedb1", "[bookmarks]": {"[]": "*"}, "mode": "r", "imp_user": "user2"}
8+
S: SUCCESS {"fields": ["n"]}
9+
{{
10+
C: PULL {"n": {"Z": "*"}, "[qid]": -1}
11+
S: RECORD [1]
12+
----
13+
C: DISCARD {"n": {"Z": "*"}, "[qid]": -1}
14+
}}
15+
S: SUCCESS {}
16+
*: RESET
17+
18+
C: RUN {"U": "*"} {} {"[bookmarks]": {"[]": "*"}, "mode": "r"}
19+
S: SUCCESS {"fields": ["n"]}
20+
{{
21+
C: PULL {"n": {"Z": "*"}, "[qid]": -1}
22+
S: RECORD [1]
23+
----
24+
C: DISCARD {"n": {"Z": "*"}, "[qid]": -1}
25+
}}
26+
S: SUCCESS {}
27+
*: RESET
28+
29+
?: GOODBYE
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
!: BOLT 5.8
2+
3+
A: HELLO {"{}": "*"}
4+
A: LOGON {"{}": "*"}
5+
*: RESET
6+
7+
{+
8+
C: RUN {"U": "*"} {} {"db": "homedb1", "[bookmarks]": {"[]": "*"}, "mode": "r"}
9+
S: SUCCESS {"fields": ["n"]}
10+
{{
11+
C: PULL {"n": {"Z": "*"}, "[qid]": -1}
12+
S: RECORD [1]
13+
----
14+
C: DISCARD {"n": {"Z": "*"}, "[qid]": -1}
15+
}}
16+
S: SUCCESS {}
17+
*: RESET
18+
+}
19+
20+
?: GOODBYE
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
!: BOLT 5.8
2+
!: ALLOW CONCURRENT
3+
4+
A: HELLO {"{}": "*"}
5+
A: LOGON {"{}": "*"}
6+
*: RESET
7+
8+
9+
# explicit resolution (1st connection)
10+
C: ROUTE "*" "*" "*"
11+
S: SUCCESS { "rt": { "ttl": 1000000, "db": "homedb1", "servers": [{"addresses": ["#HOST#:9000"], "role": "ROUTE"}, {"addresses": ["#HOST#:9010"], "role": "READ"}, {"addresses": ["#HOST#:9020"], "role": "WRITE"}]}}
12+
*: RESET
13+
14+
# explicit resolution (fallback connection)
15+
C: ROUTE "*" "*" "*"
16+
S: SUCCESS { "rt": { "ttl": 1000000, "db": "homedb2", "servers": [{"addresses": ["#HOST#:9000"], "role": "ROUTE"}, {"addresses": ["#HOST#:9010"], "role": "READ"}, {"addresses": ["#HOST#:#WRITER_2#"], "role": "WRITE"}]}}
17+
*: RESET
18+
19+
?: GOODBYE
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
!: BOLT #BOLT_VERSION#
2+
!: ALLOW CONCURRENT
3+
4+
A: HELLO {"{}": "*"}
5+
A: LOGON {"{}": "*"}
6+
*: RESET
7+
8+
C: ROUTE {"{}": "*"} [] {"[db]": null#IMPERSONATED_USER#}
9+
S: SUCCESS { "rt": { "ttl": 1000000, "db": "homedb1", "servers": [{"addresses": ["#HOST#:9000"], "role": "ROUTE"}, {"addresses": ["#HOST#:9010"], "role": "READ"}, {"addresses": [], "role": "WRITE"}]}}
10+
*: RESET

0 commit comments

Comments
 (0)