Skip to content

Commit 7adb711

Browse files
committed
Make Batch a customizable type in RaftTypeConfig
1 parent 8d3592c commit 7adb711

File tree

19 files changed

+328
-61
lines changed

19 files changed

+328
-61
lines changed

openraft/src/base/batch/display.rs

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,46 @@
33
use std::fmt;
44
use std::fmt::Formatter;
55

6-
use super::Batch;
6+
use super::RaftBatch;
7+
use crate::OptionalSend;
78

8-
impl<T: fmt::Display> Batch<T> {
9-
/// Returns a display helper that shows all elements.
10-
pub fn display(&self) -> impl fmt::Display + '_ {
11-
BatchDisplay {
12-
elements: self,
13-
max: None,
14-
}
15-
}
9+
/// Display helper for types implementing `RaftBatch`.
10+
pub struct DisplayBatch<'a, T, B>
11+
where
12+
T: fmt::Display + OptionalSend + 'static + fmt::Debug,
13+
B: RaftBatch<T>,
14+
{
15+
pub(super) elements: &'a B,
16+
pub(super) max: Option<usize>,
17+
pub(super) _phantom: std::marker::PhantomData<T>,
18+
}
1619

17-
/// Returns a display helper that shows at most `max` elements.
18-
pub fn display_n(&self, max: usize) -> impl fmt::Display + '_ {
19-
BatchDisplay {
20-
elements: self,
21-
max: Some(max),
20+
impl<'a, T, B> DisplayBatch<'a, T, B>
21+
where
22+
T: fmt::Display + OptionalSend + 'static + fmt::Debug,
23+
B: RaftBatch<T>,
24+
{
25+
pub(super) fn new(elements: &'a B, max: Option<usize>) -> Self {
26+
Self {
27+
elements,
28+
max,
29+
_phantom: std::marker::PhantomData,
2230
}
2331
}
2432
}
2533

26-
struct BatchDisplay<'a, T> {
27-
elements: &'a Batch<T>,
28-
max: Option<usize>,
29-
}
30-
31-
impl<'a, T: fmt::Display> fmt::Display for BatchDisplay<'a, T> {
34+
impl<'a, T, B> fmt::Display for DisplayBatch<'a, T, B>
35+
where
36+
T: fmt::Display + OptionalSend + 'static + fmt::Debug,
37+
B: RaftBatch<T>,
38+
{
3239
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
33-
let slice = self.elements.as_slice();
34-
let max = self.max.unwrap_or(slice.len());
35-
let len = slice.len();
40+
let len = self.elements.len();
41+
let max = self.max.unwrap_or(len);
3642
let shown = max.min(len);
3743

3844
write!(f, "[")?;
39-
for (i, e) in slice.iter().take(max).enumerate() {
45+
for (i, e) in self.elements.iter().take(max).enumerate() {
4046
if i > 0 {
4147
write!(f, ", ")?;
4248
}
@@ -55,25 +61,53 @@ impl<'a, T: fmt::Display> fmt::Display for BatchDisplay<'a, T> {
5561
#[cfg(test)]
5662
mod tests {
5763
use super::*;
64+
use crate::impls::Batch;
5865

5966
#[test]
6067
fn test_display() {
61-
assert_eq!(format!("{}", Batch::Single(42).display()), "[42]");
62-
assert_eq!(format!("{}", Batch::Vec(vec![1, 2]).display()), "[1, 2]");
63-
assert_eq!(format!("{}", Batch::<i32>::Vec(vec![]).display()), "[]");
68+
assert_eq!(
69+
format!("{}", <Batch<i32> as RaftBatch<i32>>::display(&Batch::Single(42))),
70+
"[42]"
71+
);
72+
assert_eq!(
73+
format!("{}", <Batch<i32> as RaftBatch<i32>>::display(&Batch::Vec(vec![1, 2]))),
74+
"[1, 2]"
75+
);
76+
assert_eq!(
77+
format!(
78+
"{}",
79+
<Batch<i32> as RaftBatch<i32>>::display(&Batch::<i32>::Vec(vec![]))
80+
),
81+
"[]"
82+
);
6483
}
6584

6685
#[test]
6786
fn test_display_n() {
6887
let v: Batch<i32> = [1, 2, 3, 4, 5].into();
6988

70-
assert_eq!(format!("{}", v.display_n(3)), "[1, 2, 3, ... 2 more]");
71-
assert_eq!(format!("{}", v.display_n(5)), "[1, 2, 3, 4, 5]");
72-
assert_eq!(format!("{}", v.display_n(10)), "[1, 2, 3, 4, 5]");
73-
assert_eq!(format!("{}", v.display_n(0)), "[... 5 more]");
89+
assert_eq!(
90+
format!("{}", <Batch<i32> as RaftBatch<i32>>::display_n(&v, 3)),
91+
"[1, 2, 3, ... 2 more]"
92+
);
93+
assert_eq!(
94+
format!("{}", <Batch<i32> as RaftBatch<i32>>::display_n(&v, 5)),
95+
"[1, 2, 3, 4, 5]"
96+
);
97+
assert_eq!(
98+
format!("{}", <Batch<i32> as RaftBatch<i32>>::display_n(&v, 10)),
99+
"[1, 2, 3, 4, 5]"
100+
);
101+
assert_eq!(
102+
format!("{}", <Batch<i32> as RaftBatch<i32>>::display_n(&v, 0)),
103+
"[... 5 more]"
104+
);
74105

75106
let v2: Batch<i32> = 42.into();
76-
assert_eq!(format!("{}", v2.display_n(0)), "[... 1 more]");
77-
assert_eq!(format!("{}", v2.display_n(1)), "[42]");
107+
assert_eq!(
108+
format!("{}", <Batch<i32> as RaftBatch<i32>>::display_n(&v2, 0)),
109+
"[... 1 more]"
110+
);
111+
assert_eq!(format!("{}", <Batch<i32> as RaftBatch<i32>>::display_n(&v2, 1)), "[42]");
78112
}
79113
}

openraft/src/base/batch/iter.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ impl<T> Iterator for BatchIter<T> {
4444

4545
impl<T> ExactSizeIterator for BatchIter<T> {}
4646

47+
// Safety: BatchIter<T> is Send when T is Send because both Option<T> and vec::IntoIter<T> are Send
48+
unsafe impl<T: Send> Send for BatchIter<T> {}
49+
4750
#[cfg(test)]
4851
mod tests {
4952
use super::*;

openraft/src/base/batch/mod.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22
33
mod display;
44
mod iter;
5+
mod raft_batch;
56

67
use std::ops::Index;
78
use std::slice;
89

10+
pub use raft_batch::RaftBatch;
11+
912
/// A container that stores elements efficiently by avoiding heap allocation for single elements.
1013
///
1114
/// This type uses an enum with two variants:
1215
/// - `Single`: stores exactly one element inline (no heap allocation)
1316
/// - `Vec`: stores zero or more elements using a `Vec`
1417
#[derive(Debug, Clone, Eq)]
15-
pub(crate) enum Batch<T> {
18+
pub enum Batch<T> {
1619
/// A single element stored inline without heap allocation.
1720
Single(T),
1821
/// Multiple elements stored in a Vec.
@@ -135,6 +138,66 @@ impl<T> Batch<T> {
135138
}
136139
}
137140

141+
// Implement RaftBatch trait for Batch
142+
impl<T> RaftBatch<T> for Batch<T>
143+
where T: crate::OptionalSend + 'static + std::fmt::Debug
144+
{
145+
type Iter<'a>
146+
= slice::Iter<'a, T>
147+
where T: 'a;
148+
type IterMut<'a>
149+
= slice::IterMut<'a, T>
150+
where T: 'a;
151+
type IntoIter = iter::BatchIter<T>;
152+
153+
fn from_item(item: T) -> Self {
154+
Batch::Single(item)
155+
}
156+
157+
fn from_vec(vec: Vec<T>) -> Self {
158+
Batch::from(vec)
159+
}
160+
161+
fn from_exact_iter<I>(iter: I) -> Self
162+
where I: ExactSizeIterator<Item = T> {
163+
match iter.len() {
164+
0 => Batch::Vec(Vec::new()),
165+
1 => Batch::Single(iter.into_iter().next().unwrap()),
166+
_ => Batch::Vec(iter.collect()),
167+
}
168+
}
169+
170+
fn len(&self) -> usize {
171+
Batch::len(self)
172+
}
173+
174+
fn first(&self) -> Option<&T> {
175+
Batch::first(self)
176+
}
177+
178+
fn last(&self) -> Option<&T> {
179+
Batch::last(self)
180+
}
181+
182+
fn iter(&self) -> Self::Iter<'_> {
183+
self.as_slice().iter()
184+
}
185+
186+
fn iter_mut(&mut self) -> Self::IterMut<'_> {
187+
self.as_mut_slice().iter_mut()
188+
}
189+
190+
fn into_iter(self) -> Self::IntoIter {
191+
// Use the existing IntoIterator impl
192+
IntoIterator::into_iter(self)
193+
}
194+
195+
fn extend(&mut self, other: Self) {
196+
Batch::extend(self, other)
197+
}
198+
}
199+
200+
// Index
138201
impl<T> Index<usize> for Batch<T> {
139202
type Output = T;
140203

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//! Trait definition for customizable batch containers in Raft.
2+
3+
use std::fmt::Debug;
4+
use std::fmt::Display;
5+
6+
use super::display::DisplayBatch;
7+
use crate::OptionalSend;
8+
9+
/// Trait for batch containers used throughout Raft for efficient element grouping.
10+
///
11+
/// This trait abstracts over different batch container implementations while
12+
/// preserving performance characteristics required by Raft.
13+
///
14+
/// Implementations may optimize for:
15+
/// - Avoiding heap allocation for small batches
16+
/// - Arena or pool-backed allocation
17+
/// - Cache locality
18+
///
19+
/// # Design Notes
20+
///
21+
/// - The trait is `Sized` to enable static dispatch and full monomorphization.
22+
/// - Iteration is explicit via `iter()` / `iter_mut()` to preserve lifetime and `ExactSizeIterator`
23+
/// guarantees.
24+
/// - Consuming iteration via `into_iter()` method with explicit `IntoIter` associated type.
25+
pub trait RaftBatch<T>: OptionalSend + Sized + Default + Debug + 'static
26+
where T: OptionalSend + Debug + 'static
27+
{
28+
/// Iterator type for immutable element references.
29+
///
30+
/// Must implement `ExactSizeIterator` to allow efficient size queries.
31+
type Iter<'a>: Iterator<Item = &'a T> + ExactSizeIterator
32+
where
33+
T: 'a,
34+
Self: 'a;
35+
36+
/// Iterator type for mutable element references.
37+
///
38+
/// Must implement `ExactSizeIterator` to allow efficient size queries.
39+
type IterMut<'a>: Iterator<Item = &'a mut T> + ExactSizeIterator
40+
where
41+
T: 'a,
42+
Self: 'a;
43+
44+
/// Iterator type for consuming iteration.
45+
///
46+
/// Must implement `ExactSizeIterator` to allow efficient size queries.
47+
type IntoIter: Iterator<Item = T> + ExactSizeIterator + OptionalSend;
48+
49+
/// Creates a batch containing a single item.
50+
///
51+
/// Implementations may optimize this case to avoid heap allocation.
52+
fn from_item(item: T) -> Self;
53+
54+
/// Creates a batch from a `Vec`.
55+
///
56+
/// Implementations may optimize for the single-element case.
57+
fn from_vec(vec: Vec<T>) -> Self;
58+
59+
/// Creates a batch from an exact-size iterator.
60+
///
61+
/// The exact size allows implementations to pre-allocate or select
62+
/// efficient internal representations.
63+
fn from_exact_iter<I>(iter: I) -> Self
64+
where I: ExactSizeIterator<Item = T>;
65+
66+
/// Returns the number of elements in the batch.
67+
fn len(&self) -> usize;
68+
69+
/// Returns `true` if the batch contains no elements.
70+
fn is_empty(&self) -> bool {
71+
self.len() == 0
72+
}
73+
74+
/// Returns a reference to the first element, or `None` if empty.
75+
fn first(&self) -> Option<&T>;
76+
77+
/// Returns a reference to the last element, or `None` if empty.
78+
fn last(&self) -> Option<&T>;
79+
80+
/// Returns an iterator over immutable element references.
81+
fn iter(&self) -> Self::Iter<'_>;
82+
83+
/// Returns an iterator over mutable element references.
84+
fn iter_mut(&mut self) -> Self::IterMut<'_>;
85+
86+
/// Consumes the batch and returns an iterator over the elements.
87+
fn into_iter(self) -> Self::IntoIter;
88+
89+
/// Appends all elements from another batch to this batch.
90+
///
91+
/// This method consumes `other` to allow efficient transfer of ownership.
92+
fn extend(&mut self, other: Self);
93+
94+
/// Returns a display helper that formats all elements.
95+
fn display(&self) -> DisplayBatch<'_, T, Self>
96+
where T: Display {
97+
DisplayBatch::new(self, None)
98+
}
99+
100+
/// Returns a display helper that formats at most `max` elements.
101+
fn display_n(&self, max: usize) -> DisplayBatch<'_, T, Self>
102+
where T: Display {
103+
DisplayBatch::new(self, Some(max))
104+
}
105+
}

openraft/src/base/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub(crate) mod finalized;
3232
pub(crate) mod histogram;
3333
pub(crate) mod shared_id_generator;
3434

35-
pub(crate) use batch::Batch;
35+
pub use batch::RaftBatch;
3636
pub use openraft_rt::BoxAny;
3737
pub use openraft_rt::BoxAsyncOnceMut;
3838
pub use openraft_rt::BoxFuture;

openraft/src/core/merged_raft_msg_receiver.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use crate::RaftTypeConfig;
99
use crate::async_runtime::MpscReceiver;
1010
use crate::async_runtime::TryRecvError;
11+
use crate::base::RaftBatch;
1112
use crate::core::raft_msg::RaftMsg;
1213
use crate::error::Fatal;
1314
use crate::type_config::alias::MpscReceiverOf;
@@ -196,7 +197,6 @@ where C: RaftTypeConfig
196197
mod tests {
197198
use super::*;
198199
use crate::async_runtime::MpscSender;
199-
use crate::base::Batch;
200200
use crate::engine::testing::UTConfig;
201201
use crate::engine::testing::log_id;
202202
use crate::entry::EntryPayload;
@@ -211,15 +211,14 @@ mod tests {
211211

212212
fn client_write(data: u64, leader: Option<CommittedLeaderIdOf<C>>) -> RaftMsg<C> {
213213
RaftMsg::ClientWrite {
214-
payloads: Batch::Single(EntryPayload::Normal(data)),
215-
responders: Batch::Single(None),
214+
payloads: C::Batch::from_item(EntryPayload::Normal(data)),
215+
responders: C::Batch::from_item(None),
216216
expected_leader: leader,
217217
}
218218
}
219219

220-
fn extract_payload_data(payloads: &Batch<EntryPayload<C>>) -> Vec<u64> {
220+
fn extract_payload_data(payloads: &C::Batch<EntryPayload<C>>) -> Vec<u64> {
221221
payloads
222-
.as_slice()
223222
.iter()
224223
.map(|p| match p {
225224
EntryPayload::Normal(d) => *d,

0 commit comments

Comments
 (0)