1use std::collections::BTreeMap;
2use std::collections::BTreeSet;
3use std::fmt;
4
5use serde::Deserialize;
6use serde::Serialize;
7use ursula_shard::RaftGroupId;
8
9use crate::model::LearnerStatus;
10use crate::model::MigrationPhase;
11use crate::model::NodeId;
12use crate::model::NodeState;
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub enum ControlCommand {
16 RegisterNode {
17 node_id: NodeId,
18 client_url: String,
19 cluster_url: String,
20 #[serde(default)]
21 labels: BTreeMap<String, String>,
22 now_ms: u64,
23 },
24 SetNodeState {
25 node_id: NodeId,
26 state: NodeState,
27 now_ms: u64,
28 },
29 SeedPlacement {
30 raft_group_id: RaftGroupId,
31 voters: BTreeSet<NodeId>,
32 now_ms: u64,
33 },
34 BeginMigration {
35 raft_group_id: RaftGroupId,
36 target_voters: BTreeSet<NodeId>,
37 retain_removed: bool,
38 now_ms: u64,
39 },
40 AdvanceMigration {
41 migration_id: u64,
42 phase: MigrationPhase,
43 now_ms: u64,
44 },
45 SetLearnerStatus {
46 migration_id: u64,
47 node_id: NodeId,
48 status: LearnerStatus,
49 now_ms: u64,
50 },
51 RecordMigrationError {
52 migration_id: u64,
53 error: String,
54 now_ms: u64,
55 },
56 CommitPlacement {
57 raft_group_id: RaftGroupId,
58 voters: BTreeSet<NodeId>,
59 learners: BTreeSet<NodeId>,
60 draining: BTreeSet<NodeId>,
61 now_ms: u64,
62 },
63 FinishMigration {
64 migration_id: u64,
65 success: bool,
66 now_ms: u64,
67 },
68 EvictLearner {
69 raft_group_id: RaftGroupId,
70 node_id: NodeId,
71 now_ms: u64,
72 },
73}
74
75impl ControlCommand {
76 pub fn now_ms(&self) -> u64 {
77 match self {
78 Self::RegisterNode { now_ms, .. }
79 | Self::SetNodeState { now_ms, .. }
80 | Self::SeedPlacement { now_ms, .. }
81 | Self::BeginMigration { now_ms, .. }
82 | Self::AdvanceMigration { now_ms, .. }
83 | Self::SetLearnerStatus { now_ms, .. }
84 | Self::RecordMigrationError { now_ms, .. }
85 | Self::CommitPlacement { now_ms, .. }
86 | Self::FinishMigration { now_ms, .. }
87 | Self::EvictLearner { now_ms, .. } => *now_ms,
88 }
89 }
90}
91
92impl fmt::Display for ControlCommand {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.write_str(match self {
95 Self::RegisterNode { .. } => "register_node",
96 Self::SetNodeState { .. } => "set_node_state",
97 Self::SeedPlacement { .. } => "seed_placement",
98 Self::BeginMigration { .. } => "begin_migration",
99 Self::AdvanceMigration { .. } => "advance_migration",
100 Self::SetLearnerStatus { .. } => "set_learner_status",
101 Self::RecordMigrationError { .. } => "record_migration_error",
102 Self::CommitPlacement { .. } => "commit_placement",
103 Self::FinishMigration { .. } => "finish_migration",
104 Self::EvictLearner { .. } => "evict_learner",
105 })
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110pub enum ControlResponse {
111 Ok,
112 MigrationStarted { migration_id: u64 },
113 Rejected { reason: String },
114}
115
116impl ControlResponse {
117 pub fn is_rejected(&self) -> bool {
118 matches!(self, Self::Rejected { .. })
119 }
120}
121
122impl fmt::Display for ControlResponse {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 f.write_str(match self {
125 Self::Ok => "ok",
126 Self::MigrationStarted { .. } => "migration_started",
127 Self::Rejected { .. } => "rejected",
128 })
129 }
130}