Skip to content

Commit

Permalink
Reject empty arcs and add more docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ThetaSinner committed Dec 20, 2024
1 parent 1aed111 commit f83dd17
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 7 deletions.
46 changes: 42 additions & 4 deletions crates/dht/src/arc_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,56 @@ impl ArcSet {
}

/// Get the intersection of two arc sets as a new [ArcSet].
///
/// # Example
///
/// ```rust
/// use kitsune2_api::DhtArc;
/// use kitsune2_dht::ArcSet;
///
/// # fn main() -> kitsune2_api::K2Result<()> {
/// use tracing::Instrument;
/// let arc_size = 1 << 23;
/// let arc_1 = DhtArc::Arc(0, 2 * arc_size - 1);
/// let arc_set_1 = ArcSet::new(arc_size, vec![arc_1])?;
///
/// let arc_2 = DhtArc::Arc(arc_size, 4 * arc_size - 1);
/// let arc_set_2 = ArcSet::new(arc_size, vec![arc_2])?;
///
/// assert_eq!(1, arc_set_1.intersection(&arc_set_2).covered_sector_count());
/// # Ok(())
/// # }
/// ```
pub fn intersection(&self, other: &Self) -> Self {
ArcSet {
inner: self.inner.intersection(&other.inner).copied().collect(),
}
}

pub(crate) fn includes_sector_id(&self, value: u32) -> bool {
self.inner.contains(&value)
/// The number of sectors covered by this arc set.
///
/// # Example
///
/// ```rust
/// use kitsune2_api::DhtArc;
/// use kitsune2_dht::ArcSet;
///
/// # fn main() -> kitsune2_api::K2Result<()> {
/// let arc_size = 1 << 23;
/// let arc_1 = DhtArc::Arc(0, 2 * arc_size - 1);
/// let arc_2 = DhtArc::Arc(2 * arc_size, 4 * arc_size - 1);
/// let arc_set = ArcSet::new(arc_size, vec![arc_1, arc_2])?;
///
/// assert_eq!(4, arc_set.covered_sector_count());
/// # Ok(())
/// # }
/// ```
pub fn covered_sector_count(&self) -> usize {
self.inner.len()
}

pub(crate) fn covered_sector_count(&self) -> usize {
self.inner.len()
pub(crate) fn includes_sector_id(&self, value: u32) -> bool {
self.inner.contains(&value)
}
}

Expand Down
24 changes: 23 additions & 1 deletion crates/dht/src/dht.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use crate::arc_set::ArcSet;
use crate::PartitionedHashes;
use kitsune2_api::{DynOpStore, K2Result, OpId, StoredOp, Timestamp};
use kitsune2_api::{DynOpStore, K2Error, K2Result, OpId, StoredOp, Timestamp};
use snapshot::{DhtSnapshot, SnapshotDiff};

pub mod snapshot;
Expand Down Expand Up @@ -113,11 +113,22 @@ impl Dht {
/// This is the entry point for comparing state with another DHT model. A minimal snapshot may
/// be enough to check that two DHTs are in sync. The receiver should call [Dht::handle_snapshot]
/// which will determine if the two DHTs are in sync or if a more detailed snapshot is required.
///
/// # Errors
///
/// Returns an error if there are no arcs to snapshot. If there is no overlap between the arc
/// sets of two DHT models then there is no point in comparing them because it will always
/// yield an empty diff. The [ArcSet::covered_sector_count] should be checked before calling
/// this method.
pub async fn snapshot_minimal(
&self,
arc_set: &ArcSet,
store: DynOpStore,
) -> K2Result<DhtSnapshot> {
if arc_set.covered_sector_count() == 0 {
return Err(K2Error::other("No arcs to snapshot"));
}

let (disc_top_hash, disc_boundary) =
self.partition.disc_top_hash(arc_set, store).await?;

Expand Down Expand Up @@ -166,13 +177,24 @@ impl Dht {
///
/// The `arc_set` parameter is used to determine which arcs are relevant to the DHT model. This
/// should be the [ArcSet::intersection] of the arc sets of the two DHT models to be compared.
///
/// # Errors
///
/// Returns an error if there are no arcs to snapshot. If there is no overlap between the arc
/// sets of two DHT models then there is no point in comparing them because it will always
/// yield an empty diff. The [ArcSet::covered_sector_count] should be checked before calling
/// this method.
pub async fn handle_snapshot(
&self,
their_snapshot: &DhtSnapshot,
our_previous_snapshot: Option<DhtSnapshot>,
arc_set: &ArcSet,
store: DynOpStore,
) -> K2Result<DhtSnapshotNextAction> {
if arc_set.covered_sector_count() == 0 {
return Err(K2Error::other("No arcs to snapshot"));
}

let is_final = matches!(
our_previous_snapshot,
Some(
Expand Down
46 changes: 46 additions & 0 deletions crates/dht/src/dht/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,52 @@ async fn take_minimal_snapshot() {
}
}

#[tokio::test]
async fn cannot_take_minimal_snapshot_with_empty_arc_set() {
let current_time = Timestamp::now();
let dht1 = DhtSyncHarness::new(current_time, DhtArc::Empty).await;

let err = dht1
.dht
.snapshot_minimal(
&ArcSet::new(SECTOR_SIZE, vec![dht1.arc]).unwrap(),
dht1.store.clone(),
)
.await
.unwrap_err();
assert_eq!("No arcs to snapshot (src: None)", err.to_string());
}

#[tokio::test]
async fn cannot_handle_snapshot_with_empty_arc_set() {
let current_time = Timestamp::now();
let dht1 = DhtSyncHarness::new(current_time, DhtArc::Empty).await;

// Declare a full arc to get a snapshot
let snapshot = dht1
.dht
.snapshot_minimal(
&ArcSet::new(SECTOR_SIZE, vec![DhtArc::FULL]).unwrap(),
dht1.store.clone(),
)
.await
.unwrap();

// Now try to compare that snapshot to ourselves with an empty arc set
let err = dht1
.dht
.handle_snapshot(
&snapshot,
None,
&ArcSet::new(SECTOR_SIZE, vec![DhtArc::Empty]).unwrap(),
dht1.store.clone(),
)
.await
.unwrap_err();

assert_eq!("No arcs to snapshot (src: None)", err.to_string());
}

#[tokio::test]
async fn empty_dht_is_in_sync_with_empty() {
let current_time = Timestamp::now();
Expand Down
4 changes: 2 additions & 2 deletions crates/dht/src/dht/tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ use std::sync::Arc;
/// Intended to represent a single agent in a network, which knows how to sync with
/// some other agent.
pub(crate) struct DhtSyncHarness {
store: Arc<Kitsune2MemoryOpStore>,
pub(crate) store: Arc<Kitsune2MemoryOpStore>,
pub(crate) dht: Dht,
arc: DhtArc,
pub(crate) arc: DhtArc,
pub(crate) agent_id: AgentId,
pub(crate) discovered_ops: HashMap<AgentId, Vec<OpId>>,
}
Expand Down

0 comments on commit f83dd17

Please sign in to comment.