Repository navigation
fix(bigtable): session client — single ownership + single-flight pool loading - #20368
Merged
mutianf merged 2 commits intoAug 13, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
Code Review
This pull request introduces a nonce field to poolKey to ensure each Open* call owns a private pair of pools, preventing a shared-pool race. The sessionClient now uses an atomic counter to generate unique nonces for tables, authorized views, and materialized views. Feedback suggests adding a test case with a non-zero nonce to verify that displayName ignores the nonce field, preventing potential OTel metric cardinality issues.
… loading
Rebuild the session-pool ownership model to mirror Java's shape so a
cold-start burst can no longer close a pool that a live sibling still
uses (the ErrPoolClosed-forever bug).
Two structural changes replace the previous shared-key registry:
* Single ownership. Each buildLazyOpener invocation mints its OWN
pool (no keyed dedup) and hands the pool's teardown to a per-owner
poolCloser. sessionTable.closeRead/closeWrite are that owner's
single-shot Close, so a table only ever closes the pool ITS opener
created — by captured identity — never a sibling sessionTable's pool
that happens to share a (resource, permission). sessionPools becomes
an id-keyed observational index (sessionz snapshots, config-listener
fanout, Close-all) rather than the ownership/dedup key. poolCloser
also handles the Close-straddles-open race: if the table is torn
down before the opener finishes wiring, set() closes the fresh pool
on the spot instead of leaking it.
* Single-flight loading. TableCache.GetOrOpen is now single-flight per
key via a loading map + ready channel, matching Guava LoadingCache's
atomic single-load contract. A post-eviction RPC storm collapses
onto one openFn call and installs exactly one successor pool instead
of dialing N and discarding N-1. The closed-gate still releases a
handle whose open straddled cache.Close.
This supersedes the earlier nonce bolt-on, which kept the shared-key
registry and left a dead dedup lookup.
Change-Id: I3e664ab411e4b4d468a7ef44e117fe76d0f28112
mutianf
force-pushed
the
refactor-bigtable-session-loading-cache
branch
from
August 12, 2026 15:30
44a6820 to
e4dc647
Compare
sushanb
reviewed
Aug 12, 2026
2 tasks
Harden the single-flight loader and pool-teardown paths per PR review: - TableCache.GetOrOpen: clear the loading slot and wake waiters in a defer so a panic deep in openFn (buildLazyOpener → createSessionPoolForPayload → pool construction) can't leave the slot wedged forever. Previously the inline cleanup after openFn was unreachable on panic, deadlocking every subsequent GetOrOpen(key) and wedging the resource until restart. Add a regression test. - buildLazyOpener: capture and join the teardown error from owner.set(release) instead of discarding it, so a failed drain when the sessionTable was closed mid-dial surfaces rather than being swallowed. - Add a single-ownership test pinning that two Open* calls resolving the same poolKey get distinct pools and independent id-scoped closers. - Drop stale "Java parity" phrasing from an idempotency test comment. Change-Id: Ie7f8b144dbb1a12f9393025b6e6d51f391cc07a0
sushanb
approved these changes
Aug 13, 2026
mutianf
enabled auto-merge (squash)
August 13, 2026 15:32
sushanb
pushed a commit
that referenced
this pull request
Aug 28, 2026
🤖 I have created a release *beep* *boop* --- ## [1.53.0](bigtable/v1.52.0...bigtable/v1.53.0) (2026-08-27) ### Features * **bigtable/accelerator:** Add --user-agent flag to daemon ([#20341](#20341)) ([6b33c06](6b33c06)) * **bigtable/accelerator:** Add daemon binary with identity verification ([#20330](#20330)) ([2f9cf89](2f9cf89)) * **bigtable/accelerator:** Add proxy channel scaffold owning a sessi… ([#20310](#20310)) ([d65ca7d](d65ca7d)) * **bigtable/accelerator:** Add stdin-EOF and parent-PID lifecycle wa… ([#20328](#20328)) ([dc1c6ae](dc1c6ae)) * **bigtable/accelerator:** Add UDS server with authenticated proxy i… ([#20320](#20320)) ([a837020](a837020)) * **bigtable/accelerator:** Add V2-to-session request adapters ([#20271](#20271)) ([4cb6f16](4cb6f16)) * **bigtable/accelerator:** Authenticate UDS callers with a stdin tok… ([#20324](#20324)) ([7847ab7](7847ab7)) * **bigtable/accelerator:** Dispatch MutateRow and single-row ReadRow… ([#20312](#20312)) ([c89f336](c89f336)) * Update API sources and regenerate ([#20369](#20369)) ([bfe3aaf](bfe3aaf)) * Update sources and regenerate ([#20399](#20399)) ([a6b5cdb](a6b5cdb)) ### Bug Fixes * **bigtable/internal/transport:** Drop redundant defer p.Close() in AFE picker tests ([#20311](#20311)) ([c05b767](c05b767)) * **bigtable:** Register c2p resolver + RLS balancer for accelerator daemon DirectPath ([#20362](#20362)) ([95f372f](95f372f)) * **bigtable:** Session client — set AllowNonDefaultServiceAccount on DirectPath dial ([#20343](#20343)) ([b9c76ec](b9c76ec)) * **bigtable:** Session client — single ownership + single-flight pool loading ([#20368](#20368)) ([f4ad950](f4ad950)) * **bigtable:** Session pool — re-drive creation after a never-activated close so the consecutive-failure breaker is reachable ([#20355](#20355)) ([ee74949](ee74949)) * **bigtable:** Use composed user agent as CSM client_name ([#20383](#20383)) ([cb02335](cb02335)) --- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Approach 2 of 2 — Java-shape single ownership + single-flight loading
Second of two alternative fixes for the cold-start burst bug where a session pool is
Close()d and never reopens, leaving every op for the run returningErrPoolClosed. (Approach 1, the localizedopened()-gate + refcount patch, is #20366.)The bug
A pool was keyed only by
(resource, permission), so everysessionTablefor the same resource shared one entry insessionPools. Two independent races then closed a pool a live sibling still referenced:TableCache.GetOrOpenrunsopenFnfor all concurrent missers and discards the losers viaapi.Close(). The loser's release closed the shared pool the winner had already memoized in itslazyPool.sessionTablethat picks up the still-mapped pool during the delete-then-close window; the old handle's release then closes it out from under the new one.Either way
lazyPoolmemoizes the closed Invoker forever and there is no path to reopen →ErrPoolClosedfor the rest of the run.The fix
Mirror the java-bigtable shape faithfully: the cache value solely owns its pool, and loads are single-flight. Two structural changes replace the shared-key registry.
1. Single ownership. Each
buildLazyOpenerinvocation mints its OWN pool (no keyed dedup) and hands the pool's teardown to a per-ownerpoolCloser.sessionTable.closeRead/closeWriteare that owner's single-shotClose, so a table only ever closes the pool ITS opener created — by captured identity — never a siblingsessionTable's pool that happens to share a(resource, permission).getOrCreateSessionPool/releaseSessionPool/buildLazyReleaserare gone;createSessionPoolalways creates and returns an id-scoped releaser, andreleasePoolByIDtears down by id.sessionPoolsbecomes an id-keyed observational index (sessionz snapshots, config-listener fanout,Client.Closeclose-all) rather than the ownership/dedup key.poolKeyloses its nonce and survives only to label the OTelsession_namemetric and order snapshots.poolCloseralso handles the Close-straddles-open race: if the table is torn down before the opener finishes wiring,set()closes the fresh pool on the spot instead of leaking it.2. Single-flight loading.
TableCache.GetOrOpenis now single-flight per key via aloadingmap + ready channel, matching GuavaLoadingCache's atomic single-load contract. A post-eviction RPC storm collapses onto oneopenFncall and installs exactly one successor pool instead of dialing N and discarding N-1. The closed-gate still releases a handle whose open straddledcache.Close(the interleaving that previously deadlocked a naive single-flight is handled by keeping theloadStateseparate from the always-completeTableHandle).Notes
releaseSessionPooltests → id-basedreleasePoolByIDtests;buildLazyOpenertest gains the owner arg; the eviction-storm test's assertions are unchanged (they already tolerated single-flight) but its stale "NOT single-flight" / "loser-close" comments are corrected.newSessionTable/lazyPoolsignatures are unchanged. Full session suite passes under-race.