<!--
title: Serialize async per-key
domain: devtools
topic: Concurrency
language: Rust
source: warpdotdev/warp
updated: 2026-05-06
url: https://awesomereviewers.com/reviewers/warp-serialize-async-per-key/
-->

Ensure async/stateful flows are race-free by avoiding overlapping work for the same logical key and by handling events that arrive before the relevant state is ready.

Practical rules:
- If multiple updates for the same key (e.g., `path`, `session_id`, `conversation_id`) can be triggered concurrently, allow only one in-flight async task per key. Queue subsequent updates and apply them sequentially after the current task completes.
- If an external event can arrive before initialization/handshake finishes, do not drop it due to temporarily-missing state. Buffer the specific safe event(s) and replay/emit them after the session transitions to the ready state.
- Don’t rely on side-effects that normally happen via subscriptions/entry hooks when control flow can short-circuit. Apply the required state change immediately at the callsite.
- Avoid blocking filesystem/UI work: if filesystem checks could require syscalls, perform them off the main thread and/or in bounded async work.

Example (per-key queue to prevent stale overwrites):
```rust
// pseudo-pattern
if let Some(queued) = pending_updates.get_mut(&key) {
    queued.push(update);
    return;
}

pending_updates.insert(key.clone(), Vec::new());
let rules = current_rules.clone();
spawn(async move {
    process_update(update, rules, key.clone()).await
}, move |me, result, ctx| {
    me.apply(result);
    me.drain_pending_updates(&key, ctx); // sequentially process queued updates
});
```
