1use std::fmt;
2
3use bytes::Bytes;
4use serde::Deserialize;
5use serde::Serialize;
6use ursula_shard::BucketStreamId;
7use ursula_shard::ShardPlacement;
8use ursula_stream::ColdChunkRef;
9use ursula_stream::ExternalPayloadRef;
10use ursula_stream::ProducerRequest;
11use ursula_stream::StreamAttrs;
12use ursula_stream::StreamSnapshot;
13
14use crate::request::AppendBatchRequest;
15use crate::request::AppendExternalRequest;
16use crate::request::AppendRequest;
17use crate::request::CloseStreamRequest;
18use crate::request::CreateStreamExternalRequest;
19use crate::request::CreateStreamRequest;
20use crate::request::DeleteStreamRequest;
21use crate::request::FlushColdRequest;
22use crate::request::PublishSnapshotRequest;
23use crate::request::StreamAppendCount;
24use crate::request::UpdateStreamAttrsRequest;
25
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub struct GroupSnapshot {
28 pub placement: ShardPlacement,
29 pub group_commit_index: u64,
30 pub stream_snapshot: StreamSnapshot,
31 pub stream_append_counts: Vec<StreamAppendCount>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(tag = "command", rename_all = "snake_case")]
36pub enum GroupWriteCommand {
37 CreateStream {
38 stream_id: BucketStreamId,
39 content_type: String,
40 initial_payload: Bytes,
41 close_after: bool,
42 stream_seq: Option<String>,
43 producer: Option<ProducerRequest>,
44 stream_ttl_seconds: Option<u64>,
45 stream_expires_at_ms: Option<u64>,
46 forked_from: Option<BucketStreamId>,
47 fork_offset: Option<u64>,
48 #[serde(default)]
50 attrs: Option<StreamAttrs>,
51 now_ms: u64,
52 },
53 CreateExternal {
54 stream_id: BucketStreamId,
55 content_type: String,
56 initial_payload: ExternalPayloadRef,
57 close_after: bool,
58 stream_seq: Option<String>,
59 producer: Option<ProducerRequest>,
60 stream_ttl_seconds: Option<u64>,
61 stream_expires_at_ms: Option<u64>,
62 forked_from: Option<BucketStreamId>,
63 fork_offset: Option<u64>,
64 #[serde(default)]
66 attrs: Option<StreamAttrs>,
67 now_ms: u64,
68 },
69 Append {
70 stream_id: BucketStreamId,
71 content_type: String,
72 payload: Bytes,
73 close_after: bool,
74 stream_seq: Option<String>,
75 producer: Option<ProducerRequest>,
76 now_ms: u64,
77 },
78 AppendExternal {
79 stream_id: BucketStreamId,
80 content_type: String,
81 payload: ExternalPayloadRef,
82 close_after: bool,
83 stream_seq: Option<String>,
84 producer: Option<ProducerRequest>,
85 now_ms: u64,
86 },
87 AppendBatch {
88 stream_id: BucketStreamId,
89 content_type: String,
90 payloads: Vec<Bytes>,
91 producer: Option<ProducerRequest>,
92 now_ms: u64,
93 },
94 PublishSnapshot {
95 stream_id: BucketStreamId,
96 snapshot_offset: u64,
97 content_type: String,
98 payload: Bytes,
99 now_ms: u64,
100 },
101 TouchStreamAccess {
102 stream_id: BucketStreamId,
103 now_ms: u64,
104 renew_ttl: bool,
105 },
106 UpdateStreamAttrs {
107 stream_id: BucketStreamId,
108 attrs: Option<StreamAttrs>,
109 now_ms: u64,
110 },
111 AddForkRef {
112 stream_id: BucketStreamId,
113 now_ms: u64,
114 },
115 ReleaseForkRef {
116 stream_id: BucketStreamId,
117 },
118 FlushCold {
119 stream_id: BucketStreamId,
120 chunk: ColdChunkRef,
121 },
122 CloseStream {
123 stream_id: BucketStreamId,
124 stream_seq: Option<String>,
125 producer: Option<ProducerRequest>,
126 now_ms: u64,
127 },
128 DeleteStream {
129 stream_id: BucketStreamId,
130 },
131 AckColdGc {
132 up_to_seq: u64,
133 },
134 Batch {
135 commands: Vec<GroupWriteCommand>,
136 },
137}
138
139impl From<CreateStreamRequest> for GroupWriteCommand {
140 fn from(request: CreateStreamRequest) -> Self {
141 Self::CreateStream {
142 stream_id: request.stream_id,
143 content_type: request.content_type,
144 initial_payload: request.initial_payload,
145 close_after: request.close_after,
146 stream_seq: request.stream_seq,
147 producer: request.producer,
148 stream_ttl_seconds: request.stream_ttl_seconds,
149 stream_expires_at_ms: request.stream_expires_at_ms,
150 forked_from: request.forked_from,
151 fork_offset: request.fork_offset,
152 attrs: request.attrs,
153 now_ms: request.now_ms,
154 }
155 }
156}
157
158impl From<&CreateStreamRequest> for GroupWriteCommand {
159 fn from(request: &CreateStreamRequest) -> Self {
160 Self::CreateStream {
161 stream_id: request.stream_id.clone(),
162 content_type: request.content_type.clone(),
163 initial_payload: request.initial_payload.clone(),
164 close_after: request.close_after,
165 stream_seq: request.stream_seq.clone(),
166 producer: request.producer.clone(),
167 stream_ttl_seconds: request.stream_ttl_seconds,
168 stream_expires_at_ms: request.stream_expires_at_ms,
169 forked_from: request.forked_from.clone(),
170 fork_offset: request.fork_offset,
171 attrs: request.attrs.clone(),
172 now_ms: request.now_ms,
173 }
174 }
175}
176
177impl From<CreateStreamExternalRequest> for GroupWriteCommand {
178 fn from(request: CreateStreamExternalRequest) -> Self {
179 Self::CreateExternal {
180 stream_id: request.stream_id,
181 content_type: request.content_type,
182 initial_payload: request.initial_payload,
183 close_after: request.close_after,
184 stream_seq: request.stream_seq,
185 producer: request.producer,
186 stream_ttl_seconds: request.stream_ttl_seconds,
187 stream_expires_at_ms: request.stream_expires_at_ms,
188 forked_from: request.forked_from,
189 fork_offset: request.fork_offset,
190 attrs: request.attrs,
191 now_ms: request.now_ms,
192 }
193 }
194}
195
196impl From<&CreateStreamExternalRequest> for GroupWriteCommand {
197 fn from(request: &CreateStreamExternalRequest) -> Self {
198 Self::CreateExternal {
199 stream_id: request.stream_id.clone(),
200 content_type: request.content_type.clone(),
201 initial_payload: request.initial_payload.clone(),
202 close_after: request.close_after,
203 stream_seq: request.stream_seq.clone(),
204 producer: request.producer.clone(),
205 stream_ttl_seconds: request.stream_ttl_seconds,
206 stream_expires_at_ms: request.stream_expires_at_ms,
207 forked_from: request.forked_from.clone(),
208 fork_offset: request.fork_offset,
209 attrs: request.attrs.clone(),
210 now_ms: request.now_ms,
211 }
212 }
213}
214
215impl From<UpdateStreamAttrsRequest> for GroupWriteCommand {
216 fn from(request: UpdateStreamAttrsRequest) -> Self {
217 Self::UpdateStreamAttrs {
218 stream_id: request.stream_id,
219 attrs: request.attrs,
220 now_ms: request.now_ms,
221 }
222 }
223}
224
225impl From<&UpdateStreamAttrsRequest> for GroupWriteCommand {
226 fn from(request: &UpdateStreamAttrsRequest) -> Self {
227 Self::UpdateStreamAttrs {
228 stream_id: request.stream_id.clone(),
229 attrs: request.attrs.clone(),
230 now_ms: request.now_ms,
231 }
232 }
233}
234
235impl From<AppendRequest> for GroupWriteCommand {
236 fn from(request: AppendRequest) -> Self {
237 Self::Append {
238 stream_id: request.stream_id,
239 content_type: request.content_type,
240 payload: request.payload,
241 close_after: request.close_after,
242 stream_seq: request.stream_seq,
243 producer: request.producer,
244 now_ms: request.now_ms,
245 }
246 }
247}
248
249impl From<&AppendRequest> for GroupWriteCommand {
250 fn from(request: &AppendRequest) -> Self {
251 Self::Append {
252 stream_id: request.stream_id.clone(),
253 content_type: request.content_type.clone(),
254 payload: request.payload.clone(),
255 close_after: request.close_after,
256 stream_seq: request.stream_seq.clone(),
257 producer: request.producer.clone(),
258 now_ms: request.now_ms,
259 }
260 }
261}
262
263impl From<AppendExternalRequest> for GroupWriteCommand {
264 fn from(request: AppendExternalRequest) -> Self {
265 Self::AppendExternal {
266 stream_id: request.stream_id,
267 content_type: request.content_type,
268 payload: request.payload,
269 close_after: request.close_after,
270 stream_seq: request.stream_seq,
271 producer: request.producer,
272 now_ms: request.now_ms,
273 }
274 }
275}
276
277impl From<&AppendExternalRequest> for GroupWriteCommand {
278 fn from(request: &AppendExternalRequest) -> Self {
279 Self::AppendExternal {
280 stream_id: request.stream_id.clone(),
281 content_type: request.content_type.clone(),
282 payload: request.payload.clone(),
283 close_after: request.close_after,
284 stream_seq: request.stream_seq.clone(),
285 producer: request.producer.clone(),
286 now_ms: request.now_ms,
287 }
288 }
289}
290
291impl From<AppendBatchRequest> for GroupWriteCommand {
292 fn from(request: AppendBatchRequest) -> Self {
293 Self::AppendBatch {
294 stream_id: request.stream_id,
295 content_type: request.content_type,
296 payloads: request.payloads,
297 producer: request.producer,
298 now_ms: request.now_ms,
299 }
300 }
301}
302
303impl From<&AppendBatchRequest> for GroupWriteCommand {
304 fn from(request: &AppendBatchRequest) -> Self {
305 Self::AppendBatch {
306 stream_id: request.stream_id.clone(),
307 content_type: request.content_type.clone(),
308 payloads: request.payloads.clone(),
309 producer: request.producer.clone(),
310 now_ms: request.now_ms,
311 }
312 }
313}
314
315impl From<PublishSnapshotRequest> for GroupWriteCommand {
316 fn from(request: PublishSnapshotRequest) -> Self {
317 Self::PublishSnapshot {
318 stream_id: request.stream_id,
319 snapshot_offset: request.snapshot_offset,
320 content_type: request.content_type,
321 payload: request.payload,
322 now_ms: request.now_ms,
323 }
324 }
325}
326
327impl From<&PublishSnapshotRequest> for GroupWriteCommand {
328 fn from(request: &PublishSnapshotRequest) -> Self {
329 Self::PublishSnapshot {
330 stream_id: request.stream_id.clone(),
331 snapshot_offset: request.snapshot_offset,
332 content_type: request.content_type.clone(),
333 payload: request.payload.clone(),
334 now_ms: request.now_ms,
335 }
336 }
337}
338
339impl From<CloseStreamRequest> for GroupWriteCommand {
340 fn from(request: CloseStreamRequest) -> Self {
341 Self::CloseStream {
342 stream_id: request.stream_id,
343 stream_seq: request.stream_seq,
344 producer: request.producer,
345 now_ms: request.now_ms,
346 }
347 }
348}
349
350impl From<&CloseStreamRequest> for GroupWriteCommand {
351 fn from(request: &CloseStreamRequest) -> Self {
352 Self::CloseStream {
353 stream_id: request.stream_id.clone(),
354 stream_seq: request.stream_seq.clone(),
355 producer: request.producer.clone(),
356 now_ms: request.now_ms,
357 }
358 }
359}
360
361impl From<DeleteStreamRequest> for GroupWriteCommand {
362 fn from(request: DeleteStreamRequest) -> Self {
363 Self::DeleteStream {
364 stream_id: request.stream_id,
365 }
366 }
367}
368
369impl From<&DeleteStreamRequest> for GroupWriteCommand {
370 fn from(request: &DeleteStreamRequest) -> Self {
371 Self::DeleteStream {
372 stream_id: request.stream_id.clone(),
373 }
374 }
375}
376
377impl From<FlushColdRequest> for GroupWriteCommand {
378 fn from(request: FlushColdRequest) -> Self {
379 Self::FlushCold {
380 stream_id: request.stream_id,
381 chunk: request.chunk,
382 }
383 }
384}
385
386impl From<&FlushColdRequest> for GroupWriteCommand {
387 fn from(request: &FlushColdRequest) -> Self {
388 Self::FlushCold {
389 stream_id: request.stream_id.clone(),
390 chunk: request.chunk.clone(),
391 }
392 }
393}
394
395impl fmt::Display for GroupWriteCommand {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 match self {
398 Self::CreateStream { stream_id, .. } => {
399 write!(f, "create_stream:{stream_id}")
400 }
401 Self::CreateExternal {
402 stream_id,
403 initial_payload,
404 ..
405 } => {
406 write!(
407 f,
408 "create_external:{stream_id}:{} bytes",
409 initial_payload.payload_len
410 )
411 }
412 Self::Append {
413 stream_id, payload, ..
414 } => {
415 write!(f, "append:{stream_id}:{} bytes", payload.len())
416 }
417 Self::AppendExternal {
418 stream_id, payload, ..
419 } => {
420 write!(
421 f,
422 "append_external:{stream_id}:{} bytes",
423 payload.payload_len
424 )
425 }
426 Self::AppendBatch {
427 stream_id,
428 payloads,
429 ..
430 } => {
431 write!(f, "append_batch:{stream_id}:{} items", payloads.len())
432 }
433 Self::PublishSnapshot {
434 stream_id,
435 snapshot_offset,
436 payload,
437 ..
438 } => {
439 write!(
440 f,
441 "publish_snapshot:{stream_id}:{snapshot_offset}:{} bytes",
442 payload.len()
443 )
444 }
445 Self::TouchStreamAccess {
446 stream_id,
447 renew_ttl,
448 ..
449 } => {
450 write!(f, "touch_stream_access:{stream_id}:renew_ttl={renew_ttl}")
451 }
452 Self::UpdateStreamAttrs { stream_id, .. } => {
453 write!(f, "update_stream_attrs:{stream_id}")
454 }
455 Self::AddForkRef { stream_id, .. } => {
456 write!(f, "add_fork_ref:{stream_id}")
457 }
458 Self::ReleaseForkRef { stream_id } => {
459 write!(f, "release_fork_ref:{stream_id}")
460 }
461 Self::FlushCold { stream_id, chunk } => {
462 write!(
463 f,
464 "flush_cold:{stream_id}:{}..{}",
465 chunk.start_offset, chunk.end_offset
466 )
467 }
468 Self::CloseStream { stream_id, .. } => {
469 write!(f, "close_stream:{stream_id}")
470 }
471 Self::DeleteStream { stream_id } => {
472 write!(f, "delete_stream:{stream_id}")
473 }
474 Self::AckColdGc { up_to_seq } => {
475 write!(f, "ack_cold_gc:up_to_seq={up_to_seq}")
476 }
477 Self::Batch { commands } => {
478 write!(f, "batch:{} commands", commands.len())
479 }
480 }
481 }
482}