You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(opensearchtransport): serialize discovery warmup recalc under pool lock
Backport of the opensearch-project#981 follow-up race fixed on v5 (opensearch-project#989).
In createOrUpdateMultiNodePoolWithLock, the warmup recalculation, ready-list
partitioning, and mu.activeCount write ran after the pool lock was released.
Those touch mu-guarded fields (activeListCap, warmupRounds, warmupSkipCount,
activeCount) that resurrectWithLock also reads/writes under pool.mu. The caller
holds c.mu(W), which serializes them against metrics.snapshot() but not against
resurrection -- a data race. Hold allConnsPool.mu across the whole section; the
per-connection conn.mu is taken inside the loop, matching the pool.mu -> conn.mu
ordering already used by deferredStandbyPromotion.
Rename recalculateWarmupParams/getWarmupParams to *WithLock: every caller now
holds the pool lock (the discovery path was the last one that didn't), so the
suffix documents the invariant, consistent with the pool's other WithLock
methods. Comment-only intent; no behavior change from the rename.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -215,6 +215,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
215
215
216
216
### Fixed
217
217
218
+
- Fix a data race on the multi-server pool's warmup fields in the node-discovery path: `createOrUpdateMultiNodePoolWithLock` recalculated warmup parameters, partitioned the ready list, and wrote `mu.activeCount` after releasing the pool write lock, touching `mu`-guarded fields (`activeListCap`, `warmupRounds`, `warmupSkipCount`, `activeCount`) that `resurrectWithLock` reads and writes under `pool.mu`. The caller holds the transport lock, which serializes this against `metrics.snapshot()` but not against resurrection. The whole section now runs under `allConnsPool.mu`, with per-connection `conn.mu` taken inside the loop (the `pool.mu` -> `conn.mu` ordering already used by `deferredStandbyPromotion`). `recalculateWarmupParams`/`getWarmupParams` are renamed `*WithLock` to document that every caller now holds the pool lock (the discovery path was the last that did not)
218
219
- Fix node discovery hijacking the request stream with unverified, unreachable discovered nodes and masking the user-supplied seed-URL fallback. When discovered `publish_address` values are unroutable from the client (NAT'd or misconfigured clusters, e.g. a Kubernetes stack cluster in CI), a freshly discovered but never-health-checked node could be served to requests as a zombie -- failing every request with `no route to host` -- instead of returning `ErrNoConnections` and cascading to the reachable seed URL. Connections are now considered available for routing only when they are a user-supplied seed (assumed reachable) or a discovered node confirmed reachable, and every routing policy and pool (round-robin, role, coordinator, index/doc router, single-server, and multi-server pools) consistently honors that gate on both the enabled-bit and connection-selection paths, so the seed fallback serves requests until a discovered node health-checks clean ([#952](https://github.com/opensearch-project/opensearch-go/pull/952), [#954](https://github.com/opensearch-project/opensearch-go/pull/954), [#956](https://github.com/opensearch-project/opensearch-go/pull/956))
219
220
- Fix a data race (reported by the race detector in `TestClientCustomTransport`) between `multiServerPool.snapshot()` and node discovery: `snapshot()` read `activeListCap` after releasing the pool read lock, while `recalculateWarmupParams` writes it under the write lock during `DiscoveryUpdate`. `activeListCap`, `warmupRounds`, and `warmupSkipCount` were guarded by the pool lock only by convention (declared at the top level of the struct), which let the unlocked read look correct; they are now nested inside the pool's lock-guarded `mu` struct so every access is spelled `cp.mu.<field>` and the guard is structural, and `snapshot()` reads `activeListCap` while holding the read lock. For the same reason `healthCheck` is moved under `mu` (it is rewritten by `updateConnectionPool` on pool reuse); this also surfaced one discovery-path read of `healthCheck` that had escaped the lock, now taken under the read lock
220
221
- Fix `BulkIndexerStats.NumAdded` overcounting items rejected by `Add()` when the caller's context is cancelled before the item could be enqueued: increment `NumAdded` only after the queue accepts the item, and add a new `BulkAddFailCount` counter for items dropped on the `<-ctx.Done()` branch. Migrate `bulkIndexerStats` fields to `sync/atomic.Uint64` typed values so future direct access is a compile-time error rather than a `-race`-only finding ([#783](https://github.com/opensearch-project/opensearch-go/issues/783))
0 commit comments