Skip to main content

krabka_metadata/
directory_id.rs

1//! KIP-858 reserved log-directory ids, transcribed from Kafka's
2//! `org.apache.kafka.common.DirectoryId`.
3//!
4//! A partition's `directories` list holds one id per replica. Besides the real
5//! ids a broker generates for its log directories, Kafka reserves the first
6//! 100 UUIDs (most significant bits 0, least significant bits below 100) and
7//! gives three of them a meaning.
8
9use uuid::Uuid;
10
11/// `DirectoryId.MIGRATING`, `Uuid(0, 0)`: the replica already lives in some
12/// log directory, but nobody recorded which.
13///
14/// Kafka reads a partition record with no directories (every record before
15/// `3.7-IV2`) as all `MIGRATING`, and its controller places a new replica on a
16/// broker that registered no log-dir ids, or on any broker below `3.7-IV2`, as
17/// `MIGRATING`.
18pub const MIGRATING: Uuid = Uuid::from_u64_pair(0, 0);
19
20/// `DirectoryId.UNASSIGNED`, `Uuid(0, 1)`: a new replica that no log directory
21/// hosts yet.
22///
23/// Kafka's controller places a replica on a broker with more than one log
24/// directory, or on a broker it has no registration for, as `UNASSIGNED`, and
25/// the broker's `AssignReplicasToDirs` later names the directory.
26pub const UNASSIGNED: Uuid = Uuid::from_u64_pair(0, 1);
27
28/// `DirectoryId.LOST`, `Uuid(0, 2)`: the replica's directory is offline and
29/// its id is unknown.
30pub const LOST: Uuid = Uuid::from_u64_pair(0, 2);
31
32/// `DirectoryId.reserved`: true for the first 100 UUIDs, which no broker
33/// generates as a log-directory id.
34#[must_use]
35pub fn reserved(dir: Uuid) -> bool {
36    let (most, least) = dir.as_u64_pair();
37    most == 0 && least < 100
38}
39
40/// `DirectoryId.isOnline`: whether a replica assigned to `dir` counts as on
41/// an online directory of a broker whose online directories are `online_dirs`.
42///
43/// `UNASSIGNED` and `MIGRATING` are online and `LOST` is offline. An empty
44/// `online_dirs` also counts as online: Kafka reaches it only for a broker
45/// that registered before `3.7-IV2` carried log dirs, and it assumes a broker
46/// halts once every log directory is down.
47#[must_use]
48pub fn is_online(dir: Uuid, online_dirs: &[Uuid]) -> bool {
49    if dir == UNASSIGNED || dir == MIGRATING {
50        return true;
51    }
52    if dir == LOST {
53        return false;
54    }
55    online_dirs.is_empty() || online_dirs.contains(&dir)
56}
57
58#[cfg(test)]
59mod tests {
60    use assert2::check;
61
62    use super::*;
63
64    /// The sentinels are Kafka's `new Uuid(0L, 0L)`, `new Uuid(0L, 1L)` and
65    /// `new Uuid(0L, 2L)`, whose big-endian bytes end in 0, 1 and 2.
66    #[test]
67    fn sentinels_match_kafka() {
68        check!(
69            [MIGRATING, UNASSIGNED, LOST] == [Uuid::nil(), Uuid::from_u128(1), Uuid::from_u128(2)]
70        );
71    }
72
73    #[test]
74    fn reserved_is_the_first_hundred_uuids() {
75        for (case, dir, want) in [
76            ("MIGRATING", MIGRATING, true),
77            ("UNASSIGNED", UNASSIGNED, true),
78            ("LOST", LOST, true),
79            ("last reserved", Uuid::from_u128(99), true),
80            ("first unreserved", Uuid::from_u128(100), false),
81            ("high bits set", Uuid::from_u64_pair(1, 0), false),
82        ] {
83            check!(reserved(dir) == want, "{case}");
84        }
85    }
86
87    #[test]
88    fn is_online_matches_kafka() {
89        let (a, b) = (Uuid::from_u128(0xA00), Uuid::from_u128(0xB00));
90        for (case, dir, online, want) in [
91            ("UNASSIGNED", UNASSIGNED, &[a][..], true),
92            ("MIGRATING", MIGRATING, &[a][..], true),
93            ("LOST", LOST, &[a][..], false),
94            ("LOST with no dirs", LOST, &[][..], false),
95            ("listed dir", a, &[a, b][..], true),
96            ("unlisted dir", b, &[a][..], false),
97            ("no dirs registered", b, &[][..], true),
98        ] {
99            check!(is_online(dir, online) == want, "{case}");
100        }
101    }
102}