Skip to content

fix(envoy-client): declare actors lost before the engine can reallocate them - #5823

Open
abcxff wants to merge 1 commit into
stack/fix-rivetkit-stop-lost-actor-generations-immediately-opswolspfrom
stack/fix-envoy-client-declare-actors-lost-before-the-engine-can-reallocate-them-nkyokown
Open

abcxff wants to merge 1 commit into
stack/fix-rivetkit-stop-lost-actor-generations-immediately-opswolspfrom
stack/fix-envoy-client-declare-actors-lost-before-the-engine-can-reallocate-them-nkyokown

Conversation

@abcxff

@abcxff abcxff commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

No description provided.

@abcxff

abcxff commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

@claude

claude Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Review

The approach is sound. Extracting declare_actors_lost removes duplicated teardown, and the new loss path keeps the "cancel the lost token first" ordering. The docs note is accurate about the best-effort limit.

Potential bugs

  1. Stale last_ping_ts across reconnects. last_ping_ts is only written in forward_to_envoy (connection/mod.rs). Nothing resets it when a connection closes or a new one opens. The new engine_ping_silence arm is armed whenever last_ping_ts != 0 and !ctx.actors.is_empty().
    • Scenario: the connection drops and the envoy reconnects after more than threshold - margin (about 12s with a 15s threshold). The lost timeout is cancelled on reconnect, but last_ping_ts still holds the old value. The silence deadline is already in the past, so the arm fires immediately and declares healthy actors lost.
    • Suggested fix: on connection open, reset the baseline. Either store now_millis() or set it to 0 so the deadline is only computed from pings on the current connection.
    • Add a test for the reconnect case.
  2. No protection before the first ping. last_ping_ts == 0 returns None, so a half-open connection that never delivers a ping has no silence timeout. Seeding the baseline at connection open (see 1) would close this gap too.
  3. Clock source. The deadline is compared against wall-clock now_millis(), so an NTP step can fire it early or late. is_ping_healthy uses the same pattern, so this is minor.

Code quality

  • declare_actors_lost and the engine_ping_silence_* functions are pub only for the integration tests. Consider #[doc(hidden)].
  • The message parameter is logged as reason but is a full sentence. Per CLAUDE.md, prefer a short stable label such as lost_threshold or ping_silence.
  • envoy_lost_threshold_ms uses try_lock on protocol_metadata and silently falls back to 10s on contention. That is pre-existing, but it now feeds a timing-critical path.

Tests

  • The deadline math and declare_actors_lost are covered. There is no test of the envoy_loop branch itself, covering the recheck after a ping arrives mid-sleep and the reconnect case from item 1.

Security and performance

  • No security concerns and no new hot-path cost.

Item 1 is the one I would fix before merge.

@the-company-company the-company-company Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 1 high-severity finding

Reviewed commit 26a3077.

Comment on lines 491 to +494
let iter_start = crate::time::Instant::now();
#[allow(unused_assignments)]
let mut branch: &'static str = "unknown";
let ping_silence_wait = engine_ping_silence_wait(&ctx);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 High · Ping liveness is not synchronized with the select loop or connection session

last_ping_ts is updated in forward_to_envoy without waking this loop. On a fresh connection, command replay can start actors before the ping task sends its first ping; this iteration therefore builds None, and subsequent pings do not arm a deadline until another envoy message or the 15-second KV cleanup tick. With the default 15-second lost threshold, a link that goes half-open in that window can let the engine expire the envoy before this branch runs, defeating the single-writer protection this change is meant to add. The timestamp also survives reconnects, so a reconnect after the old deadline immediately loses still-running or replayed actors before the new connection's first ping. Make ping reception/session changes an event observed by this loop (for example, a watch channel carrying the current session's last-ping value), reset it when a connection is established, and derive/restart the deadline from that event.

@abcxff
abcxff force-pushed the stack/fix-rivetkit-stop-lost-actor-generations-immediately-opswolsp branch from 2b3bd85 to 8d4a713 Compare October 3, 2026 02:05
@abcxff
abcxff force-pushed the stack/fix-envoy-client-declare-actors-lost-before-the-engine-can-reallocate-them-nkyokown branch from 26a3077 to 6a97303 Compare October 3, 2026 02:05
@abcxff
abcxff force-pushed the stack/fix-envoy-client-declare-actors-lost-before-the-engine-can-reallocate-them-nkyokown branch 3 times, most recently from 33530e0 to 6a97303 Compare October 6, 2026 00:42
@abcxff
abcxff force-pushed the stack/fix-rivetkit-stop-lost-actor-generations-immediately-opswolsp branch from 8d4a713 to 6a952ff Compare October 6, 2026 01:29
@abcxff
abcxff force-pushed the stack/fix-envoy-client-declare-actors-lost-before-the-engine-can-reallocate-them-nkyokown branch from 6a97303 to 27a48b9 Compare October 6, 2026 01:29

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant