Visitar URL original
fix(bigtable): replace SessionPoolImpl Watchdog with per-session closeTimeout by mutianf · Pull Request #14977 · googleapis/google-cloud-java · GitHub
Skip to content

fix(bigtable): replace SessionPoolImpl Watchdog with per-session closeTimeout - #14977

Merged
mutianf merged 5 commits into
googleapis:mainfrom
mutianf:fix-session-pool-watchdog-leak
Nov 12, 2026
Merged

mutianf merged 5 commits into
googleapis:mainfrom
mutianf:fix-session-pool-watchdog-leak

Conversation

@mutianf

@mutianf mutianf commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

SessionPoolImpl previously ran a recurring pool-level Watchdog on Client.sessionTimer that was only stopped inside awaitTerminated(), so callers that closed a pool without calling awaitTerminated() (such as TableBase.close() on cache eviction in compat.Util.createSessionMap) leaked the self-rescheduling watchdog task.

Remove SessionPoolImpl.Watchdog and instead arm a one-shot closeTimeout (CLOSE_SESSION_TIMEOUT = 5 minutes) on SessionImpl when entering WAIT_SERVER_CLOSE in startGracefulClose(), cancelled in updateState() as soon as the session leaves WAIT_SERVER_CLOSE (matching how openTimeout and heartbeatTimeout are managed per session). Also guard onSessionGoAway() against creating replacement sessions after the pool is closed.

…ining

SessionPoolImpl.close() previously left the watchdog running until awaitTerminated() was called. Callers that close a pool without calling awaitTerminated() (such as TableBase.close() when an idle table is evicted from compat.Util.createSessionMap) leaked the self-rescheduling watchdog on the client's shared HashedWheelTimer, retaining the closed SessionPoolImpl in memory.

Close the watchdog as soon as all sessions drain after close() (immediately if the pool is empty, or in onSessionClose when the last session terminates), and guard onSessionGoAway against creating replacement sessions after the pool is closed.
@mutianf
mutianf requested review from a team as code owners November 12, 2026 03:25

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request ensures that the watchdog timer is closed when the session pool is closed and all sessions have drained, preventing potential timer leaks when callers only invoke close() without calling awaitTerminated(). A new test is added to verify this behavior. The review feedback suggests using System.nanoTime() instead of System.currentTimeMillis() in the test's wait loop to ensure monotonic time measurements and avoid sensitivity to system clock adjustments.

Comment on lines +361 to +364
long deadlineMs = System.currentTimeMillis() + 10_000;
while (!sessionPool.watchdog.isClosed() && System.currentTimeMillis() < deadlineMs) {
Thread.sleep(10);
}

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.

medium

Using System.currentTimeMillis() to measure deadlines or elapsed time is sensitive to system clock adjustments (such as NTP updates). It is safer and more robust to use System.nanoTime() for monotonic time measurements in test wait loops.

Suggested change
long deadlineMs = System.currentTimeMillis() + 10_000;
while (!sessionPool.watchdog.isClosed() && System.currentTimeMillis() < deadlineMs) {
Thread.sleep(10);
}
long deadlineNano = System.nanoTime() + TimeUnit.SECONDS.toNanos(10);
while (!sessionPool.watchdog.isClosed() && System.nanoTime() < deadlineNano) {
Thread.sleep(10);
}

Remove SessionPoolImpl.Watchdog and instead arm a one-shot closeTimeout (CLOSE_SESSION_TIMEOUT = 5 minutes) on SessionImpl when entering WAIT_SERVER_CLOSE in startGracefulClose(), cancelled in updateState() when leaving WAIT_SERVER_CLOSE.

This eliminates the recurring per-pool watchdog timer task on Client.sessionTimer and matches how openTimeout and heartbeatTimeout are managed per session.
@mutianf mutianf changed the title fix(bigtable): stop SessionPoolImpl watchdog when sessions finish draining fix(bigtable): replace SessionPoolImpl Watchdog with per-session closeTimeout Nov 12, 2026
Remove unused Session.getLastStateChange(), add a unit test for onSessionGoAway on a closed pool using monotonic System.nanoTime(), pass FakeClock in sessionCloseTimeoutForcesClose, and update stale watchdog comments in Client and SessionPoolImplTest.
@mutianf
mutianf merged commit a15acfb into googleapis:main Nov 12, 2026
208 checks passed
@mutianf
mutianf deleted the fix-session-pool-watchdog-leak branch November 12, 2026 22:16
whowes pushed a commit that referenced this pull request Nov 13, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>1.97.0</summary>

##
[1.97.0](v1.96.0...v1.97.0)
(2026-11-12)


### ⚠ BREAKING CHANGES

* **shopping-merchant:** remove discontinued Merchant API v1beta
([#14942](#14942))

### Features

* **bigquery-jdbc:** adds BigQuery graph integration to DatabaseMetadata
methods
([#14930](#14930))
([950b81b](950b81b))
* **gax:** implement resumable upload session resumption
([#14677](#14677))
([24728d6](24728d6))
* **grpc-gcp:** add built-in channel_count metric for client library
pools
([#14947](#14947))
([712f043](712f043))
* **grpc-gcp:** add built-in channel_load metric for client library
pools
([#14985](#14985))
([9f87888](9f87888))
* **shopping-merchant:** remove discontinued Merchant API v1beta
([#14942](#14942))
([2947dfd](2947dfd))
* **storage:** add StorageErrorTypeMapper for standard metrics
([#14694](#14694))
([a9ac393](a9ac393))
* **storage:** add StorageMetricsContext for operation metrics state
([#14728](#14728))
([edea395](edea395))
* **storage:** wire logical operation metrics in Retrying and
StorageOptions
([#14695](#14695))
([589c66c](589c66c))
* update API sources and regenerate
([#14958](#14958))
([75944f3](75944f3))


### Bug Fixes

* **auth:** verify public/private key match with retries in mTLS
keystore loading
([#14829](#14829))
([096dbd6](096dbd6))
* **bigquery:** set QueryResultsFormat.ARROW in QueryArrow sample
([#14960](#14960))
([c98e7c3](c98e7c3))
* **bigtable:** avoid triggering per-RPC credential session fallback on
default call context
([#14961](#14961))
([eb06d2c](eb06d2c))
* **bigtable:** copy Metadata per GetClientConfiguration call
([#14950](#14950))
([278fe2f](278fe2f))
* **bigtable:** disable SwitchingChannelPool when
ChannelPoolConfiguration is empty
([#14911](#14911))
([fa24702](fa24702))
* **bigtable:** handle empty SessionPoolConfiguration in SessionPoolImpl
and PoolSizer
([#14912](#14912))
([cc7b235](cc7b235))
* **bigtable:** replace SessionPoolImpl Watchdog with per-session
closeTimeout
([#14977](#14977))
([a15acfb](a15acfb))
* **spanner-jdbc:** report database default isolation level
([#14929](#14929))
([7e7595a](7e7595a))
* **spanner:** fix PG SHOW isolation level statements
([#14904](#14904))
([bc917ed](bc917ed))


### Dependencies

* bump grpc to 1.85.1, opentelemetry to 1.67.0, and guava to 33.7.2-jre
([#14957](#14957))
([1a27a00](1a27a00))
* bump guava to 33.7.1-jre and jspecify to 1.0.1
([#14937](#14937))
([1441527](1441527))


### Documentation

* **spanner:** update CHANGELOG.md for java spanner releases
([#14935](#14935))
([956681e](956681e))
* **storage:** add Rapid Cache samples
([#14886](#14886))
([e714f62](e714f62))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
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.

2 participants