Skip to content

Commit 96889a7

Browse files
test(maint-notifications): address Bugbot review on the reshard timeout guard
- Pytest cap vs drain (Medium): RESHARD_TEST_TIMEOUT is now RESHARD_OP_TIMEOUT + SMIGRATED_TIMEOUT + margin (900s), so a slow ASM scale that uses the full op-wait and then drains the node cache post-join is not killed mid-reconciliation. - Shared trigger timeout leaking to standalone (Low): revert _trigger_effect's default op-wait to a modest DEFAULT_TRIGGER_OP_TIMEOUT (180s, under the standalone tests' 300s cap); the 6 OSS reshard tests now pass RESHARD_OP_TIMEOUT (600s) explicitly. - Drain helper silent timeout (Medium): _wait_for_node_cache_delta now pytest.fail()s on timeout instead of returning silently, so a failed reconciliation surfaces clearly instead of as a misleading node-count assert. - node_add missing invariants (Low): assert cache size unchanged + all initial node keys present during SMIGRATING, mirroring test_notification_handling_with_node_remove.
1 parent 51a8fd6 commit 96889a7

1 file changed

Lines changed: 33 additions & 13 deletions

File tree

tests/test_scenario/test_maint_notifications.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,15 @@
6767
# the command-execution workers are left running (their join then hangs to the pytest cap). Cover
6868
# the FI's slowest reshard (ASM scale = 600s).
6969
RESHARD_OP_TIMEOUT = 600
70-
# Per-test cap for the OSS-cluster reshard tests: must exceed RESHARD_OP_TIMEOUT so a legitimately
71-
# slow ASM scale runs to completion instead of being killed mid-reshard (only bites on the slow
72-
# path; fast reshards are unaffected).
73-
RESHARD_TEST_TIMEOUT = 660
70+
# Default op-wait for non-reshard (standalone / single-window) triggers: comfortably under the
71+
# standalone tests' 300s pytest cap so a stuck FI poll can't outlive the cap and block teardown on
72+
# thread.join(). The reshard tests pass RESHARD_OP_TIMEOUT explicitly.
73+
DEFAULT_TRIGGER_OP_TIMEOUT = 180
74+
# Per-test cap for the OSS-cluster reshard tests: must exceed the worst-case reshard wall time =
75+
# the trigger op-wait (RESHARD_OP_TIMEOUT) plus the post-join node-cache drain (SMIGRATED_TIMEOUT),
76+
# with margin, so a legitimately slow ASM scale runs to completion instead of being killed mid-way.
77+
# Only bites on the slow path; fast reshards are unaffected.
78+
RESHARD_TEST_TIMEOUT = RESHARD_OP_TIMEOUT + SMIGRATED_TIMEOUT + 180
7479

7580
DEFAULT_BIND_TTL = 15
7681
DEFAULT_STANDALONE_CLIENT_SOCKET_TIMEOUT = 1
@@ -124,7 +129,7 @@ def _trigger_effect(
124129
target_node: Optional[str] = None,
125130
empty_node: Optional[str] = None,
126131
skip_end_notification: bool = False,
127-
timeout: int = RESHARD_OP_TIMEOUT,
132+
timeout: int = DEFAULT_TRIGGER_OP_TIMEOUT,
128133
):
129134
trigger_effect_action_id = ClusterOperations.trigger_effect(
130135
fault_injector=fault_injector_client,
@@ -1454,14 +1459,12 @@ def _wait_for_node_cache_delta(
14541459
# A connection to a node being removed may error; that is expected.
14551460
pass
14561461
time.sleep(0.5)
1457-
# Best-effort: the caller asserts the exact delta next, which will fail loudly if the
1458-
# cache never reconciled. Log so a timeout here is not silent.
1459-
logging.warning(
1460-
"Node cache did not reach %s (delta %s) within %ss; current size=%s",
1461-
expected,
1462-
expected_delta,
1463-
timeout,
1464-
len(client.nodes_manager.nodes_cache),
1462+
# Fail explicitly on a drain timeout so it surfaces as a clear "node cache did not
1463+
# reconcile" error, rather than silently returning and letting the caller's exact-delta
1464+
# assertion fail later with a misleading node-count message.
1465+
pytest.fail(
1466+
f"Node cache did not reach {expected} (delta {expected_delta}) within {timeout}s; "
1467+
f"current size={len(client.nodes_manager.nodes_cache)}"
14651468
)
14661469

14671470
@pytest.fixture(autouse=True)
@@ -1541,6 +1544,7 @@ def test_notification_handling_during_node_shuffle_no_node_replacement(
15411544
effect_name,
15421545
trigger,
15431546
),
1547+
kwargs={"timeout": RESHARD_OP_TIMEOUT},
15441548
)
15451549
self.maintenance_ops_threads.append(trigger_effect_thread)
15461550
trigger_effect_thread.start()
@@ -1657,6 +1661,7 @@ def test_notification_handling_with_node_replace(
16571661
effect_name,
16581662
trigger,
16591663
),
1664+
kwargs={"timeout": RESHARD_OP_TIMEOUT},
16601665
)
16611666
self.maintenance_ops_threads.append(trigger_effect_thread)
16621667
trigger_effect_thread.start()
@@ -1778,6 +1783,7 @@ def test_notification_handling_with_node_remove(
17781783
effect_name,
17791784
trigger,
17801785
),
1786+
kwargs={"timeout": RESHARD_OP_TIMEOUT},
17811787
)
17821788
self.maintenance_ops_threads.append(trigger_effect_thread)
17831789
trigger_effect_thread.start()
@@ -1910,6 +1916,7 @@ def test_notification_handling_with_node_add(
19101916
effect_name,
19111917
trigger,
19121918
),
1919+
kwargs={"timeout": RESHARD_OP_TIMEOUT},
19131920
)
19141921
self.maintenance_ops_threads.append(trigger_effect_thread)
19151922
trigger_effect_thread.start()
@@ -1928,6 +1935,17 @@ def test_notification_handling_with_node_add(
19281935
assert conn._sock.gettimeout() == RELAXED_TIMEOUT
19291936
assert conn.should_reconnect() is False
19301937

1938+
# During SMIGRATING (before reconciliation) the added node is not in the cache yet: the
1939+
# cache size is unchanged and every initial node key is still present. Mirrors the same
1940+
# invariant asserted by test_notification_handling_with_node_remove.
1941+
assert len(initial_cluster_nodes) == len(
1942+
cluster_client_maint_notifications.nodes_manager.nodes_cache
1943+
)
1944+
for node_key in initial_cluster_nodes.keys():
1945+
assert (
1946+
node_key in cluster_client_maint_notifications.nodes_manager.nodes_cache
1947+
)
1948+
19311949
logging.info("Waiting for SMIGRATED push notifications...")
19321950
con_to_read_smigrated = random.choice(list(in_use_connections.values()))
19331951
ClientValidations.wait_push_notification(
@@ -2049,6 +2067,7 @@ def test_new_connections_receive_last_smigrating_smigrated_notification(
20492067
effect_name,
20502068
trigger,
20512069
),
2070+
kwargs={"timeout": RESHARD_OP_TIMEOUT},
20522071
)
20532072

20542073
self.maintenance_ops_threads.append(trigger_effect_thread)
@@ -2223,6 +2242,7 @@ def execute_commands(duration: int, errors: Queue):
22232242
effect_name,
22242243
trigger,
22252244
),
2245+
kwargs={"timeout": RESHARD_OP_TIMEOUT},
22262246
)
22272247
self.maintenance_ops_threads.append(trigger_effect_thread)
22282248
trigger_effect_thread.start()

0 commit comments

Comments
 (0)