krabka_remote_storage/metadata/segment_state.rs
1//! The lifecycle state machine of one remote log segment.
2//!
3//! [`RemoteLogSegmentState`] holds the four states a segment moves through and
4//! the single rule that decides which move is legal, so every metadata update
5//! is checked against one place.
6
7use krabka_verified::storage::{RemoteSegmentLifecycle, remote_segment_transition};
8
9/// Lifecycle state of a remote log segment.
10///
11/// Valid transitions (see [`RemoteLogSegmentState::is_valid_transition`]),
12/// plus every self transition:
13///
14/// ```text
15/// CopySegmentStarted ──► CopySegmentFinished ──► DeleteSegmentStarted ──► DeleteSegmentFinished
16/// └───────────────────────────────────►┘
17/// ```
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum RemoteLogSegmentState {
20 /// A copy to the remote tier has begun but not finished. The starting
21 /// state of every segment.
22 CopySegmentStarted,
23 /// The copy finished; the segment is durable in the remote tier and
24 /// readable.
25 CopySegmentFinished,
26 /// Deletion from the remote tier has begun.
27 DeleteSegmentStarted,
28 /// The segment has been fully removed from the remote tier.
29 DeleteSegmentFinished,
30}
31
32impl RemoteLogSegmentState {
33 /// `true` if a segment currently in `self` may transition to `target`.
34 ///
35 /// This is Kafka's `RemoteLogSegmentState.isValidTransition`, so a
36 /// same-state transition is valid: Kafka admits it to keep retries and
37 /// failover idempotent.
38 #[must_use]
39 pub fn is_valid_transition(self, target: Self) -> bool {
40 remote_segment_transition(self.lifecycle(), target.lifecycle())
41 }
42
43 /// The proof kernels' view of this state.
44 pub(crate) const fn lifecycle(self) -> RemoteSegmentLifecycle {
45 match self {
46 Self::CopySegmentStarted => RemoteSegmentLifecycle::CopyStarted,
47 Self::CopySegmentFinished => RemoteSegmentLifecycle::CopyFinished,
48 Self::DeleteSegmentStarted => RemoteSegmentLifecycle::DeleteStarted,
49 Self::DeleteSegmentFinished => RemoteSegmentLifecycle::DeleteFinished,
50 }
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use {super::*, assert2::check};
57
58 krabka_macros::remote_segment_transition_matrix!(expected_transitions);
59
60 #[test]
61 fn segment_state_transition_matrix_is_exhaustive() {
62 use RemoteLogSegmentState::{
63 CopySegmentFinished, CopySegmentStarted, DeleteSegmentFinished, DeleteSegmentStarted,
64 };
65 let states = [
66 CopySegmentStarted,
67 CopySegmentFinished,
68 DeleteSegmentStarted,
69 DeleteSegmentFinished,
70 ];
71 // Kafka's `RemoteLogSegmentState.isValidTransition`, row = source.
72 let expected = expected_transitions();
73 for (from_index, from) in states.into_iter().enumerate() {
74 for (to_index, to) in states.into_iter().enumerate() {
75 check!(
76 from.is_valid_transition(to) == expected[from_index][to_index],
77 "{from:?} -> {to:?}"
78 );
79 }
80 }
81 }
82}