Skip to content

Commit 82869fb

Browse files
author
Julien Loudet
committed
feat(storage-manager): add assertion on Replication Log in Debug
This commit introduces an assertion test every time an Event in inserted in the Replication Log ONLY IN DEBUG. The purpose is to make sure that the invariant of the Replication Log is upheld: it should only contain one Event per key expression. * plugins/zenoh-plugin-storage-manager/src/replication/classification.rs: added the method `assert_only_one_event_per_key_expr` to the `Interval` and `SubInterval` structures. * plugins/zenoh-plugin-storage-manager/src/replication/log.rs: added the method `assert_only_one_event_per_key_expr` to the `LogLatest` structure. Signed-off-by: Julien Loudet <julien.loudet@zettascale.tech>
1 parent bda500b commit 82869fb

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

plugins/zenoh-plugin-storage-manager/src/replication/classification.rs

+45-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//
1414

1515
use std::{
16-
collections::HashMap,
16+
collections::{HashMap, HashSet},
1717
ops::{Deref, Sub},
1818
};
1919

@@ -91,6 +91,26 @@ impl<const N: usize> From<[(SubIntervalIdx, SubInterval); N]> for Interval {
9191
}
9292

9393
impl Interval {
94+
/// Returns true if the Replication Log only contains a single Event for each key expression.
95+
///
96+
/// To perform that check a HashSet is constructed by visiting each Interval and each
97+
/// SubInterval, filling the HashSet with the key expression of all the Events contained.
98+
///
99+
/// ⚠️ This method will only be called if Zenoh is compiled in Debug mode.
100+
#[cfg(debug_assertions)]
101+
pub(crate) fn assert_only_one_event_per_key_expr(
102+
&self,
103+
events: &mut HashSet<Option<OwnedKeyExpr>>,
104+
) -> bool {
105+
for sub_interval in self.sub_intervals.values() {
106+
if !sub_interval.assert_only_one_event_per_key_expr(events) {
107+
return false;
108+
}
109+
}
110+
111+
true
112+
}
113+
94114
/// Returns the [Fingerprint] of this [Interval].
95115
///
96116
/// The [Fingerprint] of an [Interval] is equal to the XOR (exclusive or) of the fingerprints
@@ -229,6 +249,30 @@ impl<const N: usize> From<[Event; N]> for SubInterval {
229249
}
230250

231251
impl SubInterval {
252+
/// Returns true if the Replication Log only contains a single Event for each key expression.
253+
///
254+
/// To perform that check a HashSet is constructed by visiting each Interval and each
255+
/// SubInterval, filling the HashSet with the key expression of all the Events contained.
256+
///
257+
/// ⚠️ This method will only be called if Zenoh is compiled in Debug mode.
258+
#[cfg(debug_assertions)]
259+
fn assert_only_one_event_per_key_expr(
260+
&self,
261+
events: &mut HashSet<Option<OwnedKeyExpr>>,
262+
) -> bool {
263+
for event_ke in self.events.keys() {
264+
if !events.insert(event_ke.clone()) {
265+
tracing::error!(
266+
"FATAL ERROR, REPLICATION LOG INVARIANT VIOLATED, KEY APPEARS MULTIPLE TIMES: \
267+
< {event_ke:?} >"
268+
);
269+
return false;
270+
}
271+
}
272+
273+
true
274+
}
275+
232276
/// Inserts the [Event], regardless of its [Timestamp].
233277
///
234278
/// This method also updates the fingerprint of the [SubInterval].

plugins/zenoh-plugin-storage-manager/src/replication/log.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// ZettaScale Zenoh Team, <zenoh@zettascale.tech>
1313
//
1414

15-
use std::collections::{BTreeMap, HashMap};
15+
use std::collections::{BTreeMap, HashMap, HashSet};
1616

1717
use bloomfilter::Bloom;
1818
use serde::{Deserialize, Serialize};
@@ -170,6 +170,24 @@ pub struct LogLatest {
170170
}
171171

172172
impl LogLatest {
173+
/// Returns true if the Replication Log only contains a single Event for each key expression.
174+
///
175+
/// To perform that check a HashSet is constructed by visiting each Interval and each
176+
/// SubInterval, filling the HashSet with the key expression of all the Events contained.
177+
///
178+
/// ⚠️ This method will only be called if Zenoh is compiled in Debug mode.
179+
#[cfg(debug_assertions)]
180+
pub(crate) fn assert_only_one_event_per_key_expr(&self) -> bool {
181+
let mut hash_set = HashSet::new();
182+
for interval in self.intervals.values() {
183+
if !interval.assert_only_one_event_per_key_expr(&mut hash_set) {
184+
return false;
185+
}
186+
}
187+
188+
true
189+
}
190+
173191
/// Creates a new [LogLatest] configured with the provided [ReplicaConfig].
174192
pub fn new(
175193
storage_key_expr: OwnedKeyExpr,
@@ -270,6 +288,9 @@ impl LogLatest {
270288
.entry(interval_idx)
271289
.or_default()
272290
.insert_unchecked(sub_interval_idx, event);
291+
292+
#[cfg(debug_assertions)]
293+
assert!(self.assert_only_one_event_per_key_expr());
273294
}
274295

275296
/// Removes, if there is one, the previous event from the Replication Log for the provided key

0 commit comments

Comments
 (0)