Skip to main content

Crate tsoracle_codec

Crate tsoracle_codec 

Source
Expand description

§tsoracle-codec

The one version-prefixed postcard codec shared across tsoracle — every persisted blob is framed as [version_byte | postcard(value)] so an on-disk layout change fails loudly instead of misdecoding.

§Why it exists

Both consensus toolkits (tsoracle-paxos-toolkit, tsoracle-openraft-toolkit) carried a near-identical encode/decode/CodecError copy of this framing. Every persisted struct — a Ballot, a HighWaterCommand log entry, a snapshot, a Raft log record — is stamped with a leading schema-version byte; a node upgraded mid-flight that reads prior-version bytes hits a structured version mismatch rather than silently parsing them against a new struct layout. This crate is the single home for that framing.

§What’s in the box

  • encode(version, value) — serializes value with postcard and prepends version, yielding [version | postcard(value)].
  • decode(expected_version, bytes) — checks the leading byte against expected_version (returning CodecError::Version on mismatch) before deserializing the body, and rejects any bytes left unconsumed past a valid body with CodecError::TrailingBytes rather than silently discarding them.
  • CodecErrorEmpty, Version { expected, actual }, Encode(postcard::Error), Decode(postcard::Error), TrailingBytes { extra }. Encode and decode failures are kept distinct, and the underlying postcard::Error is carried as the error source (no From conversion) so a stray ? never silently becomes a CodecError. TrailingBytes flags surplus bytes after an otherwise-valid body — for a format that exists to catch drift, garbage appended by a partial overwrite is a corruption signal, not noise to drop.
  • codec_io_error(context, err) — maps a CodecError to std::io::Error for consumers whose trait surface speaks io::Error (the openraft RaftLogStorage / RaftStateMachine impls). A Version mismatch becomes ErrorKind::InvalidData so a foreign-schema record is distinguishable from a generic decode failure; context is prefixed onto the message so the failing boundary is identifiable. Shared so the mismatch-to-InvalidData contract can’t drift between those impls.

The schema-version number is deliberately not owned here. version is a parameter, so each consumer keeps its own SCHEMA_VERSION constant and evolves its on-disk format independently of the others.

§Quick reference

# consumer Cargo.toml
[dependencies]
tsoracle-codec = { workspace = true }
// Each consumer owns its own schema version.
pub const SCHEMA_VERSION: u8 = 1;

let framed = tsoracle_codec::encode(SCHEMA_VERSION, &value)?;
let value: MyType = tsoracle_codec::decode(SCHEMA_VERSION, &framed)?;

Bump the consumer’s SCHEMA_VERSION when a persisted struct’s postcard layout changes in a backward-incompatible way (field reorder/insert/remove); a stale reader then fails with CodecError::Version instead of misdecoding.

  • postcard — the compact serde wire format this frames.
  • tsoracle-failpoint, tsoracle-yieldpoint — the other shared micro-crates that exist so a single behavior is not duplicated across the workspace.

Enums§

CodecError
Failure modes of the version-prefixed postcard codec.

Traits§

VersionedCodec
A type whose persisted and on-wire representation is versioned.

Functions§

codec_io_error
Map a CodecError to an io::Error, tagging the message with context so the failing boundary is identifiable.
decode
Decode a payload produced by encode, rejecting a version mismatch.
decode_framed
Decode a [version | body] payload, rejecting a version outside the reader’s supported range [min_version, max_version] with CodecError::VersionUnsupported before any body parse. An in-range version is dispatched to VersionedCodec::decode_version, up-converting older layouts into the current type.
decode_postcard_exact
Deserialize a bare postcard body, rejecting surplus trailing bytes. A versioned format that exists to catch drift treats trailing garbage as corruption (e.g. a partial overwrite), not a clean record. Used by VersionedCodec::decode_version implementations per version.
encode
Encode value as [version | postcard(value)].
encode_framed
Encode value as [version | body], where body is value’s layout at version (see VersionedCodec::encode_version).
encode_postcard
Serialize value to a bare postcard body (no version prefix). Used by VersionedCodec::encode_version implementations per version.