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 {
133 bucket_id: String,
134 },
135 AckColdGc {
139 up_to_seq: u64,
140 },
141 ImportSnapshot {
149 snapshot: Box<StreamSnapshot>,
150 },
151 SetBucketQuota {
155 bucket_id: String,
156 max_streams: Option<u64>,
157 max_retained_bytes: Option<u64>,
158 },
159}
160
161impl fmt::Display for StreamCommand {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 match self {
164 Self::CreateBucket { bucket_id } => write!(f, "create_bucket:{bucket_id}"),
165 Self::DeleteBucket { bucket_id } => write!(f, "delete_bucket:{bucket_id}"),
166 Self::CreateStream { stream_id, .. } => write!(f, "create_stream:{stream_id}"),
167 Self::CreateExternal {
168 stream_id,
169 initial_payload,
170 ..
171 } => write!(
172 f,
173 "create_external:{stream_id}:{} bytes",
174 initial_payload.payload_len
175 ),
176 Self::Append {
177 stream_id, payload, ..
178 } => write!(f, "append:{stream_id}:{} bytes", payload.len()),
179 Self::AppendExternal {
180 stream_id, payload, ..
181 } => write!(
182 f,
183 "append_external:{stream_id}:{} bytes",
184 payload.payload_len
185 ),
186 Self::AppendBatch {
187 stream_id,
188 payloads,
189 ..
190 } => write!(f, "append_batch:{stream_id}:{} items", payloads.len()),
191 Self::PublishSnapshot {
192 stream_id,
193 snapshot_offset,
194 payload,
195 ..
196 } => write!(
197 f,
198 "publish_snapshot:{stream_id}:{snapshot_offset}:{} bytes",
199 payload.len()
200 ),
201 Self::AdvanceRetention {
202 stream_id,
203 retained_offset,
204 ..
205 } => write!(f, "advance_retention:{stream_id}:{retained_offset}"),
206 Self::TouchStreamAccess {
207 stream_id,
208 renew_ttl,
209 ..
210 } => write!(f, "touch_stream_access:{stream_id}:renew_ttl={renew_ttl}"),
211 Self::UpdateStreamAttrs { stream_id, .. } => {
212 write!(f, "update_stream_attrs:{stream_id}")
213 }
214 Self::FlushCold { stream_id, chunk } => write!(
215 f,
216 "flush_cold:{stream_id}:{}..{}",
217 chunk.start_offset, chunk.end_offset
218 ),
219 Self::CompactCold {
220 stream_id,
221 old_chunks,
222 replacement,
223 ..
224 } => write!(
225 f,
226 "compact_cold:{stream_id}:{} chunks:{}..{}",
227 old_chunks.len(),
228 replacement.start_offset,
229 replacement.end_offset
230 ),
231 Self::Close { stream_id, .. } => write!(f, "close_stream:{stream_id}"),
232 Self::DeleteStream { stream_id } => write!(f, "delete_stream:{stream_id}"),
233 Self::PurgeBucket { bucket_id } => write!(f, "purge_bucket:{bucket_id}"),
234 Self::AckColdGc { up_to_seq } => write!(f, "ack_cold_gc:up_to_seq={up_to_seq}"),
235 Self::ImportSnapshot { snapshot } => write!(
236 f,
237 "import_snapshot:buckets={}:streams={}",
238 snapshot.buckets.len(),
239 snapshot.streams.len()
240 ),
241 Self::SetBucketQuota { bucket_id, .. } => {
242 write!(f, "set_bucket_quota:{bucket_id}")
243 }
244 }
245 }
246}