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