huskies: merge 889
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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!({
|
||||
|
||||
Reference in New Issue
Block a user