Skip to main content

FrameCodec

Trait FrameCodec 

Source
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 Err stops the pipeline whatever its ErrorClass. 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§

Source

fn encode_frame(&mut self, chunk: &[u8]) -> Result<&[u8], SinkError>

Code one finished chunk, returning the frame to ship.

§Errors

Any error stops the pipeline; see the trait’s contract.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§