-
-
Notifications
You must be signed in to change notification settings - Fork 1
Add Sync bound to StreamResolver trait object in execute() #302
Description
Problem
The future returned by eventcore::execute() is !Send because stream_resolver() returns Option<&dyn StreamResolver<State>> where the StreamResolver trait object lacks a Sync bound. This means the dyn StreamResolver<State> reference held across .await points makes the overall future !Send.
This is a problem in async frameworks that require Send futures. For example, Leptos's #[server] macro (and Axum handlers in general) require the future to be Send. The compiler error looks like:
error[E0277]: `dyn StreamResolver<CreateProjectState>` cannot be shared between threads safely
Current Workaround
Callers must wrap eventcore::execute() in tokio::task::spawn_blocking + Handle::block_on to run the !Send future on a blocking thread:
let handle = tokio::runtime::Handle::current();
let result = tokio::task::spawn_blocking(move || {
handle.block_on(eventcore::execute(store, command, RetryPolicy::new()))
})
.await
.expect("spawn_blocking task should not panic")?;This works but adds unnecessary complexity and a thread-pool hop for every command execution.
Proposed Fix
Add a Sync bound to the StreamResolver trait object in the execute() function (or wherever the trait object is constructed). Since StreamResolver implementations are typically stateless or use Arc-wrapped state, requiring Sync should not be a breaking change in practice.
This would make the future from execute() Send, allowing direct use in async contexts without the spawn_blocking workaround.
Context
Discovered while integrating eventcore 0.5.0 into a Leptos + Axum application (stochastic_macro PR #186).