Skip to content

Release pooled connection after DELETE to prevent pool exhaustion#1327

Open
buehlmann wants to merge 1 commit into
gitlab4j:mainfrom
buehlmann:fix/delete-connection-leak
Open

Release pooled connection after DELETE to prevent pool exhaustion#1327
buehlmann wants to merge 1 commit into
gitlab4j:mainfrom
buehlmann:fix/delete-connection-leak

Conversation

@buehlmann

@buehlmann buehlmann commented Jun 30, 2026

Copy link
Copy Markdown

AbstractApi.delete(...) returned the JAX-RS Response without reading its entity or closing it. Most delete callers are void (e.g. GroupApi.deleteGroup, ProjectApi.deleteProject) and discard the returned Response, so with a pooling/Apache connector (used for proxied clients) the underlying connection is never returned to the pool. GitLabs delete endpoints answer 200/202 with a body, so each such delete leaks one pooled connection; with the default of 2 connections per route the pool is quickly exhausted and subsequent requests block indefinitely in AbstractConnPool.getPoolEntryBlocking.

This issue was not visible before #1312: setting client properties per request kept the ClientConfig RequestScoped, so the connector (and its pool) was rebuilt per request and any leaked connection was discarded with it. Since #1312 the ClientConfig is a singleton and the pool is shared, so the leak now accumulates and exhausts the pool.

Buffer the delete body via Response.bufferEntity() in the delete() helpers. This releases the connection immediately while keeping the Response readable for the few callers that consume it (LicenseApi.deleteLicense, IssuesApi.deleteIssueLink, EpicsApi.removeIssue). get/post/put are unaffected since those callers already release the connection via readEntity().

This fix was tested against a self-hosted GitLab instance, both with and without a proxy. In both cases, all groups were deleted successfully, and no connection leaks were observed.

This PR fixes #1328

AbstractApi.delete(...) returned the JAX-RS Response without reading its
entity or closing it. Most delete callers are void (e.g. GroupApi.deleteGroup,
ProjectApi.deleteProject) and discard the returned Response, so with a
pooling/Apache connector (used for proxied clients) the underlying connection
is never returned to the pool. GitLabs delete endpoints answer 200/202 with a
body, so each such delete leaks one pooled connection; with the default of 2
connections per route the pool is quickly exhausted and subsequent requests
block indefinitely in AbstractConnPool.getPoolEntryBlocking.

This issue was not visible before gitlab4j#1312: setting client properties per request
kept the ClientConfig RequestScoped, so the connector (and its pool) was rebuilt
per request and any leaked connection was discarded with it. Since gitlab4j#1312 the
ClientConfig is a singleton and the pool is shared, so the leak now accumulates
and exhausts the pool.

Buffer the delete body via Response.bufferEntity() in the delete() helpers.
This releases the connection immediately while keeping the Response readable
for the few callers that consume it (LicenseApi.deleteLicense,
IssuesApi.deleteIssueLink, EpicsApi.removeIssue). get/post/put are unaffected
since those callers already release the connection via readEntity().
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.

DELETE calls leak pooled HTTP connections, exhausting the pool (Apache connector / proxy)

1 participant