Make the duplicate-reservation ack gate O(1) for fresh node ids
node_has_undrained_write_ack scanned ack_write_queue on every reservation, including the per-request-per-chunk prepare path whose node ids are freshly minted and can never be queued. Track the max node id ever appended to the queue: any queued id is <= max by construction (no monotonicity assumption needed for correctness), so fresh ids exit on one integer compare and the scan remains only for re-reservations of old ids (the rare write_backup fallback). All three ack_write_queue append sites now go through a single _append_write_ack funnel that maintains the max — the zero-owned-rank and non-CP write acks previously would have bypassed it, allowing false negatives on ranks owning no pages of a node. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2176,6 +2176,17 @@ class TestHiRadixCacheCPBackup(CustomTestCase):
|
||||
self.assertEqual(cache.ongoing_write_through, {53: attached_node})
|
||||
self.assertEqual(dec_locked, [])
|
||||
|
||||
@staticmethod
|
||||
def _make_exploding_queue():
|
||||
class _ExplodingQueue(list):
|
||||
def __iter__(self):
|
||||
raise AssertionError("fresh node ids must not scan the ack queue")
|
||||
|
||||
def __bool__(self):
|
||||
return True
|
||||
|
||||
return _ExplodingQueue()
|
||||
|
||||
def test_reserve_write_cp_refuses_node_with_undrained_ack(self):
|
||||
"""Producer-side invariant: at most one in-flight ack per node_id.
|
||||
A reservation for a node whose previous final ack is still queued is
|
||||
@@ -2190,6 +2201,7 @@ class TestHiRadixCacheCPBackup(CustomTestCase):
|
||||
ack_write_queue=[
|
||||
HiCacheAck(MagicMock(), MagicMock(), [4614]),
|
||||
],
|
||||
_max_write_ack_node_id=4614,
|
||||
)
|
||||
controller.node_has_undrained_write_ack = types.MethodType(
|
||||
HiCacheController.node_has_undrained_write_ack, controller
|
||||
@@ -2201,9 +2213,15 @@ class TestHiRadixCacheCPBackup(CustomTestCase):
|
||||
)
|
||||
self.assertIsInstance(result, HiCacheWriteFailure)
|
||||
self.assertEqual(result.required_host_slots, 0)
|
||||
# Different node id is not gated by the queue scan.
|
||||
# An old (<= max) but absent node id is resolved by the queue scan.
|
||||
self.assertFalse(
|
||||
HiCacheController.node_has_undrained_write_ack(controller, 4615)
|
||||
HiCacheController.node_has_undrained_write_ack(controller, 4613)
|
||||
)
|
||||
# Fresh ids (> max ever acked, i.e. every prepare-path reservation)
|
||||
# take the O(1) fast path: no queue scan at all.
|
||||
controller.ack_write_queue = self._make_exploding_queue()
|
||||
self.assertFalse(
|
||||
HiCacheController.node_has_undrained_write_ack(controller, 9999)
|
||||
)
|
||||
|
||||
def test_rollback_pending_backup_scrubs_acks_and_cancels_state(self):
|
||||
|
||||
Reference in New Issue
Block a user