Skip to main content

rpc_api/
chunk.rs

1//! The wire-level vocabulary of a chunked RPC: where a record sits in its
2//! run, and how a chunked record comes apart into key, position and part.
3//! Reassembling a run is the driver's job (`rpc_driver::ChunkRunAssembler`);
4//! this crate holds only what a protocol crate needs to describe its chunked
5//! records, so generated bindings keep their single dependency on it.
6
7/// Where one record sits in the run of records that together carry a
8/// message too large for one RPC record: its index, from 0, and the run's
9/// length. Every record of a run carries the same `count`, each index in
10/// `[0, count)` is sent exactly once, and a run always has at least one
11/// record, so an empty message is a run of one empty record. The sender may
12/// issue a run's calls concurrently and over several connections, so its
13/// records can arrive in any order, and the runs of several messages can be
14/// in flight at once; the driver's `ChunkRunAssembler` puts them back
15/// together.
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
17pub struct ChunkPosition {
18    /// The record's index in its run, from 0.
19    pub index: u32,
20    /// The number of records in the run; at least 1.
21    pub count: u32,
22}
23
24impl ChunkPosition {
25    /// The position of record `index` in a run of `count` records.
26    pub const fn new(index: u32, count: u32) -> Self {
27        Self { index, count }
28    }
29}
30
31/// A wire record that is one chunk of a run: it names the message it is
32/// part of (its key, such as a batch number), its position in the run, and
33/// carries its part of the message's items.
34///
35/// Protocol crates implement this for each chunked record they define, so a
36/// handler passes the record it was called with straight to the driver's
37/// `ChunkRunAssembler::push_record`. It lives here so a protocol crate needs
38/// no transport dependency to implement it.
39///
40/// ```
41/// use rpc_api::{ChunkPosition, ChunkRecord};
42///
43/// // The shape a chunked wire record takes: the message it belongs to, its
44/// // position in the run, and its slice of the items.
45/// struct UpdateChunk {
46///     batch: i64,
47///     chunk_index: u32,
48///     chunk_count: u32,
49///     updates: Vec<i64>,
50/// }
51///
52/// impl ChunkRecord for UpdateChunk {
53///     type Key = i64;
54///     type Part = Vec<i64>;
55///
56///     fn into_chunk(self) -> (i64, ChunkPosition, Vec<i64>) {
57///         let position = ChunkPosition::new(self.chunk_index, self.chunk_count);
58///         (self.batch, position, self.updates)
59///     }
60/// }
61///
62/// let chunk = UpdateChunk { batch: 7, chunk_index: 1, chunk_count: 2, updates: vec![3] };
63/// assert_eq!(chunk.into_chunk(), (7, ChunkPosition::new(1, 2), vec![3]));
64/// ```
65pub trait ChunkRecord {
66    /// Identifies the message the run carries.
67    type Key;
68    /// One chunk's payload.
69    type Part;
70
71    /// Takes the record apart into its key, its position and its part.
72    fn into_chunk(self) -> (Self::Key, ChunkPosition, Self::Part);
73}