Skip to content

[client] Add timeouts to actor state::select / state::mutate calls #130

@intendednull

Description

@intendednull

Parent: #108

Problem

Every willow_actor::state::select(...).await and willow_actor::state::mutate(...).await call in crates/client/src/ is awaited without a timeout. If the target StateActor hangs, deadlocks, or is slow-blocked by a large mutation, the calling task (and by transitivity any UI reactive chain) hangs forever.

Fix

  1. Add a small helper in crates/client/src/util.rs:

    use std::time::Duration;
    
    pub const ACTOR_CALL_TIMEOUT: Duration = Duration::from_secs(5);
    
    pub async fn with_timeout<T, F>(label: &'static str, f: F) -> Result<T, ClientError>
    where
        F: std::future::Future<Output = T>,
    {
        #[cfg(not(target_arch = "wasm32"))]
        {
            tokio::time::timeout(ACTOR_CALL_TIMEOUT, f)
                .await
                .map_err(|_| ClientError::ActorTimeout(label))
        }
        #[cfg(target_arch = "wasm32")]
        {
            // wasm doesn't have tokio timers; use wasm-timer or gloo_timers.
            // ...
        }
    }
  2. Wrap all willow_actor::state::select/mutate calls in with_timeout("context_label", ...).

  3. Add ClientError::ActorTimeout(&'static str) variant.

  4. On timeout, log the label and return the error to the caller so the UI can retry or show an error state.

Test

  • Unit test: register a fake actor that sleeps longer than ACTOR_CALL_TIMEOUT, call a client method that uses it, assert ClientError::ActorTimeout is returned within ~6 seconds.
  • Regression: existing happy-path tests should continue passing without modification.

Scope

Don't block on this for every call at once. Start with the hot paths in mutations.rs, accessors.rs, and joining.rs. File a follow-up for the long tail.

Out of scope

  • Switching to a different actor system.
  • Backpressure / mailbox bounds on actors (separate issue).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions