1use std::fmt;
2
3use bytes::Bytes;
4use serde::Deserialize;
5use serde::Serialize;
6use ursula_shard::BucketStreamId;
7
8use crate::model::ColdChunkRef;
9use crate::model::ExternalPayloadRef;
10use crate::model::ProducerRequest;
11use crate::model::StreamAttrs;
12use crate::snapshot::StreamSnapshot;
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub enum StreamCommand {
16 CreateBucket {
17 bucket_id: String,
18 },
19 DeleteBucket {
20 bucket_id: String,
21 },
22 CreateStream {
23 stream_id: BucketStreamId,
24 content_type: String,
25 initial_payload: Bytes,
26 close_after: bool,
27 stream_seq: Option<String>,
28 producer: Option<ProducerRequest>,
29 stream_ttl_seconds: Option<u64>,
30 stream_expires_at_ms: Option<u64>,
31 #[serde(default)]
33 attrs: Option<StreamAttrs>,
34 now_ms: u64,
35 },
36 CreateExternal {
37 stream_id: BucketStreamId,
38 content_type: String,
39 initial_payload: ExternalPayloadRef,
40 #[serde(default)]
41 record_ends: Vec<u64>,
42 close_after: bool,
43 stream_seq: Option<String>,
44 producer: Option<ProducerRequest>,
45 stream_ttl_seconds: Option<u64>,
46 stream_expires_at_ms: Option<u64>,
47 #[serde(default)]
49 attrs: Option<StreamAttrs>,
50 now_ms: u64,
51 },
52 Append {
53 stream_id: BucketStreamId,
54 content_type: Option<String>,
55 payload: Bytes,
56 close_after: bool,
57 stream_seq: Option<String>,
58 producer: Option<ProducerRequest>,
59 now_ms: u64,
60 record_match: Option<u64>,
61 },
62 AppendExternal {
63 stream_id: BucketStreamId,
64 content_type: Option<String>,
65 payload: ExternalPayloadRef,
66 #[serde(default)]
67 record_ends: Vec<u64>,
68 close_after: bool,
69 stream_seq: Option<String>,
70 producer: Option<ProducerRequest>,
71 now_ms: u64,
72 record_match: Option<u64>,
73 },
74 AppendBatch {
75 stream_id: BucketStreamId,
76 content_type: Option<String>,
77 payloads: Vec<Bytes>,
78 producer: Option<ProducerRequest>,
79 now_ms: u64,
80 },
81 PublishSnapshot {
82 stream_id: BucketStreamId,
83 snapshot_offset: u64,
84 content_type: String,
85 payload: Bytes,
86 #[serde(default)]
87 expected_digest: Option<String>,
88 now_ms: u64,
89 },
90 AdvanceRetention {
91 stream_id: BucketStreamId,
92 retained_offset: u64,
93 now_ms: u64,
94 },
95 TouchStreamAccess {
96 stream_id: BucketStreamId,
97 now_ms: u64,
98 renew_ttl: bool,
99 },
100 UpdateStreamAttrs {
101 stream_id: BucketStreamId,
102 attrs: Option<StreamAttrs>,
103 now_ms: u64,
104 },
105 FlushCold {
106 stream_id: BucketStreamId,
107 chunk: ColdChunkRef,
108 },
109 CompactCold {
114 stream_id: BucketStreamId,
115 old_chunks: Vec<ColdChunkRef>,
116 replacement: ColdChunkRef,
117 gc_not_before_ms: u64,
118 },
119 Close {
120 stream_id: BucketStreamId,
121 stream_seq: Option<String>,
122 producer: Option<ProducerRequest>,
123 now_ms: u64,
124 },
125 DeleteStream {
126 stream_id: BucketStreamId,
127 },
128 PurgeBucket {
132 bucket_id: String,
133 },
134 AckColdGc {
138 up_to_seq: u64,
139 },
140 ImportSnapshot {
148 snapshot: Box<StreamSnapshot>,
149 },
150 SetBucketQuota {
154 bucket_id: String,
155 max_streams: Option<u64>,
156 max_retained_bytes: Option<u64>,
157 },
158}
159
160impl fmt::Display for StreamCommand {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 match self {
163 Self::CreateBucket { bucket_id } => write!(f, "create_bucket:{bucket_id}"),
164 Self::DeleteBucket { bucket_id } => write!(f, "delete_bucket:{bucket_id}"),
165 Self::CreateStream { stream_id, .. } => write!(f, "create_stream:{stream_id}"),
166 Self::CreateExternal {
167 stream_id,
168 initial_payload,
169 ..
170 } => write!(
171 f,
172 "create_external:{stream_id}:{} bytes",
173 initial_payload.payload_len
174 ),
175 Self::Append {
176 stream_id, payload, ..
177 } => write!(f, "append:{stream_id}:{} bytes", payload.len()),
178 Self::AppendExternal {
179 stream_id, payload, ..
180 } => write!(
181 f,
182 "append_external:{stream_id}:{} bytes",
183 payload.payload_len
184 ),
185 Self::AppendBatch {
186 stream_id,
187 payloads,
188 ..
189 } => write!(f, "append_batch:{stream_id}:{} items", payloads.len()),
190 Self::PublishSnapshot {
191 stream_id,
192 snapshot_offset,
193 payload,
194 ..
195 } => write!(
196 f,
197 "publish_snapshot:{stream_id}:{snapshot_offset}:{} bytes",
198 payload.len()
199 ),
200 Self::AdvanceRetention {
201 stream_id,
202 retained_offset,
203 ..
204 } => write!(f, "advance_retention:{stream_id}:{retained_offset}"),
205 Self::TouchStreamAccess {
206 stream_id,
207 renew_ttl,
208 ..
209 } => write!(f, "touch_stream_access:{stream_id}:renew_ttl={renew_ttl}"),
210 Self::UpdateStreamAttrs { stream_id, .. } => {
211 write!(f, "update_stream_attrs:{stream_id}")
212 }
213 Self::FlushCold { stream_id, chunk } => write!(
214 f,
215 "flush_cold:{stream_id}:{}..{}",
216 chunk.start_offset, chunk.end_offset
217 ),
218 Self::CompactCold {
219 stream_id,
220 old_chunks,
221 replacement,
222 ..
223 } => write!(
224 f,
225 "compact_cold:{stream_id}:{} chunks:{}..{}",
226 old_chunks.len(),
227 replacement.start_offset,
228 replacement.end_offset
229 ),
230 Self::Close { stream_id, .. } => write!(f, "close_stream:{stream_id}"),
231 Self::DeleteStream { stream_id } => write!(f, "delete_stream:{stream_id}"),
232 Self::PurgeBucket { bucket_id } => write!(f, "purge_bucket:{bucket_id}"),
233 Self::AckColdGc { up_to_seq } => write!(f, "ack_cold_gc:up_to_seq={up_to_seq}"),
234 Self::ImportSnapshot { snapshot } => write!(
235 f,
236 "import_snapshot:buckets={}:streams={}",
237 snapshot.buckets.len(),
238 snapshot.streams.len()
239 ),
240 Self::SetBucketQuota { bucket_id, .. } => {
241 write!(f, "set_bucket_quota:{bucket_id}")
242 }
243 }
244 }
245}