Skip to main content

krabka_remote_storage/metadata/
partition_delete.rs

1//! The partition-delete lifecycle.
2//!
3//! [`RemotePartitionDeleteMetadata`] records how far the deletion of one
4//! partition's remote data has got, and [`RemotePartitionDeleteState`] holds
5//! the three states plus the rule for moving between them. This lifecycle is
6//! separate from the per-segment one in
7//! [`RemoteLogSegmentState`](crate::metadata::RemoteLogSegmentState).
8
9use {
10    crate::metadata::TopicIdPartition,
11    krabka_verified::storage::{
12        RemotePartitionDeleteLifecycle, remote_partition_delete_transition,
13    },
14};
15
16/// Lifecycle state of a remote *partition* deletion.
17///
18/// ```text
19/// DeletePartitionMarked ──► DeletePartitionStarted ──► DeletePartitionFinished
20/// ```
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum RemotePartitionDeleteState {
23    /// The partition has been marked for deletion of all its remote
24    /// segments.
25    DeletePartitionMarked,
26    /// Deletion of the partition's remote segments has begun.
27    DeletePartitionStarted,
28    /// All remote segments for the partition have been deleted.
29    DeletePartitionFinished,
30}
31
32impl RemotePartitionDeleteState {
33    /// `true` if a partition currently in `from` may transition to `target`.
34    ///
35    /// A `from` of `None` means the partition was never marked. This is
36    /// Kafka's `RemotePartitionDeleteState.isValidTransition`, so a same-state
37    /// transition is valid: Kafka admits it to keep retries and failover
38    /// idempotent.
39    #[must_use]
40    pub fn is_valid_transition(from: Option<Self>, target: Self) -> bool {
41        remote_partition_delete_transition(from.map(Self::lifecycle), target.lifecycle())
42    }
43
44    const fn lifecycle(self) -> RemotePartitionDeleteLifecycle {
45        match self {
46            Self::DeletePartitionMarked => RemotePartitionDeleteLifecycle::Marked,
47            Self::DeletePartitionStarted => RemotePartitionDeleteLifecycle::Started,
48            Self::DeletePartitionFinished => RemotePartitionDeleteLifecycle::Finished,
49        }
50    }
51}
52
53/// Metadata describing the deletion lifecycle of a partition's remote data.
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct RemotePartitionDeleteMetadata {
56    /// The partition being deleted from the remote tier.
57    pub topic_id_partition: TopicIdPartition,
58    /// Current deletion state.
59    pub state: RemotePartitionDeleteState,
60    /// Wall-clock time of this event.
61    pub event_timestamp_ms: i64,
62    /// Broker that produced this metadata.
63    pub broker_id: i32,
64}
65
66#[cfg(test)]
67mod tests {
68    use {super::*, assert2::check};
69
70    #[test]
71    fn partition_delete_transition_matrix_is_exhaustive() {
72        use RemotePartitionDeleteState::{
73            DeletePartitionFinished, DeletePartitionMarked, DeletePartitionStarted,
74        };
75        let from_states = [
76            None,
77            Some(DeletePartitionMarked),
78            Some(DeletePartitionStarted),
79            Some(DeletePartitionFinished),
80        ];
81        let targets = [
82            DeletePartitionMarked,
83            DeletePartitionStarted,
84            DeletePartitionFinished,
85        ];
86        // Kafka's `RemotePartitionDeleteState.isValidTransition`, row =
87        // source, the first row being no prior state.
88        let expected = [
89            [true, false, false],
90            [true, true, false],
91            [false, true, true],
92            [false, false, true],
93        ];
94        for (from_index, from) in from_states.into_iter().enumerate() {
95            for (to_index, to) in targets.into_iter().enumerate() {
96                check!(
97                    RemotePartitionDeleteState::is_valid_transition(from, to)
98                        == expected[from_index][to_index],
99                    "{from:?} -> {to:?}"
100                );
101            }
102        }
103    }
104}