Skip to main content

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}