Skip to main content

krabka_remote_storage_topic/
serde.rs

1//! Binary wire format for `__remote_log_metadata` events.
2//!
3//! This module encodes records through
4//! [`krabka_protocol::RemoteLogMetadataRecord`], which produces bytes that are
5//! byte-identical to the JVM `RemoteLogMetadataSerde`. That is the
6//! `AbstractApiMessageSerde` envelope, with all three header fields written as
7//! unsigned varints: `frameVersion(uvarint)=1 | apiKey(uvarint) |
8//! apiVersion(uvarint) | flexible-message-body`.
9//!
10//! apiKey mapping:
11//! - 0 = `RemoteLogSegmentMetadataRecord`  → [`MetadataEvent::AddSegment`]
12//! - 1 = `RemoteLogSegmentMetadataUpdateRecord` → [`MetadataEvent::UpdateSegment`]
13//! - 2 = `RemotePartitionDeleteMetadataRecord` → [`MetadataEvent::PartitionDelete`]
14//!
15//! One submodule per record, plus the scalar conversions they share:
16//! `segment_metadata` for apiKey 0, `segment_update` for apiKey 1,
17//! `partition_delete` for apiKey 2, and `primitives` for the UUID, state-byte
18//! and custom-metadata conversions all three use. The unrelated varint and
19//! cursor helpers that `snapshot.rs` frames its own envelope with live in
20//! `reader`.
21
22use {
23    crate::error::CodecError,
24    bytes::Bytes,
25    krabka_protocol::RemoteLogMetadataRecord,
26    krabka_remote_storage::{
27        RemoteLogSegmentMetadata, RemoteLogSegmentMetadataUpdate, RemotePartitionDeleteMetadata,
28    },
29};
30
31mod partition_delete;
32mod primitives;
33mod reader;
34mod segment_metadata;
35mod segment_update;
36#[cfg(test)]
37mod tests;
38
39pub(crate) use self::reader::{Reader, read_uvarint, write_uvarint};
40use self::{
41    partition_delete::{from_proto_partition_delete, to_proto_partition_delete},
42    segment_metadata::{from_proto_add, to_proto_add},
43    segment_update::{from_proto_update, to_proto_update},
44};
45
46/// One of the three event variants the topic carries.
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub enum MetadataEvent {
49    /// A new segment starts to copy (`CopySegmentStarted`).
50    AddSegment(RemoteLogSegmentMetadata),
51    /// Lifecycle transition for an existing segment.
52    UpdateSegment(RemoteLogSegmentMetadataUpdate),
53    /// Partition-delete lifecycle.
54    PartitionDelete(RemotePartitionDeleteMetadata),
55}
56
57impl MetadataEvent {
58    /// Encode this event into freshly-allocated [`Bytes`] with the JVM
59    /// `RemoteLogMetadataSerde` wire format.
60    ///
61    /// # Errors
62    ///
63    /// Returns [`CodecError::Protocol`] if the generated codec rejects the
64    /// record. That cannot happen for version 0, which every record uses.
65    pub fn encode(&self) -> Result<Bytes, CodecError> {
66        let record = match self {
67            Self::AddSegment(md) => RemoteLogMetadataRecord::SegmentMetadata(to_proto_add(md)),
68            Self::UpdateSegment(u) => {
69                RemoteLogMetadataRecord::SegmentMetadataUpdate(to_proto_update(u))
70            }
71            Self::PartitionDelete(d) => {
72                RemoteLogMetadataRecord::PartitionDelete(to_proto_partition_delete(d))
73            }
74        };
75        record
76            .encode_value()
77            .map_err(|error| CodecError::Protocol(error.to_string()))
78    }
79
80    /// Decode an event from `bytes`.
81    ///
82    /// # Errors
83    ///
84    /// Returns [`CodecError`] for any malformed input: a truncated envelope,
85    /// an unknown or unsupported apiKey, an out-of-range state byte, or a
86    /// domain invariant violation that [`RemoteLogSegmentMetadata::new`]
87    /// reports. apiKey 3, `SegmentMetadataSnapshot`, never appears on the
88    /// topic.
89    pub fn decode(bytes: &[u8]) -> Result<Self, CodecError> {
90        let record = RemoteLogMetadataRecord::decode_value(bytes)
91            .map_err(|e| CodecError::Protocol(e.to_string()))?;
92        match record {
93            RemoteLogMetadataRecord::SegmentMetadata(r) => Ok(Self::AddSegment(from_proto_add(r)?)),
94            RemoteLogMetadataRecord::SegmentMetadataUpdate(r) => {
95                Ok(Self::UpdateSegment(from_proto_update(r)?))
96            }
97            RemoteLogMetadataRecord::PartitionDelete(r) => {
98                Ok(Self::PartitionDelete(from_proto_partition_delete(r)?))
99            }
100            RemoteLogMetadataRecord::SegmentMetadataSnapshot(_) => Err(CodecError::Protocol(
101                "apiKey 3 (SegmentMetadataSnapshot) must not appear on __remote_log_metadata"
102                    .into(),
103            )),
104            RemoteLogMetadataRecord::Unknown { api_key, .. } => Err(CodecError::Protocol(format!(
105                "unknown __remote_log_metadata apiKey {api_key}"
106            ))),
107        }
108    }
109}