P5.5.c: Document commit phase implementation

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 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-05-23 08:04:24 -04:00
parent 334351867c
commit aca2381807

142
notes/miroir-uhj.5.3.md Normal file
View file

@ -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<u64> {
// 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`