Skip to content

Fix retire/release race condition when resource close yields#25

Open
wtn wants to merge 1 commit intosocketry:mainfrom
wtn:race
Open

Fix retire/release race condition when resource close yields#25
wtn wants to merge 1 commit intosocketry:mainfrom
wtn:race

Conversation

@wtn
Copy link

@wtn wtn commented Feb 12, 2026

When retire is called on a resource whose close yields (e.g. an HTTP/2 connection sending GOAWAY), another fiber can call release on the same resource and hit RuntimeError: Trying to reuse unacquired resource.

This occurs because retire deletes from @resources before calling resource.close. If close yields, the resource is no longer tracked but still reports reusable? = true, so releasereuse finds nil usage and raises.

How we triggered it

Using async-http with HTTP/2 under sustained load (~500+ req/sec), server-initiated connection resets trigger this regularly, affecting ~1.5% of requests in our scenario. The background reader calls pool.retire(connection), then Async::HTTP::Client#call's rescue handler calls pool.release(connection) on the same resource. The RuntimeError replaces the original retriable error, so requests fail instead of being retried.

The fix

  • @retiring set tracks resources currently mid-close inside retire
  • retire is made idempotent — returns false if the resource was already deleted
  • reuse returns false when the resource is in @retiring, so release silently handles the already-retired resource
  • Releasing a resource that was genuinely never acquired still raises as before

Types of Changes

  • Bug fix.

Contribution

Note

Any changes are OK with me.

Co-authored-by: Claude <noreply@anthropic.com>
@wtn
Copy link
Author

wtn commented Feb 13, 2026

Force-pushed to fix Rubocop violations.

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