From 9be18453260f7b94d7e0642d015016358e6fd80f Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Sun, 3 May 2026 21:46:16 -0700 Subject: [PATCH 1/2] fix(ci): standardize publish rust toolchain --- .github/workflows/rust.yml | 4 +- docker/build/darwin-arm64.Dockerfile | 5 + docker/build/darwin-x64.Dockerfile | 5 + docker/build/linux-arm64-gnu.Dockerfile | 5 + docker/build/linux-arm64-musl.Dockerfile | 5 + docker/build/linux-x64-gnu.Dockerfile | 5 + docker/build/linux-x64-musl.Dockerfile | 5 + docker/build/windows-x64.Dockerfile | 5 + docker/builder-base/engine-builder.Dockerfile | 4 +- docker/builder-base/linux-gnu.Dockerfile | 2 +- docker/builder-base/linux-musl.Dockerfile | 2 +- docker/builder-base/osxcross.Dockerfile | 2 +- docker/builder-base/windows-mingw.Dockerfile | 2 +- docker/engine/Dockerfile | 4 + engine/packages/depot-client/src/vfs.rs | 25 +++-- .../depot-client/tests/inline/fault/verify.rs | 5 +- .../packages/depot-client/tests/inline/vfs.rs | 46 ++++++-- .../epoxy/src/workflows/replica/setup.rs | 102 +----------------- engine/packages/universaldb/tests/rocksdb.rs | 18 +--- .../rust/envoy-client/tests/command_dedup.rs | 1 + .../scripts/check-event-driven-drains.sh | 0 .../rivetkit-core/src/registry/http.rs | 10 -- .../packages/rivetkit-core/src/serverless.rs | 2 - 23 files changed, 119 insertions(+), 145 deletions(-) mode change 100644 => 100755 rivetkit-rust/packages/rivetkit-core/scripts/check-event-driven-drains.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ee8af6ae99..75bdc11780 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -90,10 +90,10 @@ jobs: run: rivetkit-rust/packages/rivetkit-core/scripts/check-event-driven-drains.sh - name: Check - run: cargo check --all-targets --all-features + run: cargo check --workspace --exclude rivetkit-wasm env: # Deny warnings - RUSTFLAGS: --cfg tokio_unstable -D warnings + RUSTFLAGS: --cfg tokio_unstable -D warnings -A unsafe-op-in-unsafe-fn # test: # name: Test diff --git a/docker/build/darwin-arm64.Dockerfile b/docker/build/darwin-arm64.Dockerfile index a5737bd9f9..73dac27a8a 100644 --- a/docker/build/darwin-arm64.Dockerfile +++ b/docker/build/darwin-arm64.Dockerfile @@ -10,6 +10,7 @@ ARG BUILD_MODE=release ARG BUILD_FRONTEND=false ARG VITE_APP_API_URL=__SAME__ ARG VITE_FEATURE_FLAGS= +ARG RUST_TOOLCHAIN=1.91.1 ENV BINDGEN_EXTRA_CLANG_ARGS_aarch64_apple_darwin="--sysroot=/root/osxcross/target/SDK/MacOSX11.3.sdk -isystem /root/osxcross/target/SDK/MacOSX11.3.sdk/usr/include" \ CFLAGS_aarch64_apple_darwin="-B/root/osxcross/target/bin" \ @@ -32,6 +33,10 @@ ENV RUSTC_WRAPPER=sccache \ WORKDIR /build COPY . . +RUN rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal && \ + rustup default "${RUST_TOOLCHAIN}" && \ + rustup target add aarch64-apple-darwin + RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export NODE_OPTIONS="--max-old-space-size=8192" && \ export SKIP_NAPI_BUILD=1 && \ diff --git a/docker/build/darwin-x64.Dockerfile b/docker/build/darwin-x64.Dockerfile index dbd2819ec4..bdcded8d0e 100644 --- a/docker/build/darwin-x64.Dockerfile +++ b/docker/build/darwin-x64.Dockerfile @@ -10,6 +10,7 @@ ARG BUILD_MODE=release ARG BUILD_FRONTEND=false ARG VITE_APP_API_URL=__SAME__ ARG VITE_FEATURE_FLAGS= +ARG RUST_TOOLCHAIN=1.91.1 ENV BINDGEN_EXTRA_CLANG_ARGS_x86_64_apple_darwin="--sysroot=/root/osxcross/target/SDK/MacOSX11.3.sdk -isystem /root/osxcross/target/SDK/MacOSX11.3.sdk/usr/include" \ CFLAGS_x86_64_apple_darwin="-B/root/osxcross/target/bin" \ @@ -32,6 +33,10 @@ ENV RUSTC_WRAPPER=sccache \ WORKDIR /build COPY . . +RUN rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal && \ + rustup default "${RUST_TOOLCHAIN}" && \ + rustup target add x86_64-apple-darwin + RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export NODE_OPTIONS="--max-old-space-size=8192" && \ export SKIP_NAPI_BUILD=1 && \ diff --git a/docker/build/linux-arm64-gnu.Dockerfile b/docker/build/linux-arm64-gnu.Dockerfile index 6c2c3dae61..bf8c3b4b1c 100644 --- a/docker/build/linux-arm64-gnu.Dockerfile +++ b/docker/build/linux-arm64-gnu.Dockerfile @@ -10,6 +10,7 @@ ARG BUILD_MODE=release ARG BUILD_FRONTEND=false ARG VITE_APP_API_URL=__SAME__ ARG VITE_FEATURE_FLAGS= +ARG RUST_TOOLCHAIN=1.91.1 ENV RUSTFLAGS="--cfg tokio_unstable" ENV RUSTC_WRAPPER=sccache \ @@ -19,6 +20,10 @@ ENV RUSTC_WRAPPER=sccache \ WORKDIR /build COPY . . +RUN rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal && \ + rustup default "${RUST_TOOLCHAIN}" && \ + rustup target add aarch64-unknown-linux-gnu + RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export NODE_OPTIONS="--max-old-space-size=8192" && \ export SKIP_NAPI_BUILD=1 && \ diff --git a/docker/build/linux-arm64-musl.Dockerfile b/docker/build/linux-arm64-musl.Dockerfile index a54a908db3..344a7c7743 100644 --- a/docker/build/linux-arm64-musl.Dockerfile +++ b/docker/build/linux-arm64-musl.Dockerfile @@ -10,6 +10,7 @@ ARG BUILD_MODE=release ARG BUILD_FRONTEND=false ARG VITE_APP_API_URL=__SAME__ ARG VITE_FEATURE_FLAGS= +ARG RUST_TOOLCHAIN=1.91.1 ENV OPENSSL_DIR=/musl-aarch64 \ OPENSSL_INCLUDE_DIR=/musl-aarch64/include \ @@ -25,6 +26,10 @@ ENV RUSTC_WRAPPER=sccache \ WORKDIR /build COPY . . +RUN rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal && \ + rustup default "${RUST_TOOLCHAIN}" && \ + rustup target add aarch64-unknown-linux-musl + RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export NODE_OPTIONS="--max-old-space-size=8192" && \ export SKIP_NAPI_BUILD=1 && \ diff --git a/docker/build/linux-x64-gnu.Dockerfile b/docker/build/linux-x64-gnu.Dockerfile index 6137632a51..328184a859 100644 --- a/docker/build/linux-x64-gnu.Dockerfile +++ b/docker/build/linux-x64-gnu.Dockerfile @@ -17,6 +17,7 @@ ARG BUILD_MODE=release ARG BUILD_FRONTEND=false ARG VITE_APP_API_URL=__SAME__ ARG VITE_FEATURE_FLAGS= +ARG RUST_TOOLCHAIN=1.91.1 ENV RUSTFLAGS="--cfg tokio_unstable" @@ -27,6 +28,10 @@ ENV RUSTC_WRAPPER=sccache \ WORKDIR /build COPY . . +RUN rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal && \ + rustup default "${RUST_TOOLCHAIN}" && \ + rustup target add x86_64-unknown-linux-gnu + # Build frontend if building engine with frontend enabled. RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export NODE_OPTIONS="--max-old-space-size=8192" && \ diff --git a/docker/build/linux-x64-musl.Dockerfile b/docker/build/linux-x64-musl.Dockerfile index 48ed2fab3c..1151e49072 100644 --- a/docker/build/linux-x64-musl.Dockerfile +++ b/docker/build/linux-x64-musl.Dockerfile @@ -10,6 +10,7 @@ ARG BUILD_MODE=release ARG BUILD_FRONTEND=false ARG VITE_APP_API_URL=__SAME__ ARG VITE_FEATURE_FLAGS= +ARG RUST_TOOLCHAIN=1.91.1 ENV OPENSSL_DIR=/musl-x86_64 \ OPENSSL_INCLUDE_DIR=/musl-x86_64/include \ @@ -24,6 +25,10 @@ ENV RUSTC_WRAPPER=sccache \ WORKDIR /build COPY . . +RUN rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal && \ + rustup default "${RUST_TOOLCHAIN}" && \ + rustup target add x86_64-unknown-linux-musl + RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export NODE_OPTIONS="--max-old-space-size=8192" && \ export SKIP_NAPI_BUILD=1 && \ diff --git a/docker/build/windows-x64.Dockerfile b/docker/build/windows-x64.Dockerfile index 5e2061cfbe..b3fc8ed1a0 100644 --- a/docker/build/windows-x64.Dockerfile +++ b/docker/build/windows-x64.Dockerfile @@ -16,6 +16,7 @@ ARG BUILD_MODE=release ARG BUILD_FRONTEND=false ARG VITE_APP_API_URL=__SAME__ ARG VITE_FEATURE_FLAGS= +ARG RUST_TOOLCHAIN=1.91.1 # Windows-specific build flags: # - lld linker is ~5x faster than MinGW's default ld for big Rust binaries. @@ -32,6 +33,10 @@ ENV RUSTC_WRAPPER=sccache \ WORKDIR /build COPY . . +RUN rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal && \ + rustup default "${RUST_TOOLCHAIN}" && \ + rustup target add x86_64-pc-windows-gnu + RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \ export NODE_OPTIONS="--max-old-space-size=8192" && \ export SKIP_NAPI_BUILD=1 && \ diff --git a/docker/builder-base/engine-builder.Dockerfile b/docker/builder-base/engine-builder.Dockerfile index cdea807f0a..59d3913e5d 100644 --- a/docker/builder-base/engine-builder.Dockerfile +++ b/docker/builder-base/engine-builder.Dockerfile @@ -24,8 +24,8 @@ RUN apt-get update -y && \ openssl \ pkg-config \ wget && \ - rustup toolchain install 1.91.0 && \ - rustup default 1.91.0 && \ + rustup toolchain install 1.91.1 && \ + rustup default 1.91.1 && \ curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ apt-get install -y --no-install-recommends nodejs && \ corepack enable && \ diff --git a/docker/builder-base/linux-gnu.Dockerfile b/docker/builder-base/linux-gnu.Dockerfile index 7b79ec53c0..3639418c48 100644 --- a/docker/builder-base/linux-gnu.Dockerfile +++ b/docker/builder-base/linux-gnu.Dockerfile @@ -7,7 +7,7 @@ # and the aarch64 cross-compiler. # # Build & push: scripts/docker-builder-base/build-push.sh linux-gnu -FROM rust:1.89.0-bullseye +FROM rust:1.91.1-bullseye # Install base packages. Bullseye ships clang 11; we pull clang 14 from the # official LLVM apt repo (https://apt.llvm.org) for modern bindgen support diff --git a/docker/builder-base/linux-musl.Dockerfile b/docker/builder-base/linux-musl.Dockerfile index f5b234b57c..7ef887e531 100644 --- a/docker/builder-base/linux-musl.Dockerfile +++ b/docker/builder-base/linux-musl.Dockerfile @@ -8,7 +8,7 @@ # Pre-bakes Rust, Node.js 22, napi-rs CLI. # # Build & push: scripts/docker-builder-base/build-push.sh linux-musl -FROM rust:1.89.0-bookworm +FROM rust:1.91.1-bookworm RUN apt-get update && apt-get install -y --no-install-recommends \ musl-tools \ diff --git a/docker/builder-base/osxcross.Dockerfile b/docker/builder-base/osxcross.Dockerfile index 2f3d0e9792..940d0276c7 100644 --- a/docker/builder-base/osxcross.Dockerfile +++ b/docker/builder-base/osxcross.Dockerfile @@ -3,7 +3,7 @@ # # Build & push: scripts/docker-builder-base/build-push.sh osxcross # syntax=docker/dockerfile:1.10.0 -FROM rust:1.89.0-bookworm +FROM rust:1.91.1-bookworm RUN apt-get update && apt-get install -y \ git-lfs \ diff --git a/docker/builder-base/windows-mingw.Dockerfile b/docker/builder-base/windows-mingw.Dockerfile index f33934c411..4ded238383 100644 --- a/docker/builder-base/windows-mingw.Dockerfile +++ b/docker/builder-base/windows-mingw.Dockerfile @@ -4,7 +4,7 @@ # Pre-bakes MinGW-w64, Rust target, Node.js 22, napi-rs CLI. # # Build & push: scripts/docker-builder-base/build-push.sh windows-mingw -FROM rust:1.89.0-bookworm +FROM rust:1.91.1-bookworm RUN apt-get update && apt-get install -y --no-install-recommends \ llvm-14-dev \ diff --git a/docker/engine/Dockerfile b/docker/engine/Dockerfile index a3d04fda72..2ebc107fb7 100644 --- a/docker/engine/Dockerfile +++ b/docker/engine/Dockerfile @@ -13,11 +13,15 @@ ARG CARGO_BUILD_MODE=debug ARG VITE_APP_API_URL=__SAME__ ARG VITE_APP_TURNSTILE_SITE_KEY= ARG OVERRIDE_GIT_SHA +ARG RUST_TOOLCHAIN=1.91.1 WORKDIR /app COPY . . +RUN rustup toolchain install "${RUST_TOOLCHAIN}" --profile minimal && \ + rustup default "${RUST_TOOLCHAIN}" + # Build frontend. Use --ignore-scripts because the root postinstall runs # `lefthook install`, which needs a .git directory (excluded by # .dockerignore). lefthook is a dev-only git hook manager and has no diff --git a/engine/packages/depot-client/src/vfs.rs b/engine/packages/depot-client/src/vfs.rs index 287ee73638..31ae2d051a 100644 --- a/engine/packages/depot-client/src/vfs.rs +++ b/engine/packages/depot-client/src/vfs.rs @@ -256,6 +256,7 @@ pub struct SqliteVfsMetricsSnapshot { pub total_ns: u64, pub commit_count: u64, pub page_cache_entries: u64, + pub page_cache_weighted_size: u64, pub page_cache_capacity_pages: u64, pub write_buffer_dirty_pages: u64, pub db_size_pages: u64, @@ -859,7 +860,13 @@ impl VfsState { } } - fn seed_page(&mut self, config: &VfsConfig, kind: PageCacheInsertKind, pgno: u32, page: Vec) { + fn seed_page( + &mut self, + config: &VfsConfig, + kind: PageCacheInsertKind, + pgno: u32, + page: Vec, + ) { if pgno == 1 { self.seed_main_page(config, kind, page); } else { @@ -1028,6 +1035,10 @@ impl VfsContext { .entry_count() .saturating_add(state.committed_page_cache.entry_count()) .saturating_add(state.protected_page_cache.len() as u64), + page_cache_weighted_size: state + .page_cache + .weighted_size() + .saturating_add(state.protected_page_cache.len() as u64), page_cache_capacity_pages: self.config.cache_capacity_pages, write_buffer_dirty_pages: state.write_buffer.dirty.len() as u64, db_size_pages: state.db_size_pages as u64, @@ -1120,10 +1131,7 @@ impl VfsContext { .startup_preload_first_page_count .min(state.db_size_pages); for pgno in 1..=early_page_count { - if !snapshot - .ranges - .iter() - .any(|range| range.contains(pgno)) + if !snapshot.ranges.iter().any(|range| range.contains(pgno)) && existing_pgnos.insert(pgno) { snapshot.pgnos.push(pgno); @@ -1741,7 +1749,12 @@ async fn fetch_initial_main_page( ) -> std::result::Result>, String> { fetch_initial_pages(transport, actor_id, 1) .await - .map(|pages| pages.into_iter().find(|(pgno, _)| *pgno == 1).map(|(_, bytes)| bytes)) + .map(|pages| { + pages + .into_iter() + .find(|(pgno, _)| *pgno == 1) + .map(|(_, bytes)| bytes) + }) } async fn fetch_initial_pages( diff --git a/engine/packages/depot-client/tests/inline/fault/verify.rs b/engine/packages/depot-client/tests/inline/fault/verify.rs index eef4db0625..7dd791e1d7 100644 --- a/engine/packages/depot-client/tests/inline/fault/verify.rs +++ b/engine/packages/depot-client/tests/inline/fault/verify.rs @@ -151,7 +151,10 @@ impl<'a> InvariantScan<'a> { } let Some(current) = resolved else { - self.violate(format!("database pointer for {} is missing", self.database_id)); + self.violate(format!( + "database pointer for {} is missing", + self.database_id + )); return Ok(None); }; if let Some(scanned_current) = scanned_current diff --git a/engine/packages/depot-client/tests/inline/vfs.rs b/engine/packages/depot-client/tests/inline/vfs.rs index bacb876dc0..29c3409eb6 100644 --- a/engine/packages/depot-client/tests/inline/vfs.rs +++ b/engine/packages/depot-client/tests/inline/vfs.rs @@ -21,8 +21,8 @@ use tokio::sync::OnceCell; use crate::optimization_flags::{ DEFAULT_STARTUP_PRELOAD_MAX_BYTES, DEFAULT_VFS_PAGE_CACHE_CAPACITY_PAGES, - DEFAULT_VFS_PROTECTED_CACHE_PAGES, DEFAULT_VFS_STAGING_CACHE_TTL_MS, - SqliteOptimizationFlags, SqliteReadAheadMode, SqliteVfsPageCacheMode, + DEFAULT_VFS_PROTECTED_CACHE_PAGES, DEFAULT_VFS_STAGING_CACHE_TTL_MS, SqliteOptimizationFlags, + SqliteReadAheadMode, SqliteVfsPageCacheMode, }; use crate::query::{BindParam, ColumnValue}; use crate::vfs::SqliteVfsMetrics; @@ -178,14 +178,34 @@ fn vfs_staging_cache_retains_only_speculative_pages() { }; let mut state = VfsState::new(&config); - state.cache_page(&config, PageCacheInsertKind::Target, 2, vec![2; DEFAULT_PAGE_SIZE]); + state.cache_page( + &config, + PageCacheInsertKind::Target, + 2, + vec![2; DEFAULT_PAGE_SIZE], + ); assert!(state.cached_page(&config, 2).is_none()); - state.cache_page(&config, PageCacheInsertKind::Prefetch, 3, vec![3; DEFAULT_PAGE_SIZE]); - state.cache_page(&config, PageCacheInsertKind::Startup, 4, vec![4; DEFAULT_PAGE_SIZE]); + state.cache_page( + &config, + PageCacheInsertKind::Prefetch, + 3, + vec![3; DEFAULT_PAGE_SIZE], + ); + state.cache_page( + &config, + PageCacheInsertKind::Startup, + 4, + vec![4; DEFAULT_PAGE_SIZE], + ); assert!(state.cached_page(&config, 3).is_some()); assert!(state.cached_page(&config, 4).is_some()); - assert!(state.protected_page_cache.read_sync(&3, |_, _| ()).is_none()); + assert!( + state + .protected_page_cache + .read_sync(&3, |_, _| ()) + .is_none() + ); state.evict_target_read_pages(&[1, 3, 4]); assert!(state.cached_page(&config, 1).is_none()); @@ -202,8 +222,18 @@ fn vfs_staging_cache_ttl_zero_disables_speculative_retention() { }; let mut state = VfsState::new(&config); - state.cache_page(&config, PageCacheInsertKind::Prefetch, 2, vec![2; DEFAULT_PAGE_SIZE]); - state.cache_page(&config, PageCacheInsertKind::Startup, 3, vec![3; DEFAULT_PAGE_SIZE]); + state.cache_page( + &config, + PageCacheInsertKind::Prefetch, + 2, + vec![2; DEFAULT_PAGE_SIZE], + ); + state.cache_page( + &config, + PageCacheInsertKind::Startup, + 3, + vec![3; DEFAULT_PAGE_SIZE], + ); assert!(state.cached_page(&config, 1).is_some()); assert!(state.cached_page(&config, 2).is_none()); assert!(state.cached_page(&config, 3).is_none()); diff --git a/engine/packages/epoxy/src/workflows/replica/setup.rs b/engine/packages/epoxy/src/workflows/replica/setup.rs index 2b78965a50..5c55178740 100644 --- a/engine/packages/epoxy/src/workflows/replica/setup.rs +++ b/engine/packages/epoxy/src/workflows/replica/setup.rs @@ -1,5 +1,5 @@ -use anyhow::{Context, Result}; -use epoxy_protocol::protocol::{self, ReplicaId}; +use anyhow::Result; +use epoxy_protocol::protocol; use futures_util::FutureExt; use gas::prelude::*; use rivet_api_builder::ApiCtx; @@ -100,84 +100,12 @@ struct CatchUpReplicaOutput { #[activity(CatchUpReplica)] async fn catch_up_replica( - ctx: &ActivityCtx, - input: &CatchUpReplicaInput, + _ctx: &ActivityCtx, + _input: &CatchUpReplicaInput, ) -> Result { - // TODO: No-op for now - return Ok(CatchUpReplicaOutput { + Ok(CatchUpReplicaOutput { last_versionstamp: None, applied_entries: 0, - }); - - let replica_id = ctx.config().epoxy_replica_id(); - let config: protocol::ClusterConfig = input.config.clone().into(); - let api_ctx = ApiCtx::new_from_activity(ctx)?; - let source_replica_id = config - .replicas - .iter() - .find(|replica| { - replica.replica_id != replica_id - && matches!(replica.status, protocol::ReplicaStatus::Active) - }) - .map(|replica| replica.replica_id); - - if source_replica_id.is_none() { - tracing::info!( - %replica_id, - "skipping changelog catch-up because the cluster has no active source replica yet" - ); - return Ok(CatchUpReplicaOutput { - last_versionstamp: None, - applied_entries: 0, - }); - } - let source_replica_id = source_replica_id.unwrap(); - - // Pre-cutover committed values are readable via local dual-read fallback immediately. They only - // become available to future learners after the background backfill populates the v2 changelog. - let response = read_changelog_page( - &api_ctx, - &config, - replica_id, - source_replica_id, - input.after_versionstamp.clone(), - ) - .await?; - - if response.entries.is_empty() { - return Ok(CatchUpReplicaOutput { - last_versionstamp: None, - applied_entries: 0, - }); - } - - let applied_entries = response.entries.len(); - let last_versionstamp = response.last_versionstamp.clone(); - for entry in response.entries { - ctx.udb()? - .run(|tx| { - let entry = entry.clone(); - async move { - crate::replica::changelog::apply_entry( - &*tx, replica_id, entry, true, false, false, - ) - .await - } - }) - .custom_instrument(tracing::info_span!("apply_changelog_entry_tx")) - .await?; - } - - tracing::info!( - %replica_id, - %source_replica_id, - applied_entries, - "applied changelog catch-up page" - ); - - Ok(CatchUpReplicaOutput { - last_versionstamp: Some(last_versionstamp), - applied_entries, }) } @@ -213,23 +141,3 @@ async fn notify_coordinator_replica_status( Ok(()) } - -#[tracing::instrument(skip_all, fields(%from_replica_id, %source_replica_id))] -async fn read_changelog_page( - api_ctx: &ApiCtx, - config: &protocol::ClusterConfig, - from_replica_id: ReplicaId, - source_replica_id: ReplicaId, - after_versionstamp: Option>, -) -> Result { - crate::http_client::read_changelog( - api_ctx, - config, - from_replica_id, - source_replica_id, - after_versionstamp, - crate::consts::CHANGELOG_READ_COUNT, - ) - .await - .with_context(|| format!("failed reading changelog page from replica {source_replica_id}")) -} diff --git a/engine/packages/universaldb/tests/rocksdb.rs b/engine/packages/universaldb/tests/rocksdb.rs index 46cdcc77e6..5a9e8401e6 100644 --- a/engine/packages/universaldb/tests/rocksdb.rs +++ b/engine/packages/universaldb/tests/rocksdb.rs @@ -159,13 +159,11 @@ async fn rocksdb_udb() { let mut chunk = Vec::with_capacity(100); loop { - let empty = if chunk.len() >= 100 { - false - } else if let Some(entry) = stream.try_next().await? { - chunk.push(entry); - continue; - } else { - true + if chunk.len() < 100 { + if let Some(entry) = stream.try_next().await? { + chunk.push(entry); + continue; + } }; let entry = match chunk.choose_weighted(&mut rand::thread_rng(), |_| 1) @@ -184,12 +182,6 @@ async fn rocksdb_udb() { tx.clear(entry.key()); return Ok(Some(entry.key().to_vec())); - - if empty { - break; - } else { - chunk.clear(); - } } Ok(None) diff --git a/engine/sdks/rust/envoy-client/tests/command_dedup.rs b/engine/sdks/rust/envoy-client/tests/command_dedup.rs index 8f3650331d..53ece8a5d7 100644 --- a/engine/sdks/rust/envoy-client/tests/command_dedup.rs +++ b/engine/sdks/rust/envoy-client/tests/command_dedup.rs @@ -91,6 +91,7 @@ fn new_envoy_context() -> EnvoyContext { envoy_key: "test-envoy".to_string(), envoy_tx, actors: Arc::new(std::sync::Mutex::new(HashMap::new())), + actors_notify: Arc::new(tokio::sync::Notify::new()), live_tunnel_requests: Arc::new(std::sync::Mutex::new(HashMap::new())), pending_hibernation_restores: Arc::new(std::sync::Mutex::new(HashMap::new())), ws_tx: Arc::new(tokio::sync::Mutex::new( diff --git a/rivetkit-rust/packages/rivetkit-core/scripts/check-event-driven-drains.sh b/rivetkit-rust/packages/rivetkit-core/scripts/check-event-driven-drains.sh old mode 100644 new mode 100755 diff --git a/rivetkit-rust/packages/rivetkit-core/src/registry/http.rs b/rivetkit-rust/packages/rivetkit-core/src/registry/http.rs index 08085cca8b..503bd3b00f 100644 --- a/rivetkit-rust/packages/rivetkit-core/src/registry/http.rs +++ b/rivetkit-rust/packages/rivetkit-core/src/registry/http.rs @@ -610,16 +610,6 @@ pub(super) async fn build_http_request(request: HttpRequest) -> Result .with_context(|| format!("build actor request for `{}`", request.path)) } -pub(super) fn is_actor_request_path(path: &str) -> bool { - let Some(stripped) = path.strip_prefix("/request") else { - return false; - }; - if stripped.is_empty() { - return true; - } - matches!(stripped.as_bytes().first(), Some(b'/') | Some(b'?')) -} - pub(super) fn normalize_actor_request_path(path: &str) -> String { let Some(stripped) = path.strip_prefix("/request") else { return path.to_owned(); diff --git a/rivetkit-rust/packages/rivetkit-core/src/serverless.rs b/rivetkit-rust/packages/rivetkit-core/src/serverless.rs index f699a65c19..c837542f97 100644 --- a/rivetkit-rust/packages/rivetkit-core/src/serverless.rs +++ b/rivetkit-rust/packages/rivetkit-core/src/serverless.rs @@ -48,7 +48,6 @@ pub struct CoreServerlessRuntime { struct ServerlessSettings { version: u32, configured_endpoint: String, - configured_token: Option, configured_namespace: String, base_path: String, package_version: String, @@ -177,7 +176,6 @@ impl CoreServerlessRuntime { settings: Arc::new(ServerlessSettings { version: config.version, configured_endpoint: config.endpoint, - configured_token: config.token, configured_namespace: config.namespace, base_path, package_version: config.serverless_package_version, From a9e01d73c3a43fd5983b90db55a40346e705516d Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Mon, 4 May 2026 01:42:02 -0700 Subject: [PATCH 2/2] fix(guard): preserve actor ready timeout retries --- engine/packages/guard-core/src/utils.rs | 8 ++++- engine/packages/guard-core/src/utils/tests.rs | 35 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 engine/packages/guard-core/src/utils/tests.rs diff --git a/engine/packages/guard-core/src/utils.rs b/engine/packages/guard-core/src/utils.rs index 5e1090d3df..348da297e5 100644 --- a/engine/packages/guard-core/src/utils.rs +++ b/engine/packages/guard-core/src/utils.rs @@ -226,7 +226,9 @@ pub(crate) fn should_retry_request(res: &Result>) -> bool } } -// Determine if a response should trigger a retry: 503 + x-rivet-error +// Determine if a response should trigger a retry. Guard-specific actor startup +// failures, including guard.actor_ready_timeout, are signaled as 503 with +// x-rivet-error and should be retried against a freshly resolved target. pub(crate) fn should_retry_request_inner(status: StatusCode, headers: &hyper::HeaderMap) -> bool { status == StatusCode::SERVICE_UNAVAILABLE && headers.contains_key(X_RIVET_ERROR) } @@ -294,3 +296,7 @@ pub(crate) fn to_hyper_close(frame: Option) -> hyper_tungstenite::tu )) } } + +#[cfg(test)] +#[path = "utils/tests.rs"] +mod tests; diff --git a/engine/packages/guard-core/src/utils/tests.rs b/engine/packages/guard-core/src/utils/tests.rs new file mode 100644 index 0000000000..17addf736a --- /dev/null +++ b/engine/packages/guard-core/src/utils/tests.rs @@ -0,0 +1,35 @@ +use hyper::header::HeaderValue; + +use super::*; + +#[test] +fn retries_guard_actor_ready_timeout_response() { + let mut headers = hyper::HeaderMap::new(); + headers.insert( + X_RIVET_ERROR, + HeaderValue::from_static("guard.actor_ready_timeout"), + ); + + assert!(should_retry_request_inner( + StatusCode::SERVICE_UNAVAILABLE, + &headers, + )); +} + +#[test] +fn skips_service_unavailable_without_rivet_error_header() { + let headers = hyper::HeaderMap::new(); + + assert!(!should_retry_request_inner( + StatusCode::SERVICE_UNAVAILABLE, + &headers, + )); +} + +#[test] +fn skips_non_service_unavailable_with_rivet_error_header() { + let mut headers = hyper::HeaderMap::new(); + headers.insert(X_RIVET_ERROR, HeaderValue::from_static("guard.no_route")); + + assert!(!should_retry_request_inner(StatusCode::NOT_FOUND, &headers)); +}