Skip to content

fix(limiter): release permit when inner factory rejects the call - #35

Merged
costela merged 1 commit into
mainfrom
leo.antunes/release-permit-on-inner-error
Aug 4, 2026
Merged

fix(limiter): release permit when inner factory rejects the call#35
costela merged 1 commit into
mainfrom
leo.antunes/release-permit-on-inner-error

Conversation

@costela

@costela costela commented Aug 4, 2026

Copy link
Copy Markdown
Member

Both ConcurrencyLimiter variants acquire a permit before delegating inward, so an error from the inner factory (e.g. ErrCircuitOpen) has to release it again — no Observer is returned in that case, so nothing else ever would.

Leaking there is terminal: the limiter sits outside the circuit's state check, so every call dropped while the circuit is open permanently shrinks the effective limit, until all calls are rejected with ErrConcurrencyLimitReached and none can reach the circuit to ever close it again.

Also documents the obligation on the ObserverFactory contract, since it applies to any middleware claiming resources before delegating.

🤖 Generated with Claude Code

Both limiter variants acquire a permit before delegating inward, so an
error from the inner factory has to release it again: no Observer is
returned in that case, so nothing else ever would.

Leaking here is terminal. The limiter sits outside the circuit's state
check, so every call dropped while the circuit is open permanently
shrinks the effective limit, until all calls are rejected with
ErrConcurrencyLimitReached and none can reach the circuit to ever close
it again.

Document the obligation on the ObserverFactory contract, since it
applies to any middleware that claims resources before delegating.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@costela
costela requested review from totallyunknown and yvz5 and a lite review from Copilot August 4, 2026 08:16
@costela
costela marked this pull request as ready for review August 4, 2026 08:17

Copilot AI 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.

Pull request overview

This PR fixes a permit leak in the ConcurrencyLimiter middleware when the wrapped ObserverFactory rejects a call (returns an error). Without releasing the already-acquired permit on that error path, the limiter can permanently drain its capacity—especially when placed outside a circuit breaker’s state check—eventually preventing any calls from reaching the circuit so it can recover.

Changes:

  • Release the semaphore permit when the inner ObserverFactory returns an error after the limiter has acquired a permit.
  • Add a regression test covering both blocking and non-blocking limiter variants for the inner-error path.
  • Document the ObserverFactory contract obligation to clean up any claimed resources before returning an error.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
limiter.go Releases acquired permits when the inner factory errors, preventing terminal capacity leaks.
limiter_test.go Adds a regression test ensuring permits are released when the inner factory rejects calls.
hoglet.go Documents the ObserverFactory error-path responsibility to release claimed resources.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@costela
costela merged commit fa5a8f9 into main Aug 4, 2026
3 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.

3 participants