fix(#5026): HTTP transaction session lifecycle - remove on commit/rollback, sweep-race re-validation, ownership#5175
Conversation
|
Tick the box to add this pull request to the merge queue (same as
|
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 20 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Code Review
This pull request addresses several defects in the server-side HTTP transaction-session lifecycle (Issue #5026). It ensures that REST /commit and /rollback properly remove server-side sessions, re-validates session registration under lock to prevent races with idle-timeout sweeps, and enforces session ownership by invalidating live sessions when users are dropped, updated, or have their passwords changed. Additionally, a potential NullPointerException was identified in HttpSessionManager.getSessionById() if session.user is null, which should be guarded against to prevent runtime crashes.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| return null; | ||
| // Enforce ownership BEFORE the caller attaches the session to the thread context: a session must only be | ||
| // resolvable by the principal that opened it, so another user cannot adopt (and commit) its transaction. | ||
| if (user != null && !session.user.equals(user)) |
There was a problem hiding this comment.
If session.user is null (which is checked and handled defensively elsewhere in this class, such as on line 157), calling session.user.equals(user) will throw a NullPointerException. Guarding against a null session.user prevents potential runtime crashes when resolving sessions.
| if (user != null && !session.user.equals(user)) | |
| if (user != null && (session.user == null || !session.user.equals(user))) |
There was a problem hiding this comment.
Good catch - applied in 7cf87f9. getSessionById now guards session.user == null before equals, matching removeSessionsForUser; an ownerless session is never resolvable by an authenticated principal.
PR Review — #5175 HTTP transaction session lifecycleSolid, well-motivated fix. The three defects are real, the concurrency reasoning is carefully documented, and the sweep-vs-request race fix (re-validating A few points, one of which I think is a genuine regression worth addressing before merge. 1. (High)
|
|
Thanks for the thorough review. Addressed in 6913686 and 7cf87f9: 1. (High) 4. (Minor) Test assertion too broad. Tightened 5. (Nit) 2. (Minor) 3. (Minor) 401 → 404 for a stale session. Intentional and, as you say, more correct (the id is absent, not an auth failure). Noted in the tracking doc; worth a changelog line at release. |
Code Review: PR #5175 - HTTP transaction session lifecycle (#5026)Thorough, well-documented fix. The three defects are real, the root-cause analysis is accurate, and the locking discipline is careful. Below are strengths, then discussion points and a few gaps. What's done well
Discussion points
Minor / nits
Test coverageGood overall: unit tests exercise the manager/session directly and the IT covers commit/rollback removal, stale-write 404, idempotent retries, and drop-user invalidation across every server. Gaps: (a) no HTTP test for read ( Overall this is a solid, carefully-reasoned fix. The main thing I'd want an explicit decision on is point #1 (silent read degradation vs. an explicit signal), plus a test for it. |
|
Code Review - PR #5175 (fix #5026: HTTP transaction session lifecycle) Reviewed against CLAUDE.md conventions. Overall this is a strong, well-scoped fix: the three defects are real, the reasoning in the Javadoc/comments is excellent, lock ordering is consistent (session lock then manager lock everywhere; the sweep uses non-blocking tryLock, so no deadlock), and the test coverage (unit + HTTP IT) is genuinely good. A few things worth considering before merge. Correctness / concurrency
Behavioral note
Tests
Otherwise the IT coverage (commit/rollback remove session, stale write to 404, idempotent retry to 204, foreign-session commit is a no-op, drop-user invalidation) is exactly right, and the execute() re-validation happy-path/negative-path pair is a nice touch. Nits
Nothing here is a blocker; items 1 and 2 are the ones I would want a second look at. Good work. Automated review by Claude. |
|
Thanks - second round addressed in 1d799aa: 1. Silent read degradation + missing HTTP test. Added 2. 3. Ownership invariant / centralization. Added a comment on 4. Per-command 5. Import ordering in 3 (behavior)/concurrency test gap: the sweep-vs-request race is covered deterministically (removal ordering) rather than by racing real threads; the existing |
|
Thanks - addressed in daeda6b. (Note: this round reviewed 6913686, before 1d799aa, so items 2, 3-docs and the import nit were already fixed there.) 1. (Medium, liveness) Unbounded 2. 3. Read degradation contract. Documented in the tracking doc (behavior-changes section) and covered by the new 4. (Minor) Nit (import order). Already fixed in 1d799aa - All new + existing session/security tests remain green. |
Review: PR #5175 - HTTP transaction session lifecycle (#5026)Thorough, well-documented fix. The three defects are clearly separated, each with a targeted change and regression coverage, and the concurrency reasoning in the comments is genuinely careful. I checked the lock-ordering and the degradation paths and they hold up. A few notes below, one of which I think is worth addressing before merge. What's solid
Main concern (medium) - Defect 3 protection is leader-only in HAThe recreated-same-name defense relies on
Consequence: when a user is dropped on the leader, followers apply the change via The invariant comment you added in Minor
Style/conventionConsistent with the codebase - Nice work overall - the core fix is correct; the HA replication path is the one gap I would close before merge. Automated review; please verify the HA point against your understanding of session/replication topology. |
Review: PR #5175 - HTTP transaction session lifecycle (#5026)Overall this is a strong, well-scoped fix. The three defects are real, the reasoning is documented clearly in-code, the lock ordering is sound (invalidation releases the manager lock before the blocking Correctness / behavior
Tests
Minor / nits
Verified as fine
Nice work - the concerns above are refinements, not blockers. Reviewed by Claude |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5175 +/- ##
============================================
+ Coverage 66.00% 66.73% +0.73%
============================================
Files 1693 1694 +1
Lines 136917 137115 +198
Branches 29270 29317 +47
============================================
+ Hits 90368 91502 +1134
+ Misses 34306 33201 -1105
- Partials 12243 12412 +169 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…e-validation, ownership enforced Server-audit fix for three HTTP transaction-session lifecycle defects: - Defect 1: PostCommitHandler/PostRollbackHandler now unregister the server-side session after commit/rollback and are idempotent (guard with isTransactionActive, retry returns 204). An unresolved session id on a write-capable handler is rejected with an explicit HttpSessionException (HTTP 404) instead of running the command in a silent implicit auto-committing transaction; read-only and /begin,/commit,/rollback endpoints degrade to a session-less request so read-after-commit and idempotent retries keep working. - Defect 2: HttpSession.execute re-validates the session is still registered under the session lock before running its callback, so the idle-timeout sweep can no longer roll back and remove a session between the manager lookup and lock acquisition and leave the request operating on a rolled-back (leaked) transaction. - Defect 3: HttpSessionManager.getSessionById enforces ownership before attach; removeSessionsForUser plus ServerSecurity dropUser/setUserPassword/ updateUser invalidate a principal's live sessions so a dropped-and-recreated same-name principal cannot adopt the prior principal's open session. Tests: Issue5026HttpSessionLifecycleTest (unit) and Issue5026TransactionSessionLifecycleIT (HTTP end-to-end).
Guard against a null session.user when enforcing ownership in HttpSessionManager.getSessionById, matching the defensive null-handling already used in removeSessionsForUser. An ownerless session is never resolvable by an authenticated principal. Addresses review feedback.
…n tests Address review feedback: - High: removeSession(exchange) removed a session by raw header id with no ownership check, so a request carrying another principal's session id could evict that session from the manager without rolling it back, orphaning its transaction. removeSession now takes the authenticated user and only removes a session that user owns (via getSessionById); a foreign/stale id is a benign no-op. Regression test commitWithAnotherUsersSessionDoesNotOrphanIt. - Tighten executeRefusesToRunOnAnUnregisteredSession to assert HttpSessionException.
…radation test Address second-round review feedback: - updateUser no longer force-rolls-back the principal's live transactions: a metadata update is benign and per-request authorization is still re-checked on every command. Only dropUser and setUserPassword invalidate live sessions (matching the issue's Defect 3 scope). - Document the ownership invariant in getSessionById: recreated-same-name protection relies on removeSessionsForUser being invoked from every user removal/mutation path. - Add HTTP test staleSessionOnReadDegradesToSessionlessRead pinning the read-after-commit degradation contract at the HTTP layer. - Fix import ordering in ServerSecurity.
…havior; strengthen test Address third-round review feedback: - dropUser now performs the users-map mutation under the security monitor but runs session invalidation (which can block on an in-flight command via cancel()) OUTSIDE the monitor, so a long-running victim command no longer stalls other admin ops serialized on it. setUserPassword was already unsynchronized. - Strengthen recreatedSameNameUserCannotAdoptPriorSession with a companion assertion proving a same-name principal WOULD resolve the session until invalidation removes it - i.e. removal, not the identity check, is the guard. - Document the stale-session behavior contract (read degradation vs write 404, drop/password invalidation) in the tracking doc for the changelog.
daeda6b to
247a9fb
Compare
Review: HTTP transaction session lifecycle (#5026)Reviewed the full diff plus the surrounding code ( Strengths
Findings1. (Medium - security consistency) Password change via 2. (Low) Narrow race in 3. (Low - test) Defect 2 (sweep-vs-request race) is only simulated, not exercised concurrently. Minor / nits
Nice work overall - the fixes are correct and the reasoning in the comments will age well. Finding #1 is the one I'd resolve before merge. |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 20 |
🟢 Coverage 98.18% diff coverage · +28.04% coverage variation
Metric Results Coverage variation ✅ +28.04% coverage variation Diff coverage ✅ 98.18% diff coverage Coverage variation details
Coverable lines Covered lines Coverage Common ancestor commit (23936cc) 137066 55403 40.42% Head commit (247a9fb) 168953 (+31887) 115662 (+60259) 68.46% (+28.04%) Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch:
<coverage of head commit> - <coverage of common ancestor commit>Diff coverage details
Coverable lines Covered lines Diff coverage Pull request (#5175) 55 54 98.18% Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified:
<covered lines added or modified>/<coverable lines added or modified> * 100%
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Bumps `swagger.version` from 2.2.50 to 2.2.51. Updates `io.swagger.core.v3:swagger-core` from 2.2.50 to 2.2.51 Release notes *Sourced from [io.swagger.core.v3:swagger-core's releases](https://github.com/swagger-api/swagger-core/releases).* > Swagger-core 2.2.51 released! > ----------------------------- > > * chore(deps-dev): bump logback-version from 1.5.25 to 1.5.32 ([ArcadeData#5149](https://redirect.github.com/swagger-api/swagger-core/issues/5149)) > * chore(deps): bump slf4j-version from 2.0.9 to 2.0.17 ([ArcadeData#5137](https://redirect.github.com/swagger-api/swagger-core/issues/5137)) Commits * [`6e2eabd`](swagger-api/swagger-core@6e2eabd) prepare release 2.2.51 ([ArcadeData#5194](https://redirect.github.com/swagger-api/swagger-core/issues/5194)) * [`6e74355`](swagger-api/swagger-core@6e74355) chore(deps-dev): bump logback-version from 1.5.25 to 1.5.32 ([ArcadeData#5149](https://redirect.github.com/swagger-api/swagger-core/issues/5149)) * [`1bc8c05`](swagger-api/swagger-core@1bc8c05) chore(deps): bump slf4j-version from 2.0.9 to 2.0.17 ([ArcadeData#5137](https://redirect.github.com/swagger-api/swagger-core/issues/5137)) * [`7d89d07`](swagger-api/swagger-core@7d89d07) bump snapshot 2.2.51-SNAPSHOT ([ArcadeData#5175](https://redirect.github.com/swagger-api/swagger-core/issues/5175)) * See full diff in [compare view](swagger-api/swagger-core@v2.2.50...v2.2.51) Updates `io.swagger.core.v3:swagger-annotations` from 2.2.50 to 2.2.51 Updates `io.swagger.core.v3:swagger-models` from 2.2.50 to 2.2.51
…0.0 in /e2e-python [skip ci] Updates the requirements on [setuptools](https://github.com/pypa/setuptools) to permit the latest version. Changelog *Sourced from [setuptools's changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst).* > v83.0.0 > ======= > > Features > -------- > > * Require Python 3.10 or later. > > Bugfixes > -------- > > * `MANIFEST.in` matching (via `FileList`) is now insensitive to Unicode > normalization form. A pattern authored in one form (e.g. NFC, as typically > saved by editors) now matches a file whose name is stored on disk in another > (e.g. NFD, as produced by macOS APFS/HFS+). Previously an `exclude`, > `global-exclude`, `recursive-exclude`, or `prune` rule could silently > fail to drop a non-ASCII-named file from the source distribution, publishing > it despite the exclusion -- see GHSA-h35f-9h28-mq5c. > > Deprecations and Removals > ------------------------- > > * `pypa/distutils#334` > > v82.0.1 > ======= > > Bugfixes > -------- > > * Fix the loading of `launcher manifest.xml` file. ([ArcadeData#5047](https://redirect.github.com/pypa/setuptools/issues/5047)) > * Replaced deprecated `json.__version__` with fixture in tests. ([ArcadeData#5186](https://redirect.github.com/pypa/setuptools/issues/5186)) > > Improved Documentation > ---------------------- > > * Add advice about how to improve predictability when installing sdists. ([ArcadeData#5168](https://redirect.github.com/pypa/setuptools/issues/5168)) > > Misc > ---- > > * [ArcadeData#4941](https://redirect.github.com/pypa/setuptools/issues/4941), [ArcadeData#5157](https://redirect.github.com/pypa/setuptools/issues/5157), [ArcadeData#5169](https://redirect.github.com/pypa/setuptools/issues/5169), [ArcadeData#5175](https://redirect.github.com/pypa/setuptools/issues/5175) > > v82.0.0 ... (truncated) Commits * [`6519f72`](pypa/setuptools@6519f72) Bump version: 82.0.1 → 83.0.0 * [`d1151b1`](pypa/setuptools@d1151b1) Merge pull request [ArcadeData#5250](https://redirect.github.com/pypa/setuptools/issues/5250) from pypa/feature/distutils-d7633fbed * [`a2df31e`](pypa/setuptools@a2df31e) Capture removal of dry\_run parameter in changelog. * [`00144dc`](pypa/setuptools@00144dc) Moved newsfragment to the release where it occurred. * [`a4a5a2b`](pypa/setuptools@a4a5a2b) Add news fragment. * [`77470c2`](pypa/setuptools@77470c2) Merge <https://github.com/pypa/distutils> into feature/distutils-d7633fbed * [`3c43897`](pypa/setuptools@3c43897) Merge pull request [ArcadeData#5247](https://redirect.github.com/pypa/setuptools/issues/5247) from pypa/copilot/fix-pypy-version-issue * [`bb6ea66`](pypa/setuptools@bb6ea66) Bump PyPy from 3.10 to 3.11 in CI workflow * [`a2bc3ac`](pypa/setuptools@a2bc3ac) Fix broken intersphinx reference to build's installation docs * [`2d6a739`](pypa/setuptools@2d6a739) Use stacked parametrize decorators instead of itertools.product * Additional commits viewable in [compare view](pypa/setuptools@v82.0.1...v83.0.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- Dependabot commands and options You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Fixes #5026 (2026-07 server audit). Three related defects in the server-side HTTP transaction-session lifecycle.
Defect 1 (HIGH) -
/commitand/rollbacknever removed the server-side sessionPostCommitHandler/PostRollbackHandleronly stripped the response header; the session stayed registered until the idle sweep. A stale session id kept resolving, so a follow-up write ran in an implicit auto-committing transaction while the client believed it was inside a transaction it could still roll back, and a retried/commit//rollbackreturned HTTP 500.Fix:
isTransactionActive(), so a retried call is an idempotent 204 (never 500).DatabaseAbstractHandler.setTransactionInThreadLocalrejects an unresolved session id on a write-capable handler (requiresTransaction()) with an explicitHttpSessionExceptionmapped to HTTP 404, instead of silently falling through to an auto-committing transaction. Read-only (GET /query) and the transaction endpoints (/begin,/commit,/rollback) degrade to a session-less request, preserving read-after-commit and idempotent retries.Defect 2 (MEDIUM) - idle-sweep vs in-flight request race leaked a transaction
The sweep could
cancelIfIdle()(rollback) and remove a session betweengetSessionById()andHttpSession.execute()acquiring the session lock; the request then ran on the rolled-backTransactionContext, and because the session was already untracked nothing ever committed/rolled back the fresh transaction it began.Fix:
HttpSession.execute()re-validates, under the session lock, that the session is still registered before running the callback; otherwise it throws (no callback runs on a swept transaction).Defect 3 (MEDIUM) - ownership checked by name only, and after attach
getSessionByIdignored theuserparam and ownership relied onServerSecurityUser.equals(by name), so a dropped-and-recreated same-name principal could adopt the prior principal's open session.Fix:
HttpSessionManager.getSessionByIdenforces ownership before attach (returns null for a non-owner).removeSessionsForUser(name);ServerSecurity.dropUser/setUserPassword/updateUserinvalidate the principal's live HTTP sessions (rolling back their transactions), so a recreated same-name principal starts clean.Acceptance criteria
/commit//rollbackthe session id is no longer resolvable; a follow-up write returns an explicit error, not a silent implicit-tx commit./commit//rollbackis idempotent (204, no 500).Tests
Issue5026HttpSessionLifecycleTest(unit): execute re-validation, ownership,removeSessionsForUser, recreated-same-name adoption.Issue5026TransactionSessionLifecycleIT(HTTP): commit/rollback remove the session, stale-session write rejected (404), retried commit/rollback idempotent (204), drop-user invalidates sessions so a recreated same-name user cannot adopt.All new tests pass; existing session/security tests remain green (
HTTPTransactionIT,Issue4141SessionManagementIT,HttpSessionTimeoutRaceTest,HTTPAuthSessionIT,UserManagementIT,AutoCommitParameterTest,ServerSecurityUsersConcurrencyTest). No wire-format change; server module only.