Skip to main content

krabka_remote_storage/metadata/
segment_id.rs

1//! Identity types for a remote log segment.
2//!
3//! [`TopicIdPartition`] names a partition by its stable topic UUID, and
4//! [`RemoteLogSegmentId`] pairs that partition with the random per-segment
5//! UUID that makes one segment globally unique. Both are pure identity, which
6//! is why they sit apart from the metadata record that carries them.
7
8use {
9    std::hash::{Hash, Hasher},
10    uuid::Uuid,
11};
12
13/// A partition addressed by its stable topic UUID, with the topic name for
14/// diagnostics.
15///
16/// Equality and hash use `topic_id` and `partition` only. The topic name is
17/// informational, and a topic's id is its identity. This matches Kafka's
18/// `TopicIdPartition`.
19#[derive(Debug, Clone)]
20pub struct TopicIdPartition {
21    /// Stable topic UUID, as assigned at topic creation.
22    pub topic_id: Uuid,
23    /// Topic name (informational; not part of identity).
24    pub topic: String,
25    /// Partition index.
26    pub partition: i32,
27}
28
29impl TopicIdPartition {
30    /// Constructs a [`TopicIdPartition`].
31    #[must_use]
32    pub fn new(topic_id: Uuid, topic: impl Into<String>, partition: i32) -> Self {
33        Self {
34            topic_id,
35            topic: topic.into(),
36            partition,
37        }
38    }
39}
40
41impl PartialEq for TopicIdPartition {
42    fn eq(&self, other: &Self) -> bool {
43        self.topic_id == other.topic_id && self.partition == other.partition
44    }
45}
46
47impl Eq for TopicIdPartition {}
48
49impl Hash for TopicIdPartition {
50    fn hash<H: Hasher>(&self, state: &mut H) {
51        self.topic_id.hash(state);
52        self.partition.hash(state);
53    }
54}
55
56/// Globally-unique identifier for one remote log segment: the owning
57/// partition plus a random per-segment UUID.
58#[derive(Debug, Clone, PartialEq, Eq, Hash)]
59pub struct RemoteLogSegmentId {
60    /// The partition this segment belongs to.
61    pub topic_id_partition: TopicIdPartition,
62    /// Random per-segment UUID.
63    pub id: Uuid,
64}
65
66impl RemoteLogSegmentId {
67    /// Constructs a [`RemoteLogSegmentId`] from an explicit UUID.
68    #[must_use]
69    pub fn new(topic_id_partition: TopicIdPartition, id: Uuid) -> Self {
70        Self {
71            topic_id_partition,
72            id,
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use {super::*, assert2::assert, std::collections::HashSet};
80
81    #[test]
82    fn topic_id_partition_identity_ignores_name() {
83        let a = TopicIdPartition::new(Uuid::from_u128(7), "alpha", 3);
84        let b = TopicIdPartition::new(Uuid::from_u128(7), "renamed", 3);
85        assert!(a == b);
86        let set: HashSet<_> = maplit::hashset! {a, b};
87        assert!(set.len() == 1, "same id+partition must collapse in a set");
88    }
89
90    #[test]
91    fn topic_id_partition_distinct_partitions_differ() {
92        let a = TopicIdPartition::new(Uuid::from_u128(7), "alpha", 0);
93        let b = TopicIdPartition::new(Uuid::from_u128(7), "alpha", 1);
94        assert!(a != b);
95    }
96}