Skip to content

fix: PLTF-2895 dedicated read session for RemoteSandboxService.get_sandbox (avoids idle-in-txn without breaking delete atomicity) - #142

Open
aivong-openhands wants to merge 1 commit into
mainfrom
fix/pltf-2895-sandbox-idle-in-txn-read-session
Open

fix: PLTF-2895 dedicated read session for RemoteSandboxService.get_sandbox (avoids idle-in-txn without breaking delete atomicity)#142
aivong-openhands wants to merge 1 commit into
mainfrom
fix/pltf-2895-sandbox-idle-in-txn-read-session

Conversation

@aivong-openhands

Copy link
Copy Markdown
Contributor

Summary

RemoteSandboxService.get_sandbox() reads the stored sandbox on the shared, request-scoped AsyncSession and then makes a (slow) runtime API call. That leaves the session "idle in transaction" for the duration of the network call — pinning a pooled connection and blocking autovacuum on v1_remote_sandbox.

This PR reads the stored sandbox on a short-lived, isolated session (_isolated_read_session) so the runtime API call never spans the shared transaction. The shared request session is left completely untouched.

Background — why not just commit the shared session?

This is the same "idle in transaction" problem addressed by OpenHands/OpenHands#14769 (fix: PLTF-2895 release DB transactions before runtime API I/O), which was opened before the app-server code moved to this repo. That PR's approach was to commit() the shared session before the runtime call.

Review of #14769 found that committing the shared session is unsafe (finding #1): the session is shared by every service in a request, so committing it also flushes sibling services' still-pending writes. Concretely, delete_app_conversation interleaves, per conversation:

_delete_from_agent_server(...)  ->  sandbox_service.get_sandbox(...)
_delete_from_database(...)      ->  raw DELETEs, no internal commit

so committing inside get_sandbox would commit the previous iteration's still-pending DELETEs — making the multi-step delete no longer all-or-nothing.

The dedicated-read-session approach here achieves #14769's goal (no transaction spans runtime I/O) without that atomicity regression, because the shared session is never read on or committed.

Test

Adds TestSharedSessionAtomicityDuringDelete — a real SQLAlchemy (SQLite) regression test (no DB mocking) that:

  1. seeds an uncommitted sibling DELETE on the shared session,
  2. calls get_sandbox,
  3. asserts from a separate connection that the sibling write was not committed.

This test fails if get_sandbox ever commits the shared session again. Full test_remote_sandbox_service.py suite: 114 passed; ruff check + ruff format --check clean.

Scope / follow-ups

References


This PR was created by an AI agent (OpenHands) on behalf of @aivong-openhands.

…void idle-in-transaction (PLTF-2895)

get_sandbox() read the stored sandbox on the shared, request-scoped
AsyncSession and then made a (slow) runtime API call, leaving that
session "idle in transaction" — pinning a pooled connection and blocking
autovacuum on v1_remote_sandbox.

Instead of committing the shared session before the network call (the
approach taken in OpenHands/OpenHands#14769), read on a short-lived,
isolated session so the runtime API call never spans the shared
transaction. Committing the shared session would also flush sibling
services' still-pending writes (e.g. the raw DELETEs queued by
delete_app_conversation), breaking the atomicity of those multi-step
flows — the regression identified in the review of #14769 (finding #1).

Adds a real-session regression test (TestSharedSessionAtomicityDuringDelete)
that seeds an uncommitted sibling DELETE on the shared session, calls
get_sandbox, and asserts from a separate connection that the sibling write
was not committed.

Refs OpenHands/OpenHands#14769 and its review feedback.

Co-authored-by: openhands <openhands@all-hands.dev>
@linear

linear Bot commented Aug 6, 2026

Copy link
Copy Markdown

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