huskies: merge 889

This commit is contained in:
dave
2026-05-01 14:56:13 +00:00
parent 61cf7684de
commit f8a295eaec
7 changed files with 215 additions and 3 deletions
+18 -2
View File
@@ -5,11 +5,11 @@
//! persistence task. It is safe to call only once; subsequent calls are
//! no-ops (guarded by [`super::CRDT_STATE`]).
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::path::Path;
use std::sync::Mutex;
use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp};
use bft_json_crdt::json_crdt::{BaseCrdt, CrdtNode, JsonValue, SignedOp};
use bft_json_crdt::keypair::make_keypair;
use fastcrypto::ed25519::Ed25519KeyPair;
use fastcrypto::traits::ToFromBytes;
@@ -71,6 +71,21 @@ pub async fn init(db_path: &Path) -> Result<(), sqlx::Error> {
let _ = ALL_OPS.set(Mutex::new(all_ops_vec));
let _ = VECTOR_CLOCK.set(Mutex::new(vector_clock));
// Rebuild tombstone set: deleted list items still carry their original
// PipelineItemCrdt content, so we can extract the story_id directly.
let tombstones: HashSet<String> = crdt
.doc
.items
.ops
.iter()
.filter(|op| op.is_deleted)
.filter_map(|op| op.content.as_ref())
.filter_map(|item| match item.story_id.view() {
JsonValue::String(s) if !s.is_empty() => Some(s),
_ => None,
})
.collect();
// Build the indices from the reconstructed state.
let index = rebuild_index(&crdt);
let node_index = rebuild_node_index(&crdt);
@@ -151,6 +166,7 @@ pub async fn init(db_path: &Path) -> Result<(), sqlx::Error> {
gateway_project_index,
persist_tx,
lamport_floor,
tombstones,
};
let _ = CRDT_STATE.set(Mutex::new(state));
+8 -1
View File
@@ -6,7 +6,7 @@
//! - [`init`]: async startup and keypair persistence
//! - [`apply`]: write path (sign, apply, persist, broadcast)
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::sync::{Mutex, OnceLock};
use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp};
@@ -67,6 +67,12 @@ pub(super) struct CrdtState {
/// Newly-created registers (post-init) must have their Lamport clock
/// advanced to this floor so they don't re-emit low sequence numbers.
pub(super) lamport_floor: u64,
/// Story IDs permanently tombstoned via `evict_item`.
///
/// `write_item` consults this set before inserting a new CRDT entry so
/// that a concurrent or late-arriving write cannot resurrect a deleted
/// story. Rebuilt from the CRDT op log on restart.
pub(super) tombstones: HashSet<String>,
}
// ── Singleton and accessor ───────────────────────────────────────────
@@ -134,6 +140,7 @@ pub fn init_for_test() {
gateway_project_index: HashMap::new(),
persist_tx,
lamport_floor: 0,
tombstones: HashSet::new(),
};
let _ = lock.set(Mutex::new(state));
}
+2
View File
@@ -176,6 +176,7 @@ fn persist_tx_send_failure_logs_warn_with_op_type_and_seq() {
gateway_project_index: HashMap::new(),
persist_tx,
lamport_floor: 0,
tombstones: std::collections::HashSet::new(),
};
// Drop the receiver so that the next send fails immediately.
@@ -249,6 +250,7 @@ fn persist_tx_send_success_emits_no_warn() {
gateway_project_index: HashMap::new(),
persist_tx,
lamport_floor: 0,
tombstones: std::collections::HashSet::new(),
};
let item_json: JsonValue = json!({