Skip to content

fix(workspaces): read hosts concurrently so the page stops hanging - #2017

Merged
DEENUU1 merged 5 commits into
mainfrom
fix/workspaces-concurrent-reads
Oct 6, 2026
Merged

DEENUU1 merged 5 commits into
mainfrom
fix/workspaces-concurrent-reads

Conversation

@DEENUU1

@DEENUU1 DEENUU1 commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Summary

Replaces #2008 by @henfircreo: the Workspaces page reads hosts concurrently instead of one after another. This branch holds only that commit, cherry-picked onto main with its authorship kept, plus the follow-ups the review on #2008 asked for. The fork's other changes (refresh-token reissue, 1800 s timeout ceiling, Dockerfile, SQL script) are not here; the auth one is #2007.

Changes

  • 250d254c8 (henfircreo): listings and thumbnails read side by side, the thumbnail budget shared out in listing order before any fetch, a 10 s browse timeout per host call, connection resolution behind a lock.
  • Review follow-ups:
    • The fan-outs run in an asyncio.TaskGroup, so a call that raises cancels the rest instead of leaving them running against the request's session.
    • Host ls and read_bytes calls run on a process-wide eight-thread ThreadPoolExecutor of their own (_HOST_CALLS), not the default executor to_thread uses, which bcrypt and DNS share. A per-request semaphore was tried first; Codex pointed out it bounds one page load only, so a few concurrent loads against a dead host could still take every default thread. The pool bounds the process, and a dead host slows only other host calls.
    • _archive always passes a timeout, defaulting to the library's DEFAULT_TIMEOUT_SECONDS.
    • New tests: the connection lock (one resolve per connection, never two at once) and the process-wide bound, with two page loads at once on their own threads. Both fail when the fix is reverted. The budget-ordering test now uses an Event so the first-listed host really answers last, and it is exempt from the security marker (fetch budget, not spend).
    • The CHANGELOG no longer says a host is "reported after ten seconds" without qualification: the timeout applies per call, so a slow host can still take longer over a deep walk.

Verification

  • tests/test_sandbox_workspace.py, tests/api/test_workspace_browser_routes.py, tests/test_security_marker.py: 239 passed. app/services/sandbox_workspace.py at 100% coverage.
  • ruff format, ruff check, ty check on the changed files: clean.
  • Not run locally: full make test and make check; CI covers those.

Closes #2008

henfircreo and others added 2 commits October 6, 2026 12:21
The Workspaces page opens on the flat "every file" listing, which read
up to 25 container-backed workspaces one after another, then fetched
their thumbnails one after another, each on a fresh archive client. The
page waited for the sum of those round trips, and a host that had gone
away held it for the archive's 60-second default timeout.

Workspaces and thumbnails are now read concurrently, at most eight at a
time, and a listing gives up on a host after ten seconds. The thumbnail
budget is still shared out in listing order before anything is fetched,
so which tiles are drawn does not depend on which host answers first.
Connection resolution is serialised behind a lock because the
concurrent readers share one database session. The "Count files" switch
reads hosts the same way.
Review follow-up on the concurrent reads. The fan-outs run in a
TaskGroup, so a call that raises cancels the rest instead of leaving
them running against the request's session. Listings and thumbnails
share one semaphore per request, taken around single host calls only,
so one page load has at most eight executor threads in flight rather
than eight per host.

Adds tests for the connection lock and the per-request bound, makes
the budget-ordering test land the first host last with an Event, and
exempts it from the security marker: it is a fetch budget, not spend.
The archive always takes a timeout, defaulting to the library's.

Refs #2008
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-06T10:32:20.098940Z 318ea20 PR opened
🔒 Security Review ✅ Completed 2026-10-06T10:36:17.281004Z 318ea20 PR opened

Security findings

Finding details are still loading. Check the individual review comments.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 318ea20224

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/app/services/sandbox_workspace.py Outdated
The per-request semaphore bounded one page load only: the service is
built per request, so a few concurrent loads against a dead host could
take every thread of the loop's default executor, which bcrypt and DNS
also run on. Host ls and read_bytes calls now run on a process-wide
eight-thread pool, carrying the caller's context as to_thread does, and
the per-request semaphore goes. The test runs two page loads at once
and fails on the per-request version with 14 calls in flight.
@DEENUU1
DEENUU1 merged commit 4beeb28 into main Oct 6, 2026
14 checks passed
@DEENUU1
DEENUU1 deleted the fix/workspaces-concurrent-reads branch October 6, 2026 13:09
DEENUU1 added a commit that referenced this pull request Oct 6, 2026
### Summary

Release 0.0.522: version, lock and changelog.

### Changes

- `backend/pyproject.toml`, `frontend/package.json` and
`backend/uv.lock` move to 0.0.522.
- `CHANGELOG.md`: the `[Unreleased]` block becomes `[0.0.522] -
2026-10-06`, with a fresh empty `[Unreleased]` above it.
- Ships #2017 (from #2008): the Workspaces page reads hosts and
thumbnails side by side on a process-wide pool of eight threads of its
own, and a listing gives up on an unanswered host call after ten
seconds.
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.

2 participants