From aca2381807ff1ce99dc79fe5027841d5519cc934 Mon Sep 17 00:00:00 2001 From: jedarden Date: Sat, 23 May 2026 08:04:24 -0400 Subject: [PATCH] P5.5.c: Document commit phase implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit phase (Phase 3) of the two-phase settings broadcast is fully implemented. This includes: - Settings version increment in task store - Per-node version advancement in node_settings_version table - X-Miroir-Settings-Version header stamping on search responses - Broadcast completion and in-flight state clearing All tests pass and the implementation follows plan §13.5. Co-Authored-By: Claude Opus 4.7 --- notes/miroir-uhj.5.3.md | 142 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 notes/miroir-uhj.5.3.md diff --git a/notes/miroir-uhj.5.3.md b/notes/miroir-uhj.5.3.md new file mode 100644 index 0000000..814401a --- /dev/null +++ b/notes/miroir-uhj.5.3.md @@ -0,0 +1,142 @@ +# P5.5.c: Commit Phase Implementation - Settings Version Increment + +**Bead:** miroir-uhj.5.3 +**Date:** 2026-05-23 +**Plan Reference:** §13.5 Two-phase settings broadcast with verification + +## Summary + +The commit phase (Phase 3) of the two-phase settings broadcast is **already fully implemented** in the codebase. This phase is the critical moment where: +1. The cluster-wide `settings_version` is incremented in the task store +2. All verified nodes have their `node_settings_version` advanced +3. Future responses include the `X-Miroir-Settings-Version` header +4. New writes are allowed to proceed freely (broadcast completes) + +## Implementation Details + +### 1. Core Commit Logic + +**File:** `crates/miroir-core/src/settings.rs:228-269` + +```rust +pub async fn commit(&self, index: &str) -> Result { + // Increment global settings version + let mut version = self.settings_version.write().await; + *version += 1; + let new_version = *version; + + // Update per-node versions for all nodes that verified successfully + let mut node_versions = self.node_settings_version.write().await; + let now = now_ms(); + for node_id in status.node_hashes.keys() { + node_versions.insert((index.to_string(), node_id.clone()), new_version); + + // Persist to task store + if let Some(ref store) = self.task_store { + let _ = store.upsert_node_settings_version( + index, + node_id, + new_version as i64, + now, + ); + } + } + + status.phase = BroadcastPhase::Commit; + status.settings_version = Some(new_version); + + Ok(new_version) +} +``` + +### 2. Integration in Proxy Layer + +**File:** `crates/miroir-proxy/src/routes/indexes.rs:1071-1093` + +The commit phase is called after successful verification: +```rust +// Phase 3: Commit - increment settings version +let new_version = state.settings_broadcast.commit(index).await?; + +// Update settings version metric +state.metrics.set_settings_version(index, new_version); +state.metrics.clear_settings_broadcast_phase(index); + +// Complete and remove from in-flight tracking +state.settings_broadcast.complete(index).await.ok(); +``` + +### 3. Header Stamping + +**File:** `crates/miroir-proxy/src/routes/search.rs` + +The `X-Miroir-Settings-Version` header is stamped on search responses: + +**Single search (lines 489-492):** +```rust +let current_version = state.settings_broadcast.current_version().await; +if current_version > 0 { + response = response.header("X-Miroir-Settings-Version", current_version.to_string()); +} +``` + +**Multi-target search (lines 836-838):** +```rust +let current_version = state.settings_broadcast.current_version().await; +if current_version > 0 { + response = response.header("X-Miroir-Settings-Version", current_version.to_string()); +} +``` + +### 4. Per-Node Version Persistence + +**Schema:** `node_settings_version` table +```sql +CREATE TABLE IF NOT EXISTS node_settings_version ( + index_uid TEXT NOT NULL, + node_id TEXT NOT NULL, + version INTEGER NOT NULL, + updated_at INTEGER NOT NULL, + PRIMARY KEY (index_uid, node_id) +); +``` + +**Task Store Operations:** +- `upsert_node_settings_version(index_uid, node_id, version, updated_at)` - Persist per-node version +- `get_node_settings_version(index_uid, node_id)` - Retrieve for X-Miroir-Min-Settings-Version checks + +## Phase 3 Flow + +1. **Increment global version:** `settings_version` is incremented atomically +2. **Advance per-node versions:** Each node that verified successfully gets its version advanced +3. **Persist to task store:** `node_settings_version` table is updated for all (index, node) pairs +4. **Stamp responses:** Future search responses include `X-Miroir-Settings-Version` header +5. **Clear in-flight state:** Broadcast is removed from in-flight tracking +6. **Allow new writes:** Subsequent settings updates can proceed + +## Client Behavior + +Clients can observe the new settings version via: +- **Response header:** `X-Miroir-Settings-Version` on search responses +- **Freshness floor:** Echo back as `X-Miroir-Min-Settings-Version` for session-consistent reads +- **Staleness detection:** Nodes with `node_settings_version < floor` are excluded from covering set + +## Metrics + +- `miroir_settings_version` (gauge): Increments only on successful commit +- `miroir_settings_broadcast_phase` (gauge): Cleared after commit + +## Testing + +All tests pass: +- `test_two_phase_settings_broadcast_normal_flow` - ✅ Verifies version increment +- `test_node_settings_version_tracking_multiple_updates` - ✅ Verifies per-node tracking +- `test_settings_version_persistence_to_task_store` - ✅ Verifies persistence + +## References + +- Plan §13.5: `/home/coding/miroir/docs/plan/plan.md` +- Core implementation: `/home/coding/miroir/crates/miroir-core/src/settings.rs:228-269` +- Proxy integration: `/home/coding/miroir/crates/miroir-proxy/src/routes/indexes.rs:1071-1093` +- Header stamping: `/home/coding/miroir/crates/miroir-proxy/src/routes/search.rs:489-492,836-838` +- Tests: `/home/coding/miroir/crates/miroir-proxy/tests/p5_5_two_phase_settings_broadcast.rs`