fix: align write-through pending across tree cores (#37278)

This commit is contained in:
Shuwen Wang
2026-09-04 22:08:25 +08:00
committed by GitHub
parent 88021b0734
commit 19b46863f3
13 changed files with 228 additions and 38 deletions
@@ -928,8 +928,10 @@ class RustUnifiedTreeCore(UnifiedTreeCoreInterface):
result = DropSubtreeNoHostResult(is_dropped=binding_result.dropped)
return _fill_evict_result(binding_result, result)
def mark_write_through_pending(self, node_id: NodeId) -> None:
self._binding.mark_write_through_pending(node_id)
def mark_write_through_pending(
self, node_ids: list[NodeId], ack_id: NodeId
) -> list[NodeId]:
return self._binding.mark_write_through_pending(list(node_ids), ack_id)
def finish_write_through(self, node_ids: list[NodeId], ack_id: int) -> None:
self._binding.finish_write_through(list(node_ids), ack_id)
@@ -485,7 +485,7 @@ class UnifiedCacheLinkerWrapper:
cache.dec_lock_ref(node_id, lock_params)
return
cache.tree_core.mark_write_through_pending(node_id)
cache.tree_core.mark_write_through_pending([node_id], ack_id=node_id)
node.external_cache_stored = True
self.pending_offloads.append(_PendingOffload(node_id, lock_params, [node_id]))
@@ -2203,10 +2203,29 @@ class UnifiedTreeCore(UnifiedTreeCoreInterface):
self._update_duplicate_tracking(node)
node = node.parent
def mark_write_through_pending(self, node_id: NodeId) -> None:
"""Mark a node as having an in-flight write-through backup."""
node = self.node_by_id(node_id)
node.write_through_pending_id = node_id
def mark_write_through_pending(
self, node_ids: list[NodeId], ack_id: NodeId
) -> list[NodeId]:
"""Stamp ack_id on every covered node; returns them ancestors first."""
marked: list[tuple[int, NodeId]] = []
for node_id in node_ids:
node = self.node_by_id(node_id)
assert node.write_through_pending_id in (
None,
ack_id,
), f"node {node.id} is already pending under a different write-through ack"
node.write_through_pending_id = ack_id
marked.append((self._depth_from_root(node), node_id))
marked.sort()
return [node_id for _, node_id in marked]
@staticmethod
def _depth_from_root(node: UnifiedTreeNode) -> int:
depth = 0
while node.parent is not None:
depth += 1
node = node.parent
return depth
def finish_write_through(self, node_ids: list[NodeId], ack_id: int) -> None:
"""Clear the write-through-pending mark (when it matches ack_id) and record the
@@ -537,8 +537,11 @@ class UnifiedTreeCoreInterface(ABC):
write_back_duplicate_reclaim_digest: int = 0
@abstractmethod
def mark_write_through_pending(self, node_id: NodeId) -> None:
"""Mark a node as having an in-flight write-through backup."""
def mark_write_through_pending(
self, node_ids: list[NodeId], ack_id: NodeId
) -> list[NodeId]:
"""Mark every node covered by one in-flight write-through backup, and return
them ancestors first: publish links each host store event to its parent."""
...
@abstractmethod
@@ -1370,10 +1370,26 @@ class UnifiedRadixCache(BasePrefixCache):
lock_params = None
if not write_back:
lock_params = self.inc_lock_ref(node_id).to_dec_params()
self._track_write_through_node(node_id, lock_params)
publish_node_ids = self._backup_publish_node_ids(node_id, comp_xfers)
self._track_write_through_node(
node_id, lock_params, publish_node_ids=publish_node_ids
)
written = len(host_indices)
return written
@staticmethod
def _backup_publish_node_ids(
node_id: NodeId, comp_xfers: dict[ComponentType, list[PoolTransfer]]
) -> list[NodeId]:
"""The acked node plus every node a component backup transfer covers."""
publish_node_ids: list[NodeId] = []
for transfers in comp_xfers.values():
for transfer in transfers:
publish_node_ids.extend(transfer.nodes_to_load or ())
if node_id not in publish_node_ids:
publish_node_ids.append(node_id)
return list(dict.fromkeys(publish_node_ids))
def _build_backup_sidecar(self, device_value, comp_xfers):
"""Gather sidecar transfer spec."""
kv_xfer = PoolTransfer(name=PoolName.KV, device_indices=device_value)
@@ -1399,10 +1415,13 @@ class UnifiedRadixCache(BasePrefixCache):
self,
node_id: NodeId,
lock_params: Optional[DecLockRefParams],
publish_node_ids: list[NodeId],
) -> None:
self.tree_core.mark_write_through_pending(node_id)
publish_node_ids = self.tree_core.mark_write_through_pending(
publish_node_ids, ack_id=node_id
)
self.ongoing_write_through[node_id] = _OngoingWriteThrough(
node_id, lock_params, [node_id]
node_id, lock_params, publish_node_ids
)
def _replace_pending_write_through_node(