Skip to content

[FEATURE]: configurable loaded-location cap with bounded eviction concurrency #55892

Open

Description

@mradwankhalil

Problem

A process serving many short-lived chat directories can retain many loaded locations, each with plugin instances and resources. A TTL bounds age but not the count during a burst.

The local OpenCode V2 carry location-live-cap adds a configurable loaded-location limit and bounds eviction concurrency. This is a proposal for the general mechanism, not a request to impose this workstation's chosen cap on all users.

Expected design constraints: preserve active/in-flight locations, choose eviction candidates deterministically, bound concurrent cleanup, and define behavior when all locations are active or closing. Scope fairness is tracked separately by #55272 / #55754; this proposal is about admission and retention, not yielding alone.

Based on the local carry manifest; no new benchmark is claimed here. Searched issues and PRs for location cap before filing.

Activity

  1. opencode-agent commented on Oct 15, 2026

    @opencode-agent
    Contributor

    Thanks for the proposal! As I understand it, you're asking for a configurable limit on how many locations stay loaded at once, and a limit on how many evictions can run at the same time. The cap would skip active or in-flight locations, pick what to evict in a fixed order, and define what happens when every location is active or closing.

    Right now, locations are only evicted after a fixed idle time (60 minutes). Nothing limits how many are loaded at once, and expired locations are all cleaned up at the same time. So the gap you describe is real. A plugin can't do this, because loading and evicting locations happens inside core. Maintainers will consider it.

    Related issues and PRs:

  2. mradwankhalil commented on Oct 17, 2026

    @mradwankhalil
    ContributorAuthor

    We have a data point from running a cap like this on a workstation, which affects the design. OpenCode 2.0.29 was running with maxLive=12, the app was OpenChamber, and every chat used its own folder. Over one 18-hour backend run:

    • Clients touched 22 distinct locations while the cap was 12.
    • The cap was enforced 75 times. 104 boots were of a location that had already been evicted earlier in the same run.
    • Every one of those re-boots came from an HTTP request (http.span on the location.boot readiness phase). Several came 2–5 s after the location's location services evicted … reason=capacity line.
    • Each re-boot is a full plugin activation on the shared event loop. In our setup that took 6–55 s for a chat folder, so the cap turned into a steady stream of activations while every other session waited.

    So whenever the cap is below the set of locations that clients keep polling, LRU eviction never settles; it just keeps reclaiming and re-booting. Two things would help:

    1. Do not reclaim a location that served a request within the last N minutes (or exempt locations that own a session a client is subscribed to). Then the cap only reclaims folders nobody is looking at.
    2. Log the reclaimed directory on location cap enforced. The eviction line has it, but the enforcement summary does not, which makes this pattern hard to spot.

    For now we raise OPENCODE_LOCATION_MAX_LIVE above the polled working set (about 150–400 MB per location here).

  3. mradwankhalil commented on Oct 17, 2026

    @mradwankhalil
    ContributorAuthor

    Follow-up on attributing the re-boots above (2.0.32 source):

    • Session routes boot the session's stored location, not the request's. server/src/middleware/session-location.ts resolves /api/session/:sessionID/... through instances.provide(session), i.e. map.get(session.location). Any request that names a session — message list/get, session get, todo, diff, … — therefore re-boots that session's folder if it was evicted, whatever x-opencode-directory says. A client that revisits folders with directory-scoped requests (e.g. GET /api/session + header) produced 0 re-boots in an isolated test at cap 12; the live re-boots must come from session-scoped requests.
    • The booting request is invisible in the log. server/src/process.ts disables the response logger below status 400, so in one live run only 21 of 196 re-boots could be attributed (all GET /api/session/:id/message/:messageID → 404, each waiting 20–94 s behind the boot); the other 175 were successful requests and left no trace.

    I'm carrying a small diagnostic locally: when a request reaches a location that is not loaded (checked against the location map's live keys), both location middlewares log one INFO line location boot requested by request with method, path (no query), via (directory|session), session id and user agent. Loaded locations log nothing, so it is one line per boot. Happy to open a PR if that's useful upstream; it would also make the cap/eviction behaviour discussed here observable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions