1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, ClusterError>;
6
7#[derive(Debug, Error, PartialEq, Eq)]
9pub enum CalvinError {
10 #[error("write set is empty; a Calvin transaction must write at least one key")]
11 EmptyWriteSet,
12
13 #[error(
14 "write set resolves to a single vshard ({vshard}); \
15 use the single-shard fast path instead"
16 )]
17 SingleVshardTxn { vshard: u32 },
18
19 #[error("sequencer error: {0}")]
22 Sequencer(#[from] crate::calvin::sequencer::error::SequencerError),
23}
24
25#[derive(Debug, Error)]
27pub enum MigrationCheckpointError {
28 #[error(
29 "crc32c mismatch on migration checkpoint for {migration_id}: expected {expected:#010x} got {actual:#010x}"
30 )]
31 Crc32cMismatch {
32 migration_id: uuid::Uuid,
33 expected: u32,
34 actual: u32,
35 },
36 #[error("codec error persisting migration checkpoint: {detail}")]
37 Codec { detail: String },
38 #[error("storage error persisting migration checkpoint: {detail}")]
39 Storage { detail: String },
40}
41
42#[derive(Debug, Error)]
44pub enum MigrationRecoveryError {
45 #[error("compensation failed for migration {migration_id} step {step}: {detail}")]
46 CompensationFailed {
47 migration_id: uuid::Uuid,
48 step: usize,
49 detail: String,
50 },
51 #[error("storage error during migration recovery: {detail}")]
52 Storage { detail: String },
53 #[error("codec error during migration recovery: {detail}")]
54 Codec { detail: String },
55}
56
57#[derive(Debug, Error)]
58pub enum ClusterError {
59 #[error("raft error: {0}")]
60 Raft(#[from] nodedb_raft::RaftError),
61
62 #[error("vshard {vshard_id} not mapped to any raft group")]
63 VShardNotMapped { vshard_id: u32 },
64
65 #[error("raft group {group_id} not found on this node")]
66 GroupNotFound { group_id: u64 },
67
68 #[error("migration in progress for vshard {vshard_id}")]
69 MigrationInProgress { vshard_id: u32 },
70
71 #[error("migration refused: estimated pause {estimated_us}µs exceeds budget {budget_us}µs")]
72 MigrationPauseBudgetExceeded { estimated_us: u64, budget_us: u64 },
73
74 #[error("node {node_id} not reachable")]
75 NodeUnreachable { node_id: u64 },
76
77 #[error("ghost stub not found: node={node_id} on shard={shard_id}")]
78 GhostNotFound { node_id: String, shard_id: u32 },
79
80 #[error("transport error: {detail}")]
81 Transport { detail: String },
82
83 #[error("streaming execution terminal error: {detail}")]
90 StreamTerminal {
91 error: crate::rpc_codec::TypedClusterError,
92 detail: String,
93 },
94
95 #[error("storage error: {detail}")]
96 Storage { detail: String },
97
98 #[error("codec error: {detail}")]
99 Codec { detail: String },
100
101 #[error(
102 "unsupported wire version: got {got}, this node accepts [{supported_min}..={supported_max}]"
103 )]
104 UnsupportedWireVersion {
105 got: u8,
106 supported_min: u8,
107 supported_max: u8,
108 },
109
110 #[error("circuit open for node {node_id}: peer has {failures} consecutive failures")]
111 CircuitOpen { node_id: u64, failures: u32 },
112
113 #[error("raft group {group_id} disappeared while waiting for conf change commit")]
114 JoinGroupDisappeared { group_id: u64 },
115
116 #[error("conf change commit timeout on group {group_id} (waited for index {log_index})")]
117 JoinCommitTimeout { group_id: u64, log_index: u64 },
118
119 #[error("invalid cluster configuration: {detail}")]
120 Config { detail: String },
121
122 #[error("migration checkpoint error: {0}")]
123 MigrationCheckpoint(#[from] MigrationCheckpointError),
124
125 #[error("migration recovery error: {0}")]
126 MigrationRecovery(#[from] MigrationRecoveryError),
127
128 #[error(
139 "vshard {vshard_id} misrouted: this node is no longer the owner\
140 {}", if let Some(n) = expected_owner_node { format!("; current owner may be node {n}") } else { String::new() }
141 )]
142 WrongOwner {
143 vshard_id: u32,
144 expected_owner_node: Option<u64>,
145 },
146
147 #[error("calvin error: {0}")]
148 Calvin(#[from] CalvinError),
149
150 #[error(
151 "snapshot CRC mismatch for group {group_id}: stored {stored:#010x}, computed {computed:#010x}"
152 )]
153 SnapshotCrcMismatch {
154 group_id: u64,
155 stored: u32,
156 computed: u32,
157 },
158
159 #[error("snapshot offset regression for group {group_id}: expected {expected}, got {actual}")]
160 SnapshotOffsetRegression {
161 group_id: u64,
162 expected: u64,
163 actual: u64,
164 },
165
166 #[error("partial snapshot file corrupt for group {group_id}: {detail}")]
167 PartialSnapshotCorrupt { group_id: u64, detail: String },
168
169 #[error("partial snapshot cleanup failed for group {group_id}: {detail}")]
170 PartialSnapshotCleanupFailed { group_id: u64, detail: String },
171
172 #[error("snapshot apply to local state machine failed for group {group_id}: {detail}")]
173 SnapshotApplyFailed { group_id: u64, detail: String },
174
175 #[error("mirror error: {0}")]
176 Mirror(#[from] crate::mirror::MirrorError),
177}