Skip to main content

Envelope

parse_envelope and encode_envelope read and write the Confluent wire format: a zero byte, the schema id, the datum.

The schema id takes four bytes, big-endian. parse_envelope splits a payload into the id and a view of the datum, with no copy. It returns None for a payload that does not open with the zero byte or is shorter than five bytes, which reads as "not framed". encode_envelope writes the five bytes and the datum into a new buffer. Neither function reaches a registry.

A handler that works on the framed bytes takes them through the byte lanes: a #[derive(Deserialized)] view of the delivery in, a #[derive(Serialized)] buffer out. No codec runs on either side, so the bytes travel as they are. The example moves records between two registries and rewrites the id each record names.

Example​

/// A record as it arrived: a view of its bytes, envelope included.
#[derive(Deserialized)]
struct Framed<'a>(&'a [u8]);

/// The same datum under the id the new registry gave its schema.
#[derive(Serialized, Outgoing)]
#[outgoing(name = "orders-migrated")]
struct Reframed(Vec<u8>);

/// Each schema id of the old registry, and the id the new registry gave the same schema.
const MIGRATED_IDS: &[(u32, u32)] = &[(7, 107), (8, 108)];

#[subscriber("orders", reply)]
async fn reframe(record: &Framed<'_>) -> Result<Reframed, HandlerOutcome> {
let Some((old, datum)) = parse_envelope(record.0) else {
tracing::warn!(topic = "orders", "a record without the Confluent envelope");
return Err(HandlerOutcome::drop());
};
let Some(&(_, new)) = MIGRATED_IDS.iter().find(|(from, _)| *from == old) else {
tracing::warn!(
topic = "orders",
schema_id = old,
"a schema id with no migration"
);
return Err(HandlerOutcome::drop());
};
tracing::info!(old, new, "record reframed");
Ok(Reframed(encode_envelope(new, datum)))
}

#[ruststream::app]
fn app() -> impl App {
let broker = KafkaBroker::new(["localhost:9092"]).default_group("registry-migration");
RustStream::new(AppInfo::new("registry-migration", "0.1.0")).with_broker(broker, |b| {
b.include(reframe);
})
}