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