Skip to content

pool, license: close the shutdown race, warn when the pool ignores a tier change; release v1.0.5 - #32

Merged
ZergsLaw merged 2 commits into
masterfrom
fix/licence-ceilings-and-pool-shutdown
Oct 1, 2026
Merged

ZergsLaw merged 2 commits into
masterfrom
fix/licence-ceilings-and-pool-shutdown

Conversation

@ZergsLaw

@ZergsLaw ZergsLaw commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

WorkerPool shutdown race

Get checked an atomic closed flag and then sent on the job channel; Shutdown set the flag and then closed the channel. A Get between the two panicked with send on closed channel (recovered by the gRPC interceptor as INTERNAL), and a second Shutdown closed the channel twice.

  • The check and the non-blocking send now share a read lock; Shutdown takes the write lock and is idempotent.
  • serve.GRPC also calls GracefulStop when Serve returns an error, so live handlers drain before run() closes the pool and the database.
  • TestGetRacingShutdownNeverPanics and TestShutdownIsIdempotent both failed on the old code (6 panics in one run) and pass 30× under -race now.

Licence ceilings are read only at startup

worker_pool.workers and max_concurrent_generations are capped once, in initApp. A licence lapsing past grace switched audit and the plugin cap immediately but left the pool at its Enterprise capacity until the next restart, which then lowered it silently.

  • license.Manager remembers the startup claims and logs one Warn per transition when the pool ceilings no longer match.
  • MaxGenerations now counts as a licence change.
  • README (Licensing), AGENTS.md and the cappedByLicence godoc describe the behaviour.

Release

Bumps the chart, compose defaults and docs to v1.0.5.

Verification

  • go test -race ./... — green
  • golangci-lint on the touched packages — 0 issues
  • No test for the serve.GRPC failure branch: internal/serve has no tests, and forcing Serve to fail needs listener plumbing.

🤖 Generated with Claude Code

Edgar Sipki and others added 2 commits October 1, 2026 17:19
…ier change

WorkerPool.Get checked an atomic flag and then sent on the job channel, while
Shutdown set the flag and then closed it; a Get between the two sent on a
closed channel and panicked, and a second Shutdown closed it twice. Normal
shutdown never got there only because GracefulStop drains handlers before the
deferred Shutdown — an ordering the pool did not own, and one serve.GRPC skipped
when Serve itself failed. The check and the send now share a read lock, Shutdown
is idempotent, and a failed Serve drains its handlers too.

The pool reads its licence ceilings once at startup. A licence lapsing past
grace moved audit and the plugin cap at once and the pool only on the next
restart, silently. The licence manager now logs a Warn when that gap opens,
counts MaxGenerations as a change, and the README and AGENTS.md say so.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
WorkerPool shutdown no longer panics on a racing Get or a second Shutdown; the
licence manager warns when a tier change will reach the pool only on restart.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ZergsLaw
ZergsLaw enabled auto-merge October 1, 2026 14:23
@ZergsLaw
ZergsLaw merged commit b00b63b into master Oct 1, 2026
12 checks passed
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