1use std::collections::HashMap;
2use std::sync::Arc;
3#[cfg(not(target_arch = "wasm32"))]
4use std::sync::OnceLock;
5use std::sync::atomic::Ordering;
6
7#[cfg(not(target_arch = "wasm32"))]
8use parking_lot::Mutex;
9
10use crate::async_counter::AsyncCounter;
11use rivet_envoy_protocol as protocol;
12use tokio::sync::mpsc;
13use tokio::sync::oneshot;
14use tracing::Instrument;
15
16use crate::actor::ToActor;
17use crate::commands::{ACK_COMMANDS_INTERVAL_MS, handle_commands, send_command_ack};
18use crate::config::EnvoyConfig;
19use crate::connection::{start_connection, ws_send};
20use crate::context::{SharedContext, WsTxMessage};
21use crate::events::{handle_ack_events, handle_send_events, resend_unacknowledged_events};
22use crate::handle::EnvoyHandle;
23use crate::kv::{
24 KV_CLEANUP_INTERVAL_MS, KvRequestEntry, cleanup_old_kv_requests, handle_kv_request,
25 handle_kv_response, process_unsent_kv_requests,
26};
27use crate::metrics::METRICS;
28use crate::sqlite::{
29 RemoteSqliteRequest, RemoteSqliteRequestEntry, RemoteSqliteResponseEnvelope, SqliteRequest,
30 SqliteRequestEntry, SqliteResponse, cleanup_old_remote_sqlite_requests,
31 cleanup_old_sqlite_requests, fail_remote_sqlite_requests_with_shutdown,
32 fail_sent_remote_sqlite_requests_with_indeterminate_result,
33 fail_sent_sqlite_requests_with_indeterminate_result, fail_sqlite_requests_with_shutdown,
34 handle_remote_sqlite_exec_response, handle_remote_sqlite_execute_batch_response,
35 handle_remote_sqlite_execute_response, handle_remote_sqlite_request,
36 handle_sqlite_commit_finalize_response, handle_sqlite_commit_response,
37 handle_sqlite_commit_stage_begin_response, handle_sqlite_commit_stage_segment_response,
38 handle_sqlite_get_pages_response, handle_sqlite_request, process_unsent_remote_sqlite_requests,
39 process_unsent_sqlite_requests,
40};
41use crate::tunnel::{
42 HttpRequestCancellationKey, handle_tunnel_message, make_ws_key,
43 resend_buffered_tunnel_messages, send_hibernatable_ws_message_ack,
44};
45use crate::utils::{BufferMap, EnvoyShutdownError, SleepFuture, boxed_sleep, spawn_detached};
46
47#[cfg(not(target_arch = "wasm32"))]
51static GLOBAL_ENVOY: OnceLock<Mutex<Option<EnvoyHandle>>> = OnceLock::new();
52
53pub struct EnvoyContext {
54 pub shared: Arc<SharedContext>,
55 pub shutting_down: bool,
56 pub actors: HashMap<String, HashMap<u32, ActorEntry>>,
57 pub pending_events: HashMap<(String, u32), PendingActorEvents>,
58 pub buffered_actor_messages: HashMap<String, Vec<BufferedActorMessage>>,
59 pub kv_requests: HashMap<u32, KvRequestEntry>,
60 pub next_kv_request_id: u32,
61 pub sqlite_requests: HashMap<u32, SqliteRequestEntry>,
62 pub next_sqlite_request_id: u32,
63 pub remote_sqlite_requests: HashMap<u32, RemoteSqliteRequestEntry>,
64 pub next_remote_sqlite_request_id: u32,
65 pub request_to_actor: BufferMap<WebSocketRoute>,
66 pub http_request_routes: BufferMap<HttpRequestRoute>,
67 pub http_message_indices: BufferMap<protocol::MessageIndex>,
68 pub http_request_cancellations: HashMap<HttpRequestCancellationKey, crate::time::Instant>,
71 pub buffered_messages: Vec<protocol::ToRivetTunnelMessage>,
72 pub processed_command_idx: HashMap<(String, u32), i64>,
77}
78
79pub struct HttpRequestRoute {
80 pub actor_id: String,
81 pub actor_generation: Option<u32>,
82 pub actor_admitted: bool,
83 pub session: u64,
84 pub gateway_id: protocol::GatewayId,
85 pub request_id: protocol::RequestId,
86}
87
88#[derive(Clone)]
89pub struct WebSocketRoute {
90 pub actor_id: String,
91 pub actor_generation: Option<u32>,
92}
93
94pub struct ActorEntry {
95 pub handle: mpsc::UnboundedSender<ToActor>,
96 pub active_http_request_count: Arc<AsyncCounter>,
97 pub name: String,
98 pub last_command_idx: i64,
99 pub received_stop: bool,
100}
101
102#[derive(Default)]
103pub struct PendingActorEvents {
104 pub events: Vec<protocol::EventWrapper>,
105 pub retired_at: Option<crate::time::Instant>,
106}
107
108pub enum BufferedActorMessage {
109 WsMsg {
110 message_id: protocol::MessageId,
111 msg: protocol::ToEnvoyWebSocketMessage,
112 },
113 WsClose {
114 message_id: protocol::MessageId,
115 close: protocol::ToEnvoyWebSocketClose,
116 },
117}
118
119pub enum ToEnvoyMessage {
120 ConnMessage {
121 message: protocol::ToEnvoy,
122 session: u64,
123 },
124 ConnClose {
125 evict: bool,
126 session: u64,
127 },
128 SendEvents {
129 events: Vec<protocol::EventWrapper>,
130 },
131 KvRequest {
132 actor_id: String,
133 data: protocol::KvRequestData,
134 response_tx: oneshot::Sender<anyhow::Result<protocol::KvResponseData>>,
135 },
136 SqliteRequest {
137 request: SqliteRequest,
138 response_tx: oneshot::Sender<anyhow::Result<SqliteResponse>>,
139 },
140 RemoteSqliteRequest {
141 request: RemoteSqliteRequest,
142 expected_session: Option<u64>,
143 response_tx: oneshot::Sender<anyhow::Result<RemoteSqliteResponseEnvelope>>,
144 },
145 SendOrBufferTunnelMsg {
146 msg: protocol::ToRivetTunnelMessage,
147 },
148 ActorIntent {
149 actor_id: String,
150 generation: Option<u32>,
151 intent: protocol::ActorIntent,
152 error: Option<String>,
153 },
154 SetAlarm {
155 actor_id: String,
156 generation: Option<u32>,
157 alarm_ts: Option<i64>,
158 ack_tx: Option<oneshot::Sender<()>>,
159 },
160 HwsAck {
161 gateway_id: protocol::GatewayId,
162 request_id: protocol::RequestId,
163 envoy_message_index: u16,
164 },
165 RebindWebSocket {
166 actor_id: String,
167 generation: u32,
168 gateway_id: protocol::GatewayId,
169 request_id: protocol::RequestId,
170 response_tx: oneshot::Sender<bool>,
171 },
172 HttpRequestComplete {
173 gateway_id: protocol::GatewayId,
174 request_id: protocol::RequestId,
175 },
176 GetActor {
177 actor_id: String,
178 generation: Option<u32>,
179 response_tx: oneshot::Sender<Option<ActorInfo>>,
180 },
181 Shutdown,
182 Stop,
183}
184
185#[derive(Clone)]
187pub struct ActorInfo {
188 pub name: String,
189 pub generation: u32,
190 pub active_http_request_count: Arc<AsyncCounter>,
191}
192
193impl EnvoyContext {
194 pub(crate) fn rebind_websocket(
195 &mut self,
196 actor_id: &str,
197 generation: u32,
198 gateway_id: &protocol::GatewayId,
199 request_id: &protocol::RequestId,
200 ) -> bool {
201 if let Some(route) = self.request_to_actor.get_mut(&[gateway_id, request_id]) {
202 if route.actor_id != actor_id {
203 return false;
204 }
205 if route.actor_generation.is_some() {
206 route.actor_generation = Some(generation);
207 }
208 } else {
209 self.request_to_actor.insert(
213 &[gateway_id, request_id],
214 WebSocketRoute {
215 actor_id: actor_id.to_owned(),
216 actor_generation: Some(generation),
217 },
218 );
219 }
220
221 self.shared
222 .live_tunnel_requests
223 .lock()
224 .expect("shared live tunnel request registry poisoned")
225 .insert(make_ws_key(gateway_id, request_id), actor_id.to_owned());
226 true
227 }
228
229 pub fn insert_actor(
230 &mut self,
231 actor_id: String,
232 generation: u32,
233 handle: mpsc::UnboundedSender<ToActor>,
234 active_http_request_count: Arc<AsyncCounter>,
235 name: String,
236 last_command_idx: i64,
237 ) {
238 let buffered_actor_id = actor_id.clone();
239 let buffered_handle = handle.clone();
240 self.actors
241 .entry(actor_id.clone())
242 .or_insert_with(HashMap::new)
243 .insert(
244 generation,
245 ActorEntry {
246 handle: handle.clone(),
247 active_http_request_count: active_http_request_count.clone(),
248 name,
249 last_command_idx,
250 received_stop: false,
251 },
252 );
253 self.shared
254 .actors
255 .lock()
256 .expect("shared actor registry poisoned")
257 .entry(actor_id)
258 .or_insert_with(HashMap::new)
259 .insert(
260 generation,
261 crate::context::SharedActorEntry {
262 handle,
263 active_http_request_count,
264 },
265 );
266
267 self.shared.actors_notify.notify_waiters();
268
269 if let Some(messages) = self.buffered_actor_messages.remove(&buffered_actor_id) {
270 for message in messages {
271 match message {
272 BufferedActorMessage::WsMsg { message_id, msg } => {
273 let _ = buffered_handle.send(ToActor::WsMsg { message_id, msg });
274 }
275 BufferedActorMessage::WsClose { message_id, close } => {
276 let _ = buffered_handle.send(ToActor::WsClose { message_id, close });
277 }
278 }
279 }
280 }
281 }
282
283 pub fn remove_actor(&mut self, actor_id: &str, generation: u32) {
284 if let Some(generations) = self.actors.get_mut(actor_id) {
285 generations.remove(&generation);
286 if generations.is_empty() {
287 self.actors.remove(actor_id);
288 }
289 }
290
291 let mut shared = self
292 .shared
293 .actors
294 .lock()
295 .expect("shared actor registry poisoned");
296 if let Some(generations) = shared.get_mut(actor_id) {
297 generations.remove(&generation);
298 if generations.is_empty() {
299 shared.remove(actor_id);
300 }
301 }
302 self.shared.actors_notify.notify_waiters();
303 }
304
305 pub fn get_actor(&self, actor_id: &str, generation: Option<u32>) -> Option<&ActorEntry> {
306 let gens = self.actors.get(actor_id)?;
307 if gens.is_empty() {
308 return None;
309 }
310
311 if let Some(g) = generation {
312 return gens.get(&g).filter(|entry| !entry.handle.is_closed());
313 }
314
315 let mut best: Option<&ActorEntry> = None;
318 let mut best_gen: u32 = 0;
319 for (&g, entry) in gens {
320 if !entry.handle.is_closed() && (best.is_none() || g > best_gen) {
321 best = Some(entry);
322 best_gen = g;
323 }
324 }
325 best
326 }
327
328 pub fn get_actor_for_admission(
331 &self,
332 actor_id: &str,
333 generation: Option<u32>,
334 ) -> Option<&ActorEntry> {
335 let actor = self.get_actor(actor_id, generation)?;
336 if generation.is_some() && actor.received_stop {
337 None
338 } else {
339 Some(actor)
340 }
341 }
342
343 pub fn get_actor_entry_mut(
344 &mut self,
345 actor_id: &str,
346 generation: u32,
347 ) -> Option<&mut ActorEntry> {
348 self.actors
349 .get_mut(actor_id)
350 .and_then(|gens| gens.get_mut(&generation))
351 }
352}
353
354pub async fn start_envoy(config: EnvoyConfig) -> EnvoyHandle {
355 let handle = start_envoy_sync(config);
356 handle
357 .started()
358 .await
359 .expect("envoy failed to start before returning handle");
360 handle
361}
362
363pub fn start_envoy_sync(config: EnvoyConfig) -> EnvoyHandle {
364 #[cfg(target_arch = "wasm32")]
365 {
366 start_envoy_sync_inner(config)
367 }
368
369 #[cfg(not(target_arch = "wasm32"))]
370 {
371 if config.not_global {
372 return start_envoy_sync_inner(config);
373 }
374
375 let slot = GLOBAL_ENVOY.get_or_init(|| Mutex::new(None));
376 let mut guard = slot.lock();
377 if let Some(handle) = guard.as_ref() {
378 if !handle.is_stopped() {
379 return handle.clone();
380 }
381 }
382 let handle = start_envoy_sync_inner(config);
383 *guard = Some(handle.clone());
384 handle
385 }
386}
387
388fn start_envoy_sync_inner(config: EnvoyConfig) -> EnvoyHandle {
389 let (envoy_tx, envoy_rx) = mpsc::unbounded_channel::<ToEnvoyMessage>();
390 let (start_tx, start_rx) = tokio::sync::watch::channel(());
391 let (stopped_tx, _stopped_rx) = tokio::sync::watch::channel(false);
392 let (connection_session_tx, _connection_session_rx) = tokio::sync::watch::channel(0);
393
394 let envoy_key = uuid::Uuid::new_v4().to_string();
395 let shared = Arc::new(SharedContext {
396 config,
397 envoy_key,
398 envoy_tx: envoy_tx.clone(),
399 actors: Arc::new(std::sync::Mutex::new(HashMap::new())),
400 actors_notify: Arc::new(tokio::sync::Notify::new()),
401 live_tunnel_requests: Arc::new(std::sync::Mutex::new(HashMap::new())),
402 pending_hibernation_restores: Arc::new(std::sync::Mutex::new(HashMap::new())),
403 ws_tx: Arc::new(tokio::sync::Mutex::new(None)),
404 http_ws_tx: Arc::new(tokio::sync::Mutex::new(None)),
405 connection_session: std::sync::atomic::AtomicU64::new(0),
406 next_connection_session: std::sync::atomic::AtomicU64::new(0),
407 connection_session_tx,
408 protocol_metadata: Arc::new(tokio::sync::Mutex::new(None)),
409 shutting_down: std::sync::atomic::AtomicBool::new(false),
410 last_ping_ts: std::sync::atomic::AtomicI64::new(0),
411 stopped_tx,
412 });
413
414 let handle = EnvoyHandle {
415 shared: shared.clone(),
416 started_rx: start_rx,
417 };
418
419 start_connection(shared.clone());
420
421 let ctx = EnvoyContext {
422 shared: shared.clone(),
423 shutting_down: false,
424 actors: HashMap::new(),
425 pending_events: HashMap::new(),
426 buffered_actor_messages: HashMap::new(),
427 kv_requests: HashMap::new(),
428 next_kv_request_id: 0,
429 sqlite_requests: HashMap::new(),
430 next_sqlite_request_id: 0,
431 remote_sqlite_requests: HashMap::new(),
432 next_remote_sqlite_request_id: 0,
433 request_to_actor: BufferMap::new(),
434 http_request_routes: BufferMap::new(),
435 http_message_indices: BufferMap::new(),
436 http_request_cancellations: HashMap::new(),
437 buffered_messages: Vec::new(),
438 processed_command_idx: HashMap::new(),
439 };
440
441 tracing::info!(envoy_key = %shared.envoy_key, "starting envoy");
442 let span = tracing::info_span!("envoy_client", envoy_key = %shared.envoy_key);
443 spawn_detached(envoy_loop(ctx, envoy_rx, start_tx).instrument(span));
444
445 handle
446}
447
448async fn envoy_loop(
449 mut ctx: EnvoyContext,
450 mut rx: mpsc::UnboundedReceiver<ToEnvoyMessage>,
451 start_tx: tokio::sync::watch::Sender<()>,
452) {
453 let mut ack_tick = boxed_sleep(std::time::Duration::from_millis(ACK_COMMANDS_INTERVAL_MS));
454 let mut kv_cleanup_tick = boxed_sleep(std::time::Duration::from_millis(KV_CLEANUP_INTERVAL_MS));
455
456 let mut lost_timeout: Option<SleepFuture> = None;
457
458 loop {
459 let iter_start = crate::time::Instant::now();
460 #[allow(unused_assignments)]
461 let mut branch: &'static str = "unknown";
462 tokio::select! {
463 msg = rx.recv() => {
464 branch = "envoy_msg";
465 let Some(msg) = msg else {
466 observe_envoy_loop_iteration(branch, iter_start);
467 break;
468 };
469 METRICS.envoy_tx_depth.dec();
470
471 match msg {
472 ToEnvoyMessage::ConnMessage { message, session } => {
473 lost_timeout = handle_conn_message(&mut ctx, &start_tx, lost_timeout, message, session).await;
474 }
475 ToEnvoyMessage::ConnClose { evict, session } => {
476 remove_http_routes_for_session(&mut ctx, session);
477 for generations in ctx.actors.values() {
478 for actor in generations.values() {
479 let _ = actor.handle.send(ToActor::ConnectionClosed { session });
480 }
481 }
482 fail_sent_remote_sqlite_requests_with_indeterminate_result(&mut ctx);
483 fail_sent_sqlite_requests_with_indeterminate_result(&mut ctx);
484 lost_timeout = handle_conn_close(&ctx, lost_timeout);
485 if evict {
486 observe_envoy_loop_iteration(branch, iter_start);
487 break;
488 }
489 }
490 ToEnvoyMessage::SendEvents { events } => {
491 handle_send_events(&mut ctx, events).await;
492 }
493 ToEnvoyMessage::KvRequest { actor_id, data, response_tx } => {
494 handle_kv_request(&mut ctx, actor_id, data, response_tx).await;
495 }
496 ToEnvoyMessage::SqliteRequest { request, response_tx } => {
497 handle_sqlite_request(&mut ctx, request, response_tx).await;
498 }
499 ToEnvoyMessage::RemoteSqliteRequest { request, expected_session, response_tx } => {
500 handle_remote_sqlite_request(&mut ctx, request, expected_session, response_tx).await;
501 }
502 ToEnvoyMessage::SendOrBufferTunnelMsg { msg } => {
503 crate::tunnel::send_or_buffer_tunnel_message(&mut ctx, msg).await;
504 }
505 ToEnvoyMessage::ActorIntent { actor_id, generation, intent, error } => {
506 if let Some(entry) = ctx.get_actor(&actor_id, generation) {
507 let _ = entry.handle.send(ToActor::Intent { intent, error });
508 }
509 }
510 ToEnvoyMessage::SetAlarm { actor_id, generation, alarm_ts, ack_tx } => {
511 if let Some(entry) = ctx.get_actor(&actor_id, generation) {
512 if let Err(error) = entry.handle.send(ToActor::SetAlarm { alarm_ts, ack_tx }) {
513 if let ToActor::SetAlarm { ack_tx: Some(ack_tx), .. } = error.0 {
514 let _ = ack_tx.send(());
515 }
516 }
517 } else if let Some(ack_tx) = ack_tx {
518 let _ = ack_tx.send(());
519 }
520 }
521 ToEnvoyMessage::HwsAck { gateway_id, request_id, envoy_message_index } => {
522 send_hibernatable_ws_message_ack(&mut ctx, gateway_id, request_id, envoy_message_index);
523 }
524 ToEnvoyMessage::RebindWebSocket { actor_id, generation, gateway_id, request_id, response_tx } => {
525 let rebound = ctx.rebind_websocket(
526 &actor_id,
527 generation,
528 &gateway_id,
529 &request_id,
530 );
531 let _ = response_tx.send(rebound);
532 }
533 ToEnvoyMessage::HttpRequestComplete { gateway_id, request_id } => {
534 ctx.http_request_routes.remove(&[&gateway_id, &request_id]);
535 ctx.http_message_indices.remove(&[&gateway_id, &request_id]);
536 }
537 ToEnvoyMessage::GetActor { actor_id, generation, response_tx } => {
538 let info = ctx.get_actor(&actor_id, generation).map(|entry| {
539 let actor_gen = generation.unwrap_or_else(|| {
540 ctx.actors
541 .get(&actor_id)
542 .and_then(|gens| {
543 gens.iter()
544 .filter(|(_, e)| !e.handle.is_closed())
545 .map(|(&g, _)| g)
546 .max()
547 })
548 .unwrap_or(0)
549 });
550 ActorInfo {
551 name: entry.name.clone(),
552 generation: actor_gen,
553 active_http_request_count: entry
554 .active_http_request_count
555 .clone(),
556 }
557 });
558 let _ = response_tx.send(info);
559 }
560 ToEnvoyMessage::Shutdown => {
561 handle_shutdown(&mut ctx).await;
562 }
563 ToEnvoyMessage::Stop => {
564 observe_envoy_loop_iteration(branch, iter_start);
565 break;
566 }
567 }
568 }
569 _ = ack_tick.as_mut() => {
570 branch = "ack_tick";
571 send_command_ack(&mut ctx).await;
572 ack_tick = boxed_sleep(std::time::Duration::from_millis(ACK_COMMANDS_INTERVAL_MS));
573 }
574 _ = kv_cleanup_tick.as_mut() => {
575 branch = "cleanup_tick";
576 cleanup_old_kv_requests(&mut ctx);
577 cleanup_old_sqlite_requests(&mut ctx);
578 cleanup_old_remote_sqlite_requests(&mut ctx);
579 crate::events::cleanup_expired_terminal_events(&mut ctx);
580 kv_cleanup_tick = boxed_sleep(std::time::Duration::from_millis(KV_CLEANUP_INTERVAL_MS));
581 }
582 _ = async {
583 match lost_timeout.as_mut() {
584 Some(timeout) => timeout.as_mut().await,
585 None => std::future::pending::<()>().await,
586 }
587 } => {
588 branch = "lost_timeout";
589 for (_id, request) in ctx.kv_requests.drain() {
591 METRICS.kv_requests_inflight.dec();
592 let _ = request.response_tx.send(Err(anyhow::anyhow!(EnvoyShutdownError)));
593 }
594 fail_sqlite_requests_with_shutdown(&mut ctx);
595 fail_remote_sqlite_requests_with_shutdown(&mut ctx);
596
597 if !ctx.actors.is_empty() {
598 tracing::warn!("stopping all actors due to envoy lost threshold");
599 for (_actor_id, gens) in &ctx.actors {
600 for (_g, entry) in gens {
601 if !entry.handle.is_closed() {
602 let _ = entry.handle.send(ToActor::Lost);
603 }
604 }
605 }
606 ctx.actors.clear();
607 ctx.shared
608 .actors
609 .lock()
610 .expect("shared actor registry poisoned")
611 .clear();
612 }
613
614 lost_timeout = None;
615 }
616 }
617 observe_envoy_loop_iteration(branch, iter_start);
618 }
619
620 {
622 let guard = ctx.shared.ws_tx.lock().await;
623 if let Some(tx) = guard.as_ref() {
624 let _ = tx.send(WsTxMessage::Close);
625 }
626 }
627
628 for (_id, request) in ctx.kv_requests.drain() {
629 METRICS.kv_requests_inflight.dec();
630 let _ = request
631 .response_tx
632 .send(Err(anyhow::anyhow!("envoy shutting down")));
633 }
634 fail_sqlite_requests_with_shutdown(&mut ctx);
635 fail_remote_sqlite_requests_with_shutdown(&mut ctx);
636
637 ctx.actors.clear();
638 ctx.shared
639 .actors
640 .lock()
641 .expect("shared actor registry poisoned")
642 .clear();
643
644 tracing::info!("envoy stopped");
645
646 ctx.shared.config.callbacks.on_shutdown();
647
648 let _ = ctx.shared.stopped_tx.send(true);
652}
653
654pub(crate) fn remove_http_routes_for_session(ctx: &mut EnvoyContext, session: u64) {
655 let closed_routes = ctx
656 .http_request_routes
657 .remove_where(|route| route.session == session);
658 for route in closed_routes {
659 ctx.http_message_indices
660 .remove(&[&route.gateway_id, &route.request_id]);
661 }
662}
663
664fn observe_envoy_loop_iteration(branch: &'static str, start: crate::time::Instant) {
665 let elapsed = start.elapsed();
666 METRICS
667 .envoy_loop_iteration_duration_seconds
668 .with_label_values(&[branch])
669 .observe(elapsed.as_secs_f64());
670}
671
672pub fn send_to_envoy_tx(
676 shared: &crate::context::SharedContext,
677 msg: ToEnvoyMessage,
678) -> Result<(), tokio::sync::mpsc::error::SendError<ToEnvoyMessage>> {
679 match shared.envoy_tx.send(msg) {
680 Ok(()) => {
681 METRICS.envoy_tx_depth.inc();
682 Ok(())
683 }
684 Err(e) => Err(e),
685 }
686}
687
688async fn handle_conn_message(
689 ctx: &mut EnvoyContext,
690 start_tx: &tokio::sync::watch::Sender<()>,
691 mut lost_timeout: Option<SleepFuture>,
692 message: protocol::ToEnvoy,
693 session: u64,
694) -> Option<SleepFuture> {
695 match message {
696 protocol::ToEnvoy::ToEnvoyInit(init) => {
697 {
698 let mut guard = ctx.shared.protocol_metadata.lock().await;
699 *guard = Some(init.metadata.clone());
700 }
701 tracing::info!(?init.metadata, "received init");
702
703 lost_timeout = None;
704 resend_unacknowledged_events(ctx).await;
705 process_unsent_kv_requests(ctx).await;
706 process_unsent_sqlite_requests(ctx).await;
707 process_unsent_remote_sqlite_requests(ctx).await;
708 resend_buffered_tunnel_messages(ctx).await;
709
710 let _ = start_tx.send(());
711 }
712 protocol::ToEnvoy::ToEnvoyCommands(commands) => {
713 handle_commands(ctx, commands).await;
714 }
715 protocol::ToEnvoy::ToEnvoyAckEvents(ack) => {
716 handle_ack_events(ctx, ack);
717 }
718 protocol::ToEnvoy::ToEnvoyKvResponse(response) => {
719 handle_kv_response(ctx, response).await;
720 }
721 protocol::ToEnvoy::ToEnvoySqliteGetPagesResponse(response) => {
722 handle_sqlite_get_pages_response(ctx, response).await;
723 }
724 protocol::ToEnvoy::ToEnvoySqliteCommitResponse(response) => {
725 handle_sqlite_commit_response(ctx, response).await;
726 }
727 protocol::ToEnvoy::ToEnvoySqliteCommitStageBeginResponse(response) => {
728 handle_sqlite_commit_stage_begin_response(ctx, response).await;
729 }
730 protocol::ToEnvoy::ToEnvoySqliteCommitStageSegmentResponse(response) => {
731 handle_sqlite_commit_stage_segment_response(ctx, response).await;
732 }
733 protocol::ToEnvoy::ToEnvoySqliteCommitFinalizeResponse(response) => {
734 handle_sqlite_commit_finalize_response(ctx, response).await;
735 }
736 protocol::ToEnvoy::ToEnvoySqliteExecResponse(response) => {
737 handle_remote_sqlite_exec_response(ctx, response).await;
738 }
739 protocol::ToEnvoy::ToEnvoySqliteExecuteResponse(response) => {
740 handle_remote_sqlite_execute_response(ctx, response).await;
741 }
742 protocol::ToEnvoy::ToEnvoySqliteExecuteBatchResponse(response) => {
743 handle_remote_sqlite_execute_batch_response(ctx, response).await;
744 }
745 protocol::ToEnvoy::ToEnvoyTunnelMessage(tunnel_msg) => {
746 handle_tunnel_message(ctx, session, tunnel_msg).await;
747 }
748 protocol::ToEnvoy::ToEnvoyPing(_) => {
749 }
751 }
752
753 lost_timeout
754}
755
756fn handle_conn_close(ctx: &EnvoyContext, lost_timeout: Option<SleepFuture>) -> Option<SleepFuture> {
757 if lost_timeout.is_some() {
758 return lost_timeout;
759 }
760
761 let lost_threshold = {
763 let metadata = ctx.shared.protocol_metadata.try_lock().ok();
764 metadata
765 .and_then(|guard| guard.as_ref().map(|m| m.envoy_lost_threshold as u64))
766 .unwrap_or(10_000)
767 };
768
769 tracing::debug!(ms = lost_threshold, "starting envoy lost timeout");
770
771 Some(boxed_sleep(std::time::Duration::from_millis(
772 lost_threshold,
773 )))
774}
775
776async fn handle_shutdown(ctx: &mut EnvoyContext) {
777 if ctx.shutting_down {
778 return;
779 }
780 ctx.shutting_down = true;
781 ctx.shared.shutting_down.store(true, Ordering::Release);
782
783 tracing::debug!("envoy received shutdown");
784
785 ws_send(&ctx.shared, protocol::ToRivet::ToRivetStopping).await;
786
787 let actor_handles: Vec<mpsc::UnboundedSender<ToActor>> = ctx
790 .actors
791 .values()
792 .flat_map(|gens| gens.values())
793 .filter(|entry| !entry.handle.is_closed())
794 .map(|entry| entry.handle.clone())
795 .collect();
796
797 let shared = ctx.shared.clone();
798 let shutdown_span = tracing::debug_span!(
799 parent: tracing::Span::current(),
800 "envoy_graceful_shutdown",
801 envoy_key = %ctx.shared.envoy_key,
802 );
803 spawn_detached(
804 async move {
805 futures_util::future::join_all(actor_handles.iter().map(|h| h.closed())).await;
806 tracing::debug!("all actors stopped during graceful shutdown");
807 let _ = send_to_envoy_tx(&shared, ToEnvoyMessage::Stop);
808 }
809 .instrument(shutdown_span),
810 );
811}