agentscope-ai / agentscope-ai/agentscope-java
[Bug]:Same-isolation-key concurrent calls race on sandbox state slot
- Linguagem predominante
- Java
- Estrelas
- 5.6k
- Forks
- 1.3k
- Merge médio
- 4d 12h
- PRs com merge (30d)
- 77
Descrição
Description
On a single HarnessAgent, two concurrent calls that resolve to the same SandboxIsolationKey operate on the
same persisted sandbox state record. The sandbox acquire → resume → persist → release window runs outside
the delegate's serializeOnKey gate, and the sandbox-layer guard defaults to noop() — so there is no
serialisation.
Both calls therefore start independent containers from the same state, mutate them in parallel, and
overwrite each other on completion. Workspace changes from one call are silently lost (last write wins),
with a transient double-container in between.
Expected behaviour
Concurrent calls resolving to the same isolation key should serialise and hand off state sequentially. No
call's workspace changes should be clobbered by a concurrent peer.
Actual behaviour
Calls run fully in parallel on the shared state slot; whichever call persists last wins, and the other
call's changes are dropped.
Steps to reproduce
1. Build a HarnessAgent with the default scope (or explicit GLOBAL / AGENT).
2. Fire two concurrent calls that resolve to the same isolation key, each mutating the sandbox workspace
(e.g. writing a file).
3. Inspect the final persisted state — one call's changes have been overwritten and lost.
Affected scopes
Any two concurrent calls that resolve to the same persisted state slot:
- SESSION — same sessionId
- USER (default) — same userId
- AGENT — same agentId (fixed at build time)
- GLOBAL — constant key "__global__", shared by all calls (easiest to hit)
Notes
- This is not covered by #2490. That fix isolates the in-memory live binding for different-key concurrent
calls; it does not prevent same-key calls from racing on the persisted state.
- serializeOnKey does not help here: its gate key (userId, sessionId) protects ReActAgent's session state
and runs inside the delegate, whereas the sandbox lifecycle is wrapped outside it.
Possible direction (non-binding)
Serialise same-key concurrent calls at the sandbox layer — e.g. a JVM-local default guard keyed by
SandboxIsolationKey. Multi-instance deployments would need a distributed guard (Redis SET NX, etc.).
Implementation details TBD in the PR.
Guia de contribuição
Avaliação
Esta issue ainda não foi avaliada.