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