Skip to content

fix(daemon): bound pending sessions and prune on completion - #1091

Open
PierrunoYT wants to merge 1 commit into
Twigpine:mainfrom
PierrunoYT:fix/daemon-session-admission
Open

PierrunoYT wants to merge 1 commit into
Twigpine:mainfrom
PierrunoYT:fix/daemon-session-admission

Conversation

@PierrunoYT

@PierrunoYT PierrunoYT commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Fixes #1084 (issue-approved).

Add a separate admission limit before registering a session or spawning its goroutine. MaxPending bounds queued plus running sessions, defaults to 256, and is independent of completed-history retention (MaxSessions). Excess starts return ErrSessionOverloaded, which the existing control protocol sends through its error response. Already-canceled starts are rejected without registration.

Completion holds the manager lock while finishing the session, releasing admission, and pruning history. This covers success, failure, and queued cancellation; active sessions are never evicted. No new CLI flag or protocol message is introduced.

Regression evidence

Before the implementation, the new blocked-worker tests failed on upstream main:

  • accepted 300 unfinished sessions, want default bound 256
  • retained 3 completed sessions without another Start, want 2

Coverage includes concurrent starts at the default bound, a configured bound independent of retention, overload without registration, duplicate IDs at capacity, queued cancellation, canceled input, capacity reuse, permanent worker failure, and completion-time pruning without another submission.

Validation

Linux amd64, Go 1.26.6:

  • make fmt-check — passed
  • go vet ./... — passed
  • go test ./... — passed
  • go test -race -count=20 ./internal/daemon/... — passed
  • go run ./cmd/zero-release build — passed
  • go run ./cmd/zero-release smoke — passed
  • make vulncheck — No vulnerabilities found
  • git diff HEAD --check — passed before commit
  • make lint-static — advisory failure: four pre-existing staticcheck style findings in unchanged files: internal/installtest/workflow_permissions_test.go:20 (QF1001), internal/proxydial/proxydial.go:67,72 and internal/tools/web_fetch.go:315 (QF1008). No findings in changed files.

Branch created from current upstream main; native macOS and Windows execution was not performed locally.

Summary by CodeRabbit

  • Bug Fixes
    • Session capacity is now managed separately from retained session history, preventing completed sessions from reducing the number of new sessions that can be accepted.
    • When the queue reaches its limit, new sessions are rejected instead of being registered. Duplicate session IDs and requests with canceled contexts are also rejected before registration.
    • Canceled queued sessions release capacity, and completed sessions are pruned to the configured history limit automatically.

Reject excess unfinished sessions before registration and dispatch, independently of retained history. Release admission and prune on every terminal path.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0dcbe-87ec-74e6-bdd0-6341b0c26d9a
Co-authored-by: Pierre Bruno <pierrebruno@hotmail.ch>

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Sep 26, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: Gitlawb/zero/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 7962d8d1-023d-4d34-a664-a95e405b2731

📥 Commits

Reviewing files that changed from the base of the PR and between 99721c7 and 53ba875.

📒 Files selected for processing (2)
  • internal/daemon/session.go
  • internal/daemon/session_test.go

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.


Walkthrough

The session manager now limits unfinished sessions separately from retained history. It defaults MaxPending to 256, rejects canceled or overloaded starts before creating a session, and releases capacity and prunes completed sessions after pool execution.

Changes

Session admission and retention

Layer / File(s) Summary
Pending-session admission
internal/daemon/session.go, internal/daemon/session_test.go
SessionManagerOptions adds MaxPending, which defaults to 256. Start returns the context error for an already-canceled context and ErrSessionOverloaded when the pending limit is reached. Tests check the default limit and rejection behavior.
Capacity release and retention cleanup
internal/daemon/session.go, internal/daemon/session_test.go
After Pool.Run returns, the manager decrements the pending count and prunes completed sessions. Tests cover capacity release after cancellation and pruning to MaxSessions without another start.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 53ba8

The pending-session limit and completion cleanup show no identified issue requiring a fix before merge.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 53ba8

The new limit bounds unfinished sessions and releases capacity when they finish. Remote authentication remains in place, and no new security finding was established. The limit is shared by callers, while the exposure and identity policy of a deployed remote listener remain uncertain.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — One manager-wide budget bounds unfinished work from its callers; when full, another start is rejected before it can register or spawn a run. Evidence does not establish how many independent clients or tenants share a deployed manager.

Trust Boundaries and Controls

  • observed — The remote path validates protocol version, authenticates the bearer token, verifies attestation, and checks connection mode before handing a session connection to daemon dispatch. These controls precede the changed admission check.

Resilience and Maintainability Implications

  • observed — Rejected starts consume no admission slot. Accepted starts release their slot after Pool.Run returns, and completion-time pruning preserves unfinished sessions.

Hardening Proposals

  • proposed — If independently administered clients share a manager, evaluate whether identity-scoped admission or fairness is needed alongside the global resource bound.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main changes: bounding pending sessions and pruning completed sessions on completion.
Linked Issues check ✅ Passed PR #1091 meets the coding requirements in directly linked issue #1084. SessionManagerOptions.MaxPending provides a separate unfinished-session bound with a default of 256. Start checks context can…
Out of Scope Changes check ✅ Passed The reviewed changes are limited to internal/daemon/session.go and internal/daemon/session_test.go. The production changes implement the pending admission bound and completion cleanup from issue #…
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed at 53ba8759. Admission is checked and counted under the manager lock before anything is registered or started, and released on every completion path. Moving finish under the manager lock is safe. finish only flips state under the session lock and closes channels, so it can't block, and every path takes the manager lock before the session lock, so there's no inversion.

Each piece is load-bearing:

  • Dropping the admission check fails TestSessionManagerAdmissionBound and TestSessionManagerCancellationReleasesAdmission.
  • Dropping the release on completion fails both of those too.
  • Dropping the prune on completion fails TestSessionManagerPrunesOnCompletion.
  • Registering an already-canceled start fails the cancellation test.

internal/daemon passes natively on Windows, and under -race three times over for the session tests. CI is 9 of 9 at head. Approving.

@euxaristia euxaristia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The admission bound is correct (counter under lock, refuses with a typed error, decrement plus prune after the run). One gap worth a follow-up: the decrement is not panic-safe (no defer around pool.Run), so a panicking worker would leak a pending slot until the 256 cap bricks admission.

This branch has not been deployed

No deployments
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.

daemon: queued sessions have no admission bound and over-cap retention is not pruned on completion

4 participants