Skip to content

Commit 07d71c6

Browse files
committed
change: RaftStore::delete_logs_from() use range instead of (start, end)
1 parent a29b0e5 commit 07d71c6

File tree

4 files changed

+52
-39
lines changed

4 files changed

+52
-39
lines changed

async-raft/src/core/append_entries.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ impl<D: AppData, R: AppDataResponse, N: RaftNetwork<D>, S: RaftStorage<D, R>> Ra
118118
// with an index greater than this, then we must delete them per §5.3.
119119
if self.last_log_id.index > target_entry.log_id.index {
120120
self.storage
121-
.delete_logs_from(target_entry.log_id.index + 1, None)
121+
.delete_logs_from(target_entry.log_id.index + 1..)
122122
.await
123123
.map_err(|err| self.map_fatal_storage_error(err))?;
124124
let membership =

async-raft/src/storage.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! The Raft storage interface and data types.
22
33
use std::error::Error;
4+
use std::fmt::Debug;
5+
use std::ops::RangeBounds;
46

57
use anyhow::Result;
68
use async_trait::async_trait;
@@ -159,11 +161,13 @@ where
159161
/// Errors returned from this method will cause Raft to go into shutdown.
160162
async fn get_log_entries(&self, start: u64, stop: u64) -> Result<Vec<Entry<D>>>;
161163

162-
/// Delete all logs starting from `start` and stopping at `stop`, else continuing to the end
163-
/// of the log if `stop` is `None`.
164+
/// Delete all logs in a `range`.
164165
///
165166
/// Errors returned from this method will cause Raft to go into shutdown.
166-
async fn delete_logs_from(&self, start: u64, stop: Option<u64>) -> Result<()>;
167+
async fn delete_logs_from<RNG: RangeBounds<u64> + Clone + Debug + Send + Sync + Iterator>(
168+
&self,
169+
range: RNG,
170+
) -> Result<()>;
167171

168172
/// Append a payload of entries to the log.
169173
///

memstore/src/lib.rs

+27-15
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ mod test;
66
use std::cmp::max;
77
use std::collections::BTreeMap;
88
use std::collections::HashMap;
9+
use std::fmt::Debug;
910
use std::io::Cursor;
11+
use std::ops::RangeBounds;
1012
use std::sync::Arc;
1113
use std::sync::Mutex;
1214

@@ -305,6 +307,20 @@ impl MemStore {
305307
Ok(())
306308
}
307309

310+
pub async fn defensive_nonempty_range<RT, RNG: RangeBounds<RT> + Clone + Debug + Send + Iterator>(
311+
&self,
312+
range: RNG,
313+
) -> anyhow::Result<()> {
314+
if !*self.defensive.read().await {
315+
return Ok(());
316+
}
317+
for _ in range.clone() {
318+
return Ok(());
319+
}
320+
321+
Err(anyhow::anyhow!("range must be nonempty: {:?}", range))
322+
}
323+
308324
pub async fn defensive_apply_log_id_gt_last<D: AppData>(&self, entries: &[&Entry<D>]) -> anyhow::Result<()> {
309325
if !*self.defensive.read().await {
310326
return Ok(());
@@ -471,24 +487,20 @@ impl RaftStorage<ClientRequest, ClientResponse> for MemStore {
471487
Ok(log.range(start..stop).map(|(_, val)| val.clone()).collect())
472488
}
473489

474-
#[tracing::instrument(level = "trace", skip(self))]
475-
async fn delete_logs_from(&self, start: u64, stop: Option<u64>) -> Result<()> {
476-
// TODO(xp): never delete the last applied log
477-
if stop.as_ref().map(|stop| &start > stop).unwrap_or(false) {
478-
tracing::error!("delete_logs_from: invalid request, start({}) > stop({:?})", start, stop);
479-
return Ok(());
480-
}
490+
#[tracing::instrument(level = "trace", skip(self, range), fields(range=?range))]
491+
async fn delete_logs_from<R: RangeBounds<u64> + Clone + Debug + Send + Sync + Iterator>(
492+
&self,
493+
range: R,
494+
) -> Result<()> {
495+
self.defensive_nonempty_range(range.clone()).await?;
496+
481497
let mut log = self.log.write().await;
482498

483-
// If a stop point was specified, delete from start until the given stop point.
484-
if let Some(stop) = stop.as_ref() {
485-
for key in start..*stop {
486-
log.remove(&key);
487-
}
488-
return Ok(());
499+
let keys = log.range(range).map(|(k, _v)| *k).collect::<Vec<_>>();
500+
for key in keys {
501+
log.remove(&key);
489502
}
490-
// Else, just split off the remainder.
491-
log.split_off(&start);
503+
492504
Ok(())
493505
}
494506

memstore/src/test.rs

+17-20
Original file line numberDiff line numberDiff line change
@@ -507,23 +507,12 @@ where
507507
}
508508

509509
pub async fn delete_logs_from(builder: &B) -> Result<()> {
510-
tracing::info!("--- delete start > stop");
511-
{
512-
let store = builder.new_store(NODE_ID).await;
513-
Self::feed_10_logs_vote_self(&store).await?;
514-
515-
store.delete_logs_from(10, Some(1)).await?;
516-
517-
let logs = store.get_log_entries(1, 11).await?;
518-
assert_eq!(logs.len(), 10, "expected all (10) logs to be preserved");
519-
}
520-
521510
tracing::info!("--- delete start == stop");
522511
{
523512
let store = builder.new_store(NODE_ID).await;
524513
Self::feed_10_logs_vote_self(&store).await?;
525514

526-
store.delete_logs_from(1, Some(1)).await?;
515+
store.delete_logs_from(1..1).await?;
527516

528517
let logs = store.get_log_entries(1, 11).await?;
529518
assert_eq!(logs.len(), 10, "expected all (10) logs to be preserved");
@@ -534,7 +523,7 @@ where
534523
let store = builder.new_store(NODE_ID).await;
535524
Self::feed_10_logs_vote_self(&store).await?;
536525

537-
store.delete_logs_from(1, Some(4)).await?;
526+
store.delete_logs_from(1..4).await?;
538527

539528
let logs = store.get_log_entries(0, 100).await?;
540529
assert_eq!(logs.len(), 7);
@@ -546,7 +535,7 @@ where
546535
let store = builder.new_store(NODE_ID).await;
547536
Self::feed_10_logs_vote_self(&store).await?;
548537

549-
store.delete_logs_from(1, Some(1000)).await?;
538+
store.delete_logs_from(1..1000).await?;
550539
let logs = store.get_log_entries(0, 100).await?;
551540

552541
assert_eq!(logs.len(), 0);
@@ -557,7 +546,7 @@ where
557546
let store = builder.new_store(NODE_ID).await;
558547
Self::feed_10_logs_vote_self(&store).await?;
559548

560-
store.delete_logs_from(1, None).await?;
549+
store.delete_logs_from(1..).await?;
561550
let logs = store.get_log_entries(0, 100).await?;
562551

563552
assert_eq!(logs.len(), 0);
@@ -734,7 +723,7 @@ where
734723
run_fut(Suite::df_get_membership_config_dirty_log(builder))?;
735724
run_fut(Suite::df_get_initial_state_dirty_log(builder))?;
736725
run_fut(Suite::df_save_hard_state_ascending(builder))?;
737-
run_fut(Suite::df_delete_logs_from(builder))?;
726+
run_fut(Suite::df_delete_logs_from_nonempty_range(builder))?;
738727
run_fut(Suite::df_append_to_log_nonempty_input(builder))?;
739728
run_fut(Suite::df_append_to_log_nonconsecutive_input(builder))?;
740729
run_fut(Suite::df_append_to_log_eq_last_plus_one(builder))?;
@@ -930,8 +919,16 @@ where
930919
Ok(())
931920
}
932921

933-
pub async fn df_delete_logs_from(_builder: &B) -> Result<()> {
934-
// TODO(xp): what should we test about this?
922+
pub async fn df_delete_logs_from_nonempty_range(builder: &B) -> Result<()> {
923+
let store = builder.new_store(NODE_ID).await;
924+
Self::feed_10_logs_vote_self(&store).await?;
925+
926+
let res = store.delete_logs_from(10..1).await;
927+
assert!(res.is_err());
928+
929+
let res = store.delete_logs_from(10..10).await;
930+
assert!(res.is_err());
931+
935932
Ok(())
936933
}
937934

@@ -1039,7 +1036,7 @@ where
10391036
])
10401037
.await?;
10411038

1042-
store.delete_logs_from(1, Some(2)).await?;
1039+
store.delete_logs_from(1..2).await?;
10431040

10441041
let res = store
10451042
.append_to_log(&[&Entry {
@@ -1115,7 +1112,7 @@ where
11151112
])
11161113
.await?;
11171114

1118-
store.delete_logs_from(1, Some(2)).await?;
1115+
store.delete_logs_from(1..2).await?;
11191116

11201117
let res = store
11211118
.append_to_log(&[&Entry {

0 commit comments

Comments
 (0)