1use std::collections::BTreeSet;
2use std::fmt;
3use std::future::pending;
4use std::sync::Arc;
5use std::sync::atomic::Ordering;
6use std::time::Duration;
7
8use crate::time::{Instant, SystemTime, UNIX_EPOCH, sleep};
9
10use anyhow::{Context, Result};
11use rivet_error::RivetError;
12use rivetkit_actor_persist::{generated::v4 as persist_v4, versioned as persist_versioned};
13use serde::{Deserialize, Serialize};
14#[cfg(not(target_arch = "wasm32"))]
15use tokio::runtime::{Builder, Handle};
16use tokio::sync::oneshot;
17use tokio_util::sync::CancellationToken;
18
19use crate::actor::config::ActorConfig;
20use crate::actor::context::ActorContext;
21use crate::actor::keys::{
22 QUEUE_MESSAGES_PREFIX, QUEUE_METADATA_KEY, decode_queue_message_key, make_queue_message_key,
23};
24use crate::actor::kv::APPLY_BATCH_CHUNK_SIZE;
25use crate::actor::persist::{
26 decode_latest_with_embedded_version, encode_latest_with_embedded_version,
27};
28use crate::actor::preload::PreloadedKv;
29use crate::actor::task_types::UserTaskKind;
30#[cfg(target_arch = "wasm32")]
31use crate::error::ActorRuntime;
32use crate::types::ListOpts;
33
34#[derive(Clone, Debug, Default)]
35pub struct QueueNextOpts {
36 pub names: Option<Vec<String>>,
37 pub timeout: Option<Duration>,
38 pub signal: Option<CancellationToken>,
39 pub completable: bool,
40}
41
42#[derive(Clone, Debug, Default)]
43pub struct QueueWaitOpts {
44 pub timeout: Option<Duration>,
45 pub signal: Option<CancellationToken>,
46 pub completable: bool,
47}
48
49#[derive(Clone, Debug, Default)]
50pub struct EnqueueAndWaitOpts {
51 pub timeout: Option<Duration>,
52 pub signal: Option<CancellationToken>,
53}
54
55#[derive(Clone, Debug)]
56pub struct QueueNextBatchOpts {
57 pub names: Option<Vec<String>>,
58 pub count: u32,
59 pub timeout: Option<Duration>,
60 pub signal: Option<CancellationToken>,
61 pub completable: bool,
62}
63
64impl Default for QueueNextBatchOpts {
65 fn default() -> Self {
66 Self {
67 names: None,
68 count: 1,
69 timeout: None,
70 signal: None,
71 completable: false,
72 }
73 }
74}
75
76#[derive(Clone, Debug, Default)]
77pub struct QueueTryNextOpts {
78 pub names: Option<Vec<String>>,
79 pub completable: bool,
80}
81
82#[derive(Clone, Debug)]
83pub struct QueueTryNextBatchOpts {
84 pub names: Option<Vec<String>>,
85 pub count: u32,
86 pub completable: bool,
87}
88
89impl Default for QueueTryNextBatchOpts {
90 fn default() -> Self {
91 Self {
92 names: None,
93 count: 1,
94 completable: false,
95 }
96 }
97}
98
99pub(super) type QueueWaitActivityCallback = Arc<dyn Fn() + Send + Sync>;
100pub(super) type QueueInspectorUpdateCallback = Arc<dyn Fn(u32) + Send + Sync>;
101
102#[derive(Clone, Debug)]
103pub struct QueueMessage {
104 pub id: u64,
105 pub name: String,
106 pub body: Vec<u8>,
107 pub created_at: i64,
108 completion: Option<CompletionHandle>,
109}
110
111#[derive(Clone, Debug)]
112pub struct CompletableQueueMessage {
113 pub id: u64,
114 pub name: String,
115 pub body: Vec<u8>,
116 pub created_at: i64,
117 completion: CompletionHandle,
118}
119
120#[derive(Clone)]
121struct CompletionHandle(Arc<CompletionHandleInner>);
122
123struct CompletionHandleInner {
124 ctx: ActorContext,
125 message_id: u64,
126 completed: std::sync::atomic::AtomicBool,
127}
128
129pub(super) type QueueMetadata = persist_v4::QueueMetadata;
130type PersistedQueueMessage = persist_v4::QueueMessage;
131
132fn encode_queue_metadata(metadata: &QueueMetadata) -> Result<Vec<u8>> {
133 encode_latest_with_embedded_version::<persist_versioned::QueueMetadata>(
134 metadata.clone(),
135 rivetkit_actor_persist::CURRENT_VERSION,
136 "queue metadata",
137 )
138}
139
140fn decode_queue_metadata(payload: &[u8]) -> Result<QueueMetadata> {
141 let metadata = decode_latest_with_embedded_version::<persist_versioned::QueueMetadata>(
142 payload,
143 "queue metadata",
144 )?;
145 Ok(metadata)
146}
147
148fn encode_queue_message(message: &PersistedQueueMessage) -> Result<Vec<u8>> {
149 encode_latest_with_embedded_version::<persist_versioned::QueueMessage>(
150 message.clone(),
151 rivetkit_actor_persist::CURRENT_VERSION,
152 "queue message",
153 )
154}
155
156fn decode_queue_message(payload: &[u8]) -> Result<PersistedQueueMessage> {
157 let message = decode_latest_with_embedded_version::<persist_versioned::QueueMessage>(
158 payload,
159 "queue message",
160 )?;
161 Ok(message)
162}
163
164#[derive(RivetError, Serialize, Deserialize)]
165#[error(
166 "queue",
167 "full",
168 "Queue is full",
169 "Queue is full. Limit is {limit} messages."
170)]
171struct QueueFull {
172 limit: u32,
173}
174
175#[derive(RivetError, Serialize, Deserialize)]
176#[error(
177 "queue",
178 "message_too_large",
179 "Queue message is too large",
180 "Queue message too large ({size} bytes). Limit is {limit} bytes."
181)]
182struct QueueMessageTooLarge {
183 size: usize,
184 limit: u32,
185}
186
187#[derive(RivetError)]
188#[error("queue", "already_completed", "Queue message was already completed")]
189struct QueueAlreadyCompleted;
190
191#[derive(RivetError, Serialize, Deserialize)]
192#[error(
193 "queue",
194 "complete_not_configured",
195 "Queue message does not support completion",
196 "Queue '{name}' does not support completion responses."
197)]
198struct QueueCompleteNotConfigured {
199 name: String,
200}
201
202#[derive(RivetError)]
203#[error("actor", "aborted", "Actor aborted")]
204struct QueueActorAborted;
205
206#[derive(RivetError, Serialize, Deserialize)]
207#[error(
208 "queue",
209 "timed_out",
210 "Queue wait timed out",
211 "Queue wait timed out after {timeout_ms} ms."
212)]
213struct QueueWaitTimedOut {
214 timeout_ms: u64,
215}
216
217#[derive(RivetError, Serialize, Deserialize)]
218#[error(
219 "queue",
220 "completion_waiter_conflict",
221 "Queue completion waiter conflict",
222 "Queue completion waiter is already registered for message {message_id}."
223)]
224struct QueueCompletionWaiterConflict {
225 message_id: u64,
226}
227
228#[derive(RivetError)]
229#[error(
230 "queue",
231 "completion_waiter_dropped",
232 "Queue completion waiter dropped before response"
233)]
234struct QueueCompletionWaiterDropped;
235
236impl ActorContext {
237 pub async fn send(&self, name: &str, body: &[u8]) -> Result<QueueMessage> {
238 self.enqueue_message(name, body, None).await
239 }
240
241 pub async fn enqueue_and_wait(
242 &self,
243 name: &str,
244 body: &[u8],
245 opts: EnqueueAndWaitOpts,
246 ) -> Result<Option<Vec<u8>>> {
247 let (sender, receiver) = oneshot::channel();
248 let message = self.enqueue_message(name, body, Some(sender)).await?;
249 let result = self
250 .wait_for_completion_response(message.id, receiver, opts.timeout, opts.signal.as_ref())
251 .await;
252 self.remove_completion_waiter(message.id).await;
253 result
254 }
255
256 async fn enqueue_message(
257 &self,
258 name: &str,
259 body: &[u8],
260 completion_waiter: Option<oneshot::Sender<Option<Vec<u8>>>>,
261 ) -> Result<QueueMessage> {
262 self.ensure_initialized().await?;
263
264 let created_at = current_timestamp_ms()?;
265 let persisted = PersistedQueueMessage {
266 name: name.to_owned(),
267 body: body.to_vec(),
268 created_at,
269 failure_count: None,
270 available_at: None,
271 in_flight: None,
272 in_flight_at: None,
273 };
274 let encoded_message = encode_queue_message(&persisted).context("encode queue message")?;
275 self.clear_preloaded_messages();
276
277 let config = self.config();
278 if encoded_message.len() > config.max_queue_message_size as usize {
279 return Err(QueueMessageTooLarge {
280 size: encoded_message.len(),
281 limit: config.max_queue_message_size,
282 }
283 .build());
284 }
285
286 let mut metadata = self.0.queue_metadata.lock().await;
287 if metadata.size >= config.max_queue_size {
288 return Err(QueueFull {
289 limit: config.max_queue_size,
290 }
291 .build());
292 }
293
294 let id = if metadata.next_id == 0 {
295 1
296 } else {
297 metadata.next_id
298 };
299 metadata.next_id = id.saturating_add(1);
300 metadata.size = metadata.size.saturating_add(1);
301 let encoded_metadata = encode_queue_metadata(&metadata).context("encode queue metadata")?;
302
303 let registered_completion_waiter = if let Some(waiter) = completion_waiter {
304 if self
305 .0
306 .queue_completion_waiters
307 .insert_async(id, waiter)
308 .await
309 .is_err()
310 {
311 metadata.next_id = id;
312 metadata.size = metadata.size.saturating_sub(1);
313 return Err(QueueCompletionWaiterConflict { message_id: id }.build());
314 }
315 true
316 } else {
317 false
318 };
319
320 if let Err(error) = self
321 .0
322 .kv
323 .batch_put(&[
324 (
325 make_queue_message_key(id).as_slice(),
326 encoded_message.as_slice(),
327 ),
328 (QUEUE_METADATA_KEY.as_slice(), encoded_metadata.as_slice()),
329 ])
330 .await
331 {
332 metadata.next_id = id;
333 metadata.size = metadata.size.saturating_sub(1);
334 if registered_completion_waiter {
335 self.remove_completion_waiter(id).await;
336 }
337 return Err(error).context("persist queue message");
338 }
339
340 let queue_size = metadata.size;
341 drop(metadata);
342 self.0.metrics.add_queue_messages_sent(1);
343 self.0
344 .metrics
345 .set_queue_depth(self.0.queue_metadata.lock().await.size);
346 self.notify_inspector_update(queue_size);
347 self.0.queue_notify.notify_waiters();
348
349 Ok(QueueMessage {
350 id,
351 name: name.to_owned(),
352 body: body.to_vec(),
353 created_at,
354 completion: None,
355 })
356 }
357
358 pub async fn next(&self, opts: QueueNextOpts) -> Result<Option<QueueMessage>> {
359 let mut messages = self
360 .next_batch(QueueNextBatchOpts {
361 names: opts.names,
362 count: 1,
363 timeout: opts.timeout,
364 signal: opts.signal,
365 completable: opts.completable,
366 })
367 .await?;
368 Ok(messages.pop())
369 }
370
371 pub async fn next_batch(&self, opts: QueueNextBatchOpts) -> Result<Vec<QueueMessage>> {
372 self.ensure_initialized().await?;
373
374 let count = opts.count.max(1);
375 let deadline = opts.timeout.map(|timeout| Instant::now() + timeout);
376 let names = normalize_names(opts.names);
377
378 loop {
379 let messages = self
380 .try_receive_batch(names.as_ref(), count, opts.completable)
381 .await?;
382 if !messages.is_empty() {
383 return Ok(messages);
384 }
385
386 let remaining_timeout =
387 deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
388 if matches!(remaining_timeout, Some(timeout) if timeout.is_zero()) {
389 return Ok(Vec::new());
390 }
391
392 let wait_guard = ActiveQueueWaitGuard::new(self);
393 let result = self
394 .wait_for_message(remaining_timeout, opts.signal.as_ref())
395 .await;
396 drop(wait_guard);
397
398 match result {
399 WaitOutcome::Notified => continue,
400 WaitOutcome::TimedOut => return Ok(Vec::new()),
401 WaitOutcome::Aborted => return Err(QueueActorAborted.build()),
402 }
403 }
404 }
405
406 pub async fn wait_for_names(
407 &self,
408 names: Vec<String>,
409 opts: QueueWaitOpts,
410 ) -> Result<QueueMessage> {
411 self.ensure_initialized().await?;
412
413 let deadline = opts.timeout.map(|timeout| Instant::now() + timeout);
414 let names = normalize_names(Some(names));
415
416 loop {
417 if let Some(message) = self
418 .try_receive_batch(names.as_ref(), 1, opts.completable)
419 .await?
420 .into_iter()
421 .next()
422 {
423 return Ok(message);
424 }
425
426 let remaining_timeout =
427 deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
428 if let Some(timeout) = remaining_timeout
429 && timeout.is_zero()
430 {
431 return Err(QueueWaitTimedOut {
432 timeout_ms: opts.timeout.map(duration_ms).unwrap_or(0),
433 }
434 .build());
435 }
436
437 let wait_guard = ActiveQueueWaitGuard::new(self);
438 let result = self
439 .wait_for_message(remaining_timeout, opts.signal.as_ref())
440 .await;
441 drop(wait_guard);
442
443 match result {
444 WaitOutcome::Notified => continue,
445 WaitOutcome::TimedOut => {
446 return Err(QueueWaitTimedOut {
447 timeout_ms: opts.timeout.map(duration_ms).unwrap_or(0),
448 }
449 .build());
450 }
451 WaitOutcome::Aborted => return Err(QueueActorAborted.build()),
452 }
453 }
454 }
455
456 pub async fn wait_for_names_available(
457 &self,
458 names: Vec<String>,
459 opts: QueueWaitOpts,
460 ) -> Result<()> {
461 self.ensure_initialized().await?;
462
463 let deadline = opts.timeout.map(|timeout| Instant::now() + timeout);
464 let names = normalize_names(Some(names));
465
466 loop {
467 let messages = self.list_messages().await?;
468 let has_match = if let Some(names) = names.as_ref() {
469 messages
470 .into_iter()
471 .any(|message| names.contains(&message.name))
472 } else {
473 !messages.is_empty()
474 };
475 if has_match {
476 return Ok(());
477 }
478
479 let remaining_timeout =
480 deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
481 if let Some(timeout) = remaining_timeout
482 && timeout.is_zero()
483 {
484 return Err(QueueWaitTimedOut {
485 timeout_ms: opts.timeout.map(duration_ms).unwrap_or(0),
486 }
487 .build());
488 }
489
490 let wait_guard = ActiveQueueWaitGuard::new(self);
491 let result = self
492 .wait_for_message(remaining_timeout, opts.signal.as_ref())
493 .await;
494 drop(wait_guard);
495
496 match result {
497 WaitOutcome::Notified => continue,
498 WaitOutcome::TimedOut => {
499 return Err(QueueWaitTimedOut {
500 timeout_ms: opts.timeout.map(duration_ms).unwrap_or(0),
501 }
502 .build());
503 }
504 WaitOutcome::Aborted => return Err(QueueActorAborted.build()),
505 }
506 }
507 }
508
509 pub fn try_next(&self, opts: QueueTryNextOpts) -> Result<Option<QueueMessage>> {
510 let mut messages = self.try_next_batch(QueueTryNextBatchOpts {
511 names: opts.names,
512 count: 1,
513 completable: opts.completable,
514 })?;
515 Ok(messages.pop())
516 }
517
518 pub fn try_next_batch(&self, opts: QueueTryNextBatchOpts) -> Result<Vec<QueueMessage>> {
519 self.block_on(async {
520 self.ensure_initialized().await?;
521 self.try_receive_batch(
522 normalize_names(opts.names).as_ref(),
523 opts.count.max(1),
524 opts.completable,
525 )
526 .await
527 })
528 }
529
530 pub async fn inspect_messages(&self) -> Result<Vec<QueueMessage>> {
531 self.ensure_initialized().await?;
532 self.list_messages().await
533 }
534
535 pub fn max_size(&self) -> u32 {
536 self.config().max_queue_size
537 }
538
539 pub async fn reset(&self) -> Result<()> {
541 self.ensure_initialized().await?;
542
543 let _receive_guard = self.0.queue_receive_lock.lock().await;
550
551 let mut metadata = self.0.queue_metadata.lock().await;
552
553 let entries = self.list_message_entries().await?;
557 for chunk in entries.chunks(APPLY_BATCH_CHUNK_SIZE) {
558 let key_refs: Vec<&[u8]> = chunk.iter().map(|(k, _)| k.as_slice()).collect();
559 self.0
560 .kv
561 .batch_delete(&key_refs)
562 .await
563 .context("delete all queue messages")?;
564 }
565
566 metadata.size = 0;
567 let encoded_metadata =
568 encode_queue_metadata(&metadata).context("encode reset queue metadata")?;
569 self.0
570 .kv
571 .put(&QUEUE_METADATA_KEY, &encoded_metadata)
572 .await
573 .context("persist reset queue metadata")?;
574
575 self.0.queue_completion_waiters.clear_async().await;
576
577 drop(metadata);
578
579 self.0.metrics.set_queue_depth(0);
580 self.notify_inspector_update(0);
581 self.0.queue_notify.notify_waiters();
582
583 Ok(())
584 }
585
586 pub(crate) fn configure_queue(&self, config: ActorConfig) {
587 *self.0.queue_config.lock() = config;
588 }
589
590 pub(crate) fn configure_preload(&self, preloaded_kv: Option<PreloadedKv>) {
591 *self.0.queue_preloaded_kv.lock() = preloaded_kv;
592 *self.0.queue_preloaded_message_entries.lock() = None;
593 }
594
595 pub(crate) fn set_wait_activity_callback(&self, callback: Option<Arc<dyn Fn() + Send + Sync>>) {
596 *self.0.queue_wait_activity_callback.lock() = callback;
597 }
598
599 pub(crate) fn set_inspector_update_callback(
600 &self,
601 callback: Option<Arc<dyn Fn(u32) + Send + Sync>>,
602 ) {
603 *self.0.queue_inspector_update_callback.lock() = callback;
604 }
605
606 async fn ensure_initialized(&self) -> Result<()> {
607 self.0
608 .queue_initialize
609 .get_or_try_init(|| async {
610 let preload = self.0.queue_preloaded_kv.lock().take();
611 let metadata = if let Some(preloaded) = preload.as_ref() {
612 self.configure_preloaded_messages(preloaded);
613 if let Some(metadata) = self.load_metadata_from_preload(preloaded).await? {
614 metadata
615 } else {
616 self.load_or_create_metadata().await?
617 }
618 } else {
619 self.load_or_create_metadata().await?
620 };
621 let mut state = self.0.queue_metadata.lock().await;
622 *state = metadata;
623 self.0.metrics.set_queue_depth(state.size);
624 Ok(())
625 })
626 .await
627 .map(|_| ())
628 }
629
630 fn configure_preloaded_messages(&self, preloaded: &PreloadedKv) {
631 if let Some(entries) = preloaded.prefix_entries(&QUEUE_MESSAGES_PREFIX) {
632 *self.0.queue_preloaded_message_entries.lock() = Some(entries);
633 }
634 }
635
636 async fn load_metadata_from_preload(
637 &self,
638 preloaded: &PreloadedKv,
639 ) -> Result<Option<QueueMetadata>> {
640 match preloaded.key_entry(&QUEUE_METADATA_KEY) {
641 Some(Some(encoded)) => match decode_queue_metadata(&encoded) {
642 Ok(metadata) => Ok(Some(metadata)),
643 Err(error) => {
644 tracing::warn!(
645 ?error,
646 "failed to decode preloaded queue metadata, rebuilding"
647 );
648 Ok(self.metadata_from_preloaded_messages())
649 }
650 },
651 Some(None) => Ok(self.metadata_from_preloaded_messages()),
652 None => Ok(None),
653 }
654 }
655
656 fn metadata_from_preloaded_messages(&self) -> Option<QueueMetadata> {
657 let entries = self.0.queue_preloaded_message_entries.lock().clone()?;
658 Some(metadata_from_queue_messages(decode_queue_message_entries(
659 entries,
660 )))
661 }
662
663 async fn load_or_create_metadata(&self) -> Result<QueueMetadata> {
664 let Some(encoded) = self.0.kv.get(&QUEUE_METADATA_KEY).await? else {
665 let metadata = QueueMetadata {
666 next_id: 1,
667 size: 0,
668 };
669 self.0
670 .kv
671 .put(
672 &QUEUE_METADATA_KEY,
673 &encode_queue_metadata(&metadata).context("encode default queue metadata")?,
674 )
675 .await
676 .context("persist default queue metadata")?;
677 return Ok(metadata);
678 };
679
680 match decode_queue_metadata(&encoded) {
681 Ok(metadata) => Ok(metadata),
682 Err(error) => {
683 tracing::warn!(?error, "failed to decode queue metadata, rebuilding");
684 self.rebuild_metadata().await
685 }
686 }
687 }
688
689 async fn rebuild_metadata(&self) -> Result<QueueMetadata> {
690 let messages = self.list_messages().await?;
691 let metadata = metadata_from_queue_messages(messages);
692 self.persist_metadata(&metadata)
693 .await
694 .context("persist rebuilt queue metadata")?;
695 Ok(metadata)
696 }
697
698 async fn persist_metadata(&self, metadata: &QueueMetadata) -> Result<()> {
699 let encoded = encode_queue_metadata(metadata).context("encode queue metadata")?;
700 self.0
701 .kv
702 .put(&QUEUE_METADATA_KEY, &encoded)
703 .await
704 .context("persist queue metadata")?;
705 self.notify_inspector_update(metadata.size);
706 Ok(())
707 }
708
709 async fn try_receive_batch(
710 &self,
711 names: Option<&BTreeSet<String>>,
712 count: u32,
713 completable: bool,
714 ) -> Result<Vec<QueueMessage>> {
715 let _receive_guard = self.0.queue_receive_lock.lock().await;
716
717 let messages = self.list_messages().await?;
718 let mut selected = Vec::new();
719 for message in messages {
720 if let Some(names) = names
721 && !names.contains(&message.name)
722 {
723 continue;
724 }
725
726 selected.push(message);
727 if selected.len() >= count as usize {
728 break;
729 }
730 }
731
732 if selected.is_empty() {
733 return Ok(Vec::new());
734 }
735
736 if completable {
737 let queue_size = self.0.queue_metadata.lock().await.size;
738 self.0
739 .metrics
740 .add_queue_messages_received(selected.len().try_into().unwrap_or(u64::MAX));
741 self.notify_inspector_update(queue_size);
742 return Ok(selected
743 .into_iter()
744 .map(|message| self.attach_completion(message))
745 .collect());
746 }
747
748 self.remove_messages(selected.iter().map(|message| message.id).collect())
749 .await?;
750 self.0
751 .metrics
752 .add_queue_messages_received(selected.len().try_into().unwrap_or(u64::MAX));
753
754 Ok(selected)
755 }
756
757 async fn list_messages(&self) -> Result<Vec<QueueMessage>> {
758 let messages = decode_queue_message_entries(self.list_message_entries().await?);
759
760 let actual_size = messages.len().try_into().unwrap_or(u32::MAX);
761 let mut metadata = self.0.queue_metadata.lock().await;
762 if metadata.size != actual_size {
763 metadata.size = actual_size;
764 }
765 if metadata.next_id == 0 {
766 metadata.next_id = messages
767 .last()
768 .map(|message| message.id.saturating_add(1))
769 .unwrap_or(1);
770 }
771
772 Ok(messages)
773 }
774
775 async fn list_message_entries(&self) -> Result<Vec<(Vec<u8>, Vec<u8>)>> {
776 if let Some(entries) = self.0.queue_preloaded_message_entries.lock().take() {
777 return Ok(entries);
778 }
779
780 self.0
781 .kv
782 .list_prefix(
783 &QUEUE_MESSAGES_PREFIX,
784 ListOpts {
785 reverse: false,
786 limit: None,
787 },
788 )
789 .await
790 .context("list queue messages")
791 }
792
793 fn clear_preloaded_messages(&self) {
794 self.0.queue_preloaded_message_entries.lock().take();
795 }
796
797 fn attach_completion(&self, mut message: QueueMessage) -> QueueMessage {
798 message.completion = Some(CompletionHandle::new(self.clone(), message.id));
799 message
800 }
801
802 async fn remove_messages(&self, message_ids: Vec<u64>) -> Result<()> {
803 if message_ids.is_empty() {
804 return Ok(());
805 }
806
807 let keys: Vec<Vec<u8>> = message_ids
808 .into_iter()
809 .map(make_queue_message_key)
810 .collect();
811 let key_refs: Vec<&[u8]> = keys.iter().map(Vec::as_slice).collect();
812
813 self.0
814 .kv
815 .batch_delete(&key_refs)
816 .await
817 .context("delete queue messages")?;
818
819 let encoded_metadata = {
820 let mut metadata = self.0.queue_metadata.lock().await;
821 metadata.size = metadata.size.saturating_sub(key_refs.len() as u32);
822 let queue_size = metadata.size;
823 encode_queue_metadata(&metadata)
824 .context("encode queue metadata after delete")
825 .map(|encoded| (encoded, queue_size))?
826 };
827 let (encoded_metadata, queue_size) = encoded_metadata;
828
829 self.0
830 .kv
831 .put(&QUEUE_METADATA_KEY, &encoded_metadata)
832 .await
833 .context("persist queue metadata after delete")?;
834 self.0
835 .metrics
836 .set_queue_depth(self.0.queue_metadata.lock().await.size);
837 self.notify_inspector_update(queue_size);
838 Ok(())
839 }
840
841 async fn complete_message_by_id(
842 &self,
843 message_id: u64,
844 response: Option<Vec<u8>>,
845 ) -> Result<()> {
846 self.remove_messages(vec![message_id]).await?;
847 if let Some(waiter) = self.remove_completion_waiter(message_id).await {
848 let _ = waiter.send(response);
849 }
850 Ok(())
851 }
852
853 async fn remove_completion_waiter(
854 &self,
855 message_id: u64,
856 ) -> Option<oneshot::Sender<Option<Vec<u8>>>> {
857 self.0
858 .queue_completion_waiters
859 .remove_async(&message_id)
860 .await
861 .map(|(_, waiter)| waiter)
862 }
863
864 async fn wait_for_message(
865 &self,
866 timeout: Option<Duration>,
867 signal: Option<&CancellationToken>,
868 ) -> WaitOutcome {
869 let actor_abort_signal = self.0.queue_abort_signal.lock().clone();
870 if signal.is_some_and(CancellationToken::is_cancelled) {
871 return WaitOutcome::Aborted;
872 }
873 if actor_abort_signal.is_cancelled() {
874 return WaitOutcome::Aborted;
875 }
876
877 let notified = self.0.queue_notify.notified();
878 let actor_aborted = async {
879 actor_abort_signal.cancelled().await;
880 };
881 let external_aborted = async {
882 if let Some(signal) = signal {
883 signal.cancelled().await;
884 } else {
885 pending::<()>().await;
886 }
887 };
888
889 match timeout {
890 Some(timeout) => {
891 tokio::select! {
892 _ = notified => WaitOutcome::Notified,
893 _ = actor_aborted => WaitOutcome::Aborted,
894 _ = external_aborted => WaitOutcome::Aborted,
895 _ = sleep(timeout) => WaitOutcome::TimedOut,
896 }
897 }
898 None => {
899 tokio::select! {
900 _ = notified => WaitOutcome::Notified,
901 _ = actor_aborted => WaitOutcome::Aborted,
902 _ = external_aborted => WaitOutcome::Aborted,
903 }
904 }
905 }
906 }
907
908 async fn wait_for_completion_response(
912 &self,
913 message_id: u64,
914 mut receiver: oneshot::Receiver<Option<Vec<u8>>>,
915 timeout: Option<Duration>,
916 signal: Option<&CancellationToken>,
917 ) -> Result<Option<Vec<u8>>> {
918 if signal.is_some_and(CancellationToken::is_cancelled) {
919 return Err(QueueActorAborted.build());
920 }
921
922 let external_aborted = async {
923 if let Some(signal) = signal {
924 signal.cancelled().await;
925 } else {
926 pending::<()>().await;
927 }
928 };
929
930 let wait_result = match timeout {
931 Some(timeout) => {
932 tokio::select! {
933 response = &mut receiver => CompletionWaitOutcome::Response(response),
934 _ = external_aborted => CompletionWaitOutcome::Aborted,
935 _ = sleep(timeout) => CompletionWaitOutcome::TimedOut,
936 }
937 }
938 None => {
939 tokio::select! {
940 response = &mut receiver => CompletionWaitOutcome::Response(response),
941 _ = external_aborted => CompletionWaitOutcome::Aborted,
942 }
943 }
944 };
945
946 match wait_result {
947 CompletionWaitOutcome::Response(Ok(response)) => Ok(response),
948 CompletionWaitOutcome::Response(Err(_)) => Err(QueueCompletionWaiterDropped.build())
949 .context(format!("wait for queue completion on message {message_id}")),
950 CompletionWaitOutcome::TimedOut => Err(QueueWaitTimedOut {
951 timeout_ms: timeout.map(duration_ms).unwrap_or(0),
952 }
953 .build()),
954 CompletionWaitOutcome::Aborted => Err(QueueActorAborted.build()),
955 }
956 }
957
958 fn block_on<T>(&self, future: impl std::future::Future<Output = Result<T>>) -> Result<T> {
959 #[cfg(not(target_arch = "wasm32"))]
960 {
961 if let Ok(handle) = Handle::try_current() {
962 tokio::task::block_in_place(|| handle.block_on(future))
963 } else {
964 Builder::new_current_thread()
965 .enable_all()
966 .build()
967 .context("build temporary runtime for queue operation")?
968 .block_on(future)
969 }
970 }
971
972 #[cfg(target_arch = "wasm32")]
973 {
974 drop(future);
975 Err(ActorRuntime::InvalidOperation {
976 operation: "queue.try_next_batch".to_owned(),
977 reason: "synchronous queue receive requires native runtime support".to_owned(),
978 }
979 .build())
980 }
981 }
982
983 fn config(&self) -> ActorConfig {
984 self.0.queue_config.lock().clone()
985 }
986
987 #[cfg(test)]
988 pub(crate) fn queue_config_for_tests(&self) -> ActorConfig {
989 self.config()
990 }
991
992 fn notify_wait_activity(&self) {
993 if let Some(callback) = self.0.queue_wait_activity_callback.lock().clone() {
994 callback();
995 }
996 }
997
998 fn notify_inspector_update(&self, queue_size: u32) {
999 if let Some(callback) = self.0.queue_inspector_update_callback.lock().clone() {
1000 callback(queue_size);
1001 }
1002 }
1003}
1004
1005impl QueueMessage {
1006 pub async fn complete(self, response: Option<Vec<u8>>) -> Result<()> {
1007 let completable = self.into_completable()?;
1008 completable.complete(response).await
1009 }
1010
1011 pub fn into_completable(self) -> Result<CompletableQueueMessage> {
1012 let completion = self.completion.clone().ok_or_else(|| {
1013 QueueCompleteNotConfigured {
1014 name: self.name.clone(),
1015 }
1016 .build()
1017 })?;
1018
1019 Ok(CompletableQueueMessage {
1020 id: self.id,
1021 name: self.name,
1022 body: self.body,
1023 created_at: self.created_at,
1024 completion,
1025 })
1026 }
1027
1028 pub fn is_completable(&self) -> bool {
1029 self.completion.is_some()
1030 }
1031}
1032
1033impl CompletableQueueMessage {
1034 pub async fn complete(self, response: Option<Vec<u8>>) -> Result<()> {
1035 self.completion.complete(response).await
1036 }
1037
1038 pub fn into_message(self) -> QueueMessage {
1039 QueueMessage {
1040 id: self.id,
1041 name: self.name,
1042 body: self.body,
1043 created_at: self.created_at,
1044 completion: Some(self.completion),
1045 }
1046 }
1047}
1048
1049impl CompletionHandle {
1050 fn new(ctx: ActorContext, message_id: u64) -> Self {
1051 Self(Arc::new(CompletionHandleInner {
1052 ctx,
1053 message_id,
1054 completed: std::sync::atomic::AtomicBool::new(false),
1055 }))
1056 }
1057
1058 async fn complete(&self, response: Option<Vec<u8>>) -> Result<()> {
1059 if self.0.completed.swap(true, Ordering::SeqCst) {
1060 return Err(QueueAlreadyCompleted.build());
1061 }
1062
1063 if let Err(error) = self
1064 .0
1065 .ctx
1066 .complete_message_by_id(self.0.message_id, response)
1067 .await
1068 {
1069 self.0.completed.store(false, Ordering::SeqCst);
1070 return Err(error);
1071 }
1072
1073 Ok(())
1074 }
1075}
1076
1077impl fmt::Debug for CompletionHandle {
1078 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1079 f.debug_struct("CompletionHandle")
1080 .field("message_id", &self.0.message_id)
1081 .field("completed", &self.0.completed.load(Ordering::SeqCst))
1082 .finish()
1083 }
1084}
1085
1086struct ActiveQueueWaitGuard<'a> {
1087 ctx: &'a ActorContext,
1088 started_at: Instant,
1089}
1090
1091impl<'a> ActiveQueueWaitGuard<'a> {
1092 fn new(ctx: &'a ActorContext) -> Self {
1093 ctx.0.active_queue_wait_count.fetch_add(1, Ordering::SeqCst);
1094 ctx.0.metrics.begin_user_task(UserTaskKind::QueueWait);
1095 ctx.notify_wait_activity();
1096 Self {
1097 ctx,
1098 started_at: Instant::now(),
1099 }
1100 }
1101}
1102
1103impl Drop for ActiveQueueWaitGuard<'_> {
1104 fn drop(&mut self) {
1105 self.ctx
1106 .0
1107 .metrics
1108 .end_user_task(UserTaskKind::QueueWait, self.started_at.elapsed());
1109 let previous = self
1110 .ctx
1111 .0
1112 .active_queue_wait_count
1113 .fetch_sub(1, Ordering::SeqCst);
1114 if previous == 0 {
1115 self.ctx
1116 .0
1117 .active_queue_wait_count
1118 .store(0, Ordering::SeqCst);
1119 }
1120 self.ctx.notify_wait_activity();
1121 }
1122}
1123
1124enum WaitOutcome {
1125 Notified,
1126 TimedOut,
1127 Aborted,
1128}
1129
1130enum CompletionWaitOutcome {
1131 Response(Result<Option<Vec<u8>>, oneshot::error::RecvError>),
1132 TimedOut,
1133 Aborted,
1134}
1135
1136fn normalize_names(names: Option<Vec<String>>) -> Option<BTreeSet<String>> {
1137 names.and_then(|names| {
1138 let normalized = names.into_iter().collect::<BTreeSet<_>>();
1139 if normalized.is_empty() {
1140 None
1141 } else {
1142 Some(normalized)
1143 }
1144 })
1145}
1146
1147fn decode_queue_message_entries(entries: Vec<(Vec<u8>, Vec<u8>)>) -> Vec<QueueMessage> {
1148 let mut messages = Vec::with_capacity(entries.len());
1149 for (key, value) in entries {
1150 let id = match decode_queue_message_key(&key) {
1151 Ok(id) => id,
1152 Err(error) => {
1153 tracing::warn!(?error, "failed to decode queue message key");
1154 continue;
1155 }
1156 };
1157
1158 match decode_queue_message(&value) {
1159 Ok(message) => messages.push(QueueMessage {
1160 id,
1161 name: message.name,
1162 body: message.body,
1163 created_at: message.created_at,
1164 completion: None,
1165 }),
1166 Err(error) => {
1167 tracing::warn!(
1168 ?error,
1169 queue_message_id = id,
1170 "failed to decode queue message"
1171 );
1172 }
1173 }
1174 }
1175
1176 messages.sort_by_key(|message| message.id);
1177 messages
1178}
1179
1180fn metadata_from_queue_messages(messages: Vec<QueueMessage>) -> QueueMetadata {
1181 let next_id = messages
1182 .last()
1183 .map(|message| message.id.saturating_add(1))
1184 .unwrap_or(1);
1185 QueueMetadata {
1186 next_id,
1187 size: messages.len().try_into().unwrap_or(u32::MAX),
1188 }
1189}
1190
1191fn current_timestamp_ms() -> Result<i64> {
1192 let now = SystemTime::now()
1193 .duration_since(UNIX_EPOCH)
1194 .context("current time is before unix epoch")?;
1195 i64::try_from(now.as_millis()).context("queue timestamp exceeds i64")
1196}
1197
1198fn duration_ms(duration: Duration) -> u64 {
1199 duration.as_millis().try_into().unwrap_or(u64::MAX)
1200}
1201
1202#[cfg(test)]
1204#[path = "../../tests/queue.rs"]
1205mod tests;