Skip to content

Commit 8f09f60

Browse files
committed
Introduce ArcVec<T> to improve Copy performance of messages
Summary: The ArcVec<T> is just a thin wrapper around Arc<[T]> that then can be cheaply cloned. But at the same time can be seralized and most importantly deserialized when sent over the wire
1 parent 32c0d35 commit 8f09f60

File tree

3 files changed

+131
-3
lines changed

3 files changed

+131
-3
lines changed

crates/bifrost/src/providers/replicated_loglet/rpc_routers.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl SequencersRpc {
126126
loglet_id: loglet.params().loglet_id,
127127
segment_index: loglet.segment_index(),
128128
},
129-
payloads: Vec::from_iter(payloads.iter().cloned()),
129+
payloads: payloads.into(),
130130
};
131131

132132
let commit_token = loop {
@@ -137,7 +137,6 @@ impl SequencersRpc {
137137
.await
138138
.unwrap();
139139

140-
// todo(azmy): avoid copying all records on retry
141140
match connection
142141
.send(loglet.params().sequencer, permits, msg.clone())
143142
.await

crates/types/src/net/replicated_loglet.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::logs::metadata::SegmentIndex;
2020
use crate::logs::{LogId, LogletOffset, Record, SequenceNumber, TailState};
2121
use crate::net::define_rpc;
2222
use crate::replicated_loglet::ReplicatedLogletId;
23+
use crate::storage::ArcVec;
2324

2425
// ----- ReplicatedLoglet Sequencer API -----
2526
define_rpc! {
@@ -69,12 +70,13 @@ impl CommonResponseHeader {
6970
pub struct Append {
7071
#[serde(flatten)]
7172
pub header: CommonRequestHeader,
72-
pub payloads: Vec<Record>,
73+
pub payloads: ArcVec<Record>,
7374
}
7475

7576
impl Append {
7677
pub fn estimated_encode_size(&self) -> usize {
7778
self.payloads
79+
.as_slice()
7880
.iter()
7981
.map(|p| p.estimated_encode_size())
8082
.sum()

crates/types/src/storage.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
// the Business Source License, use of this software will be governed
99
// by the Apache License, Version 2.0.
1010

11+
use core::fmt;
12+
use std::marker::PhantomData;
1113
use std::mem;
1214
use std::sync::Arc;
1315

1416
use bytes::{Buf, BufMut, Bytes, BytesMut};
1517
use downcast_rs::{impl_downcast, DowncastSync};
1618
use serde::de::{DeserializeOwned, Error as DeserializationError};
1719
use serde::ser::Error as SerializationError;
20+
use serde::ser::SerializeSeq;
1821
use serde::{Deserialize, Serialize};
1922
use tracing::error;
2023

@@ -395,6 +398,130 @@ pub fn decode_from_flexbuffers<T: DeserializeOwned, B: Buf>(
395398
}
396399
}
397400

401+
/// [`ArcVec`] mainly used by `message` types to improve
402+
/// cloning of messages.
403+
///
404+
/// It can replace [`Vec<T>`] most of the time in all structures
405+
/// that need to be serialized over the wire.
406+
///
407+
/// Internally it keeps the data inside an [`Arc<[T]>`]
408+
#[derive(Debug)]
409+
pub struct ArcVec<T> {
410+
inner: Arc<[T]>,
411+
}
412+
413+
impl<T> serde::Serialize for ArcVec<T>
414+
where
415+
T: serde::Serialize,
416+
{
417+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
418+
where
419+
S: serde::Serializer,
420+
{
421+
let slice = self.as_slice();
422+
let mut seq = serializer.serialize_seq(Some(slice.len()))?;
423+
for elem in slice.iter() {
424+
seq.serialize_element(elem)?;
425+
}
426+
427+
seq.end()
428+
}
429+
}
430+
431+
impl<'de, T> serde::Deserialize<'de> for ArcVec<T>
432+
where
433+
T: serde::Deserialize<'de>,
434+
{
435+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
436+
where
437+
D: serde::Deserializer<'de>,
438+
{
439+
deserializer.deserialize_seq(ArcVecVisitor::default())
440+
}
441+
}
442+
443+
struct ArcVecVisitor<T> {
444+
_phantom: PhantomData<T>,
445+
}
446+
447+
impl<T> Default for ArcVecVisitor<T> {
448+
fn default() -> Self {
449+
Self {
450+
_phantom: PhantomData,
451+
}
452+
}
453+
}
454+
455+
impl<'de, T> serde::de::Visitor<'de> for ArcVecVisitor<T>
456+
where
457+
T: serde::Deserialize<'de>,
458+
{
459+
type Value = ArcVec<T>;
460+
461+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
462+
write!(formatter, "expecting an array")
463+
}
464+
465+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
466+
where
467+
A: serde::de::SeqAccess<'de>,
468+
{
469+
let mut vec: Vec<T> = Vec::with_capacity(seq.size_hint().unwrap_or_default());
470+
loop {
471+
let Some(value) = seq.next_element::<T>()? else {
472+
break;
473+
};
474+
475+
vec.push(value);
476+
}
477+
478+
Ok(vec.into())
479+
}
480+
}
481+
482+
impl<T> Clone for ArcVec<T> {
483+
fn clone(&self) -> Self {
484+
Self {
485+
inner: Arc::clone(&self.inner),
486+
}
487+
}
488+
}
489+
490+
impl<T> ArcVec<T> {
491+
pub fn as_slice(&self) -> &[T] {
492+
&self.inner
493+
}
494+
}
495+
496+
impl<T> From<ArcVec<T>> for Arc<[T]> {
497+
fn from(value: ArcVec<T>) -> Self {
498+
value.inner
499+
}
500+
}
501+
502+
impl<T> From<ArcVec<T>> for Vec<T>
503+
where
504+
T: Clone,
505+
{
506+
fn from(value: ArcVec<T>) -> Self {
507+
Vec::from_iter(value.as_slice().iter().cloned())
508+
}
509+
}
510+
511+
impl<T> From<Vec<T>> for ArcVec<T> {
512+
fn from(value: Vec<T>) -> Self {
513+
Self {
514+
inner: Arc::from_iter(value),
515+
}
516+
}
517+
}
518+
519+
impl<T> From<Arc<[T]>> for ArcVec<T> {
520+
fn from(value: Arc<[T]>) -> Self {
521+
Self { inner: value }
522+
}
523+
}
524+
398525
#[cfg(test)]
399526
mod tests {
400527
use bytes::Bytes;

0 commit comments

Comments
 (0)