spetz commented on code in PR #4506:
URL: https://github.com/apache/iggy/pull/4506#discussion_r4269769204


##########
core/consensus/src/plane_helpers.rs:
##########
@@ -903,6 +903,84 @@ where
         .await
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ViewLogProof {
+    Proven,
+    /// Fetch `op`; retained entries above `unproven_through` match the view.
+    Missing {
+        op: u64,
+        unproven_through: u64,
+    },
+    Divergent(u64),
+}
+
+/// Prove held entries against the view's hash chain. Canonical headers can
+/// bridge missing bodies; a gap with no header leaves its lower prefix 
unknown.
+#[must_use]
+pub fn view_log_proof(
+    commit_floor: u64,
+    canonical: &[PrepareHeader],
+    op_head: u64,
+    header_at: impl Fn(u64) -> Option<PrepareHeader>,
+) -> ViewLogProof {
+    let proven = locally_proven_commit(commit_floor, &header_at);
+    if op_head <= proven {
+        return ViewLogProof::Proven;
+    }
+    let Some(head) = canonical.first().filter(|head| head.op == op_head) else {
+        return ViewLogProof::Missing {
+            op: op_head,
+            unproven_through: op_head,
+        };
+    };
+    let mut expected = head.checksum;
+    let mut missing = None;
+    for op in (proven + 1..=op_head).rev() {
+        let header = if let Some(local) = header_at(op) {
+            local
+        } else if let Some(header) = canonical.iter().find(|header| header.op 
== op) {
+            missing.get_or_insert(op);
+            *header
+        } else {
+            return ViewLogProof::Missing {
+                op,
+                unproven_through: op,
+            };
+        };
+        if header.checksum == CHECKSUM_UNSEALED || header.checksum != expected 
{

Review Comment:
   `view_log_proof` reads a blank canonical slot (`dvc_blank`, checksum 0, 
which equals `CHECKSUM_UNSEALED`) as `Divergent(op)`, while `view_header_at` 
and `header_is_view_entry` treat the same slot as no evidence. 
`build_dvc_suffix` writes that blank at the commit point when the sender holds 
no header there (`core/shard/src/lib.rs:11553-11570`, whose warning already 
names this state), and both the probe answer 
(`core/consensus/src/impls.rs:3489-3500`) and the stale-heartbeat answer 
(`core/consensus/src/impls.rs:3805`) ship that suffix. A replica that installs 
a metadata snapshot at op S on an idle cluster and becomes primary before S+1 
commits answers every probe with `[dvc_blank(S)]`, and a prober that lacks S (a 
node rebuilt with an empty data directory, or a second lagging backup with 5 
replicas) gets `Divergent(S)`. When the blank is the head, line 936 seeds 
`expected` with 0, so even a correct local header reads as divergent. On the 
metadata plane `reconcile_metadata_view_diver
 gence` then calls `remove_range(S..=S)` at `core/shard/src/lib.rs:6487`, which 
returns `Ok(0)` before its only await 
(`core/journal/src/prepare_journal.rs:997`), and `pending` is cloned once, so 
the loop never yields and freezes the shard thread at 100% CPU. A restart does 
not help until the primary commits a later op. On the partition plane every 
`ViewApplied` re-defers the same one-op truncation, and the replica stays 
unproven and sends no PrepareOk. Unit tests at this head reproduce all three: 
the proof returns `Divergent(4)` for an op the backup does not hold, the 
metadata reconcile does not finish within 20s while the same scenario with an 
empty suffix passes in 0.02s, and the partition truncation is re-deferred 256 
times. Both repair ingest gates (`core/shard/src/lib.rs:5521` and 
`core/partitions/src/iggy_partition.rs:11650`) also compare repaired prepares 
against the blank checksum 0 and drop every copy.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to