pub trait FrameCodec: Send + Sync {
// Required method
fn encode_frame(&mut self, chunk: &[u8]) -> Result<&[u8], SinkError>;
}Expand description
Turns each sealed chunk into its wire form, on the pipeline thread that sealed it. Compression is the usual case.
The terminal stage calls encode_frame once per
sealed chunk, after the RowEncoder has finished it and before the
chunk is queued. The output becomes the chunk’s frame, is stored, and is
never recomputed, so every write attempt of a batch, including retries,
sends the same bytes. The in-flight budget and
BatchConfig::max_bytes count the chunk’s length before the codec, so
neither depends on what a codec does.
The contract:
- The output is one unit that decodes on its own, and units concatenated in seal order form a legal stream for the sink. Compressed database blocks and the members of a multi-frame zstd or gzip object body both fit.
- The output is a deterministic function of the input.
- A codec must not perform I/O.
- An instance may keep state between calls, such as a compression context or a scratch buffer. The framework makes one instance per sink per chain, which is one per pipeline thread, and calls it from that thread only.
- The terminal stage copies the returned slice before the next call.
- An
Errstops the pipeline whatever itsErrorClass. Nothing ships for the chunk; its rows and acknowledgments stay in the shard buffer, and they fail on teardown if no later call succeeds.
The Sync bound keeps SinkHandoff, the terminal stage that holds the
codec, Sync.
§Examples
A codec that writes each chunk’s length before it:
use spate_core::error::SinkError;
use spate_core::sink::{FrameCodec, FrameCodecFactory};
#[derive(Default)]
struct LengthPrefix {
out: Vec<u8>,
}
impl FrameCodec for LengthPrefix {
fn encode_frame(&mut self, chunk: &[u8]) -> Result<&[u8], SinkError> {
self.out.clear();
let len = u32::try_from(chunk.len()).map_err(|_| SinkError::Client {
class: spate_core::ErrorClass::Fatal,
reason: "chunk longer than u32::MAX".into(),
})?;
self.out.extend_from_slice(&len.to_le_bytes());
self.out.extend_from_slice(chunk);
Ok(&self.out)
}
}
let factory = FrameCodecFactory::new(|| Box::new(LengthPrefix::default()));
let mut codec = factory.make();
assert_eq!(codec.encode_frame(b"ab").unwrap(), b"\x02\x00\x00\x00ab");Required Methods§
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".