Client
BigQuery Storage Write — bigquery/storage/managedwriter
Description
Client.resolvePool (bigquery/storage/managedwriter/client.go) holds the client-wide c.mu across the GetWriteStream RPC it uses to discover a stream's region:
func (c *Client) resolvePool(ctx context.Context, settings *streamSettings, streamFunc streamClientFunc) (*connectionPool, error) {
c.mu.Lock()
defer c.mu.Unlock()
resp, err := c.getWriteStream(ctx, settings.streamID, false) // network RPC, under the lock
...
}
That GetWriteStream RPC touches no shared state (only the immutable rawClient) — the lock exists solely to guard the pools map. Holding it across the RPC serializes every concurrent NewManagedStream/resolvePool on a client behind a single ~one-RTT call.
Impact: for workloads that open streams frequently (e.g. opening a stream per write on the _default stream), open latency grows linearly with concurrency. Measured cross-region (AWS us-west-2 → BigQuery US multi-region): a single uncontended open is ~115ms, but at 20 concurrent opens on one client, open latency rises to ~1s (≈ 115ms × workers) and throughput plateaus because opens cannot overlap. The RPC itself is fast; the mutex scope is the bottleneck. Present through the latest release and main.
Proposed fix: move the GetWriteStream call outside the lock and take c.mu only for the pools map lookup/insert. The lookup and createPool stay atomic under the lock, so the create-once-per-location invariant is unchanged. I have a branch + regression test ready and will open a PR referencing this issue.
Client
BigQuery Storage Write —
bigquery/storage/managedwriterDescription
Client.resolvePool(bigquery/storage/managedwriter/client.go) holds the client-widec.muacross theGetWriteStreamRPC it uses to discover a stream's region:That
GetWriteStreamRPC touches no shared state (only the immutablerawClient) — the lock exists solely to guard thepoolsmap. Holding it across the RPC serializes every concurrentNewManagedStream/resolvePoolon a client behind a single ~one-RTT call.Impact: for workloads that open streams frequently (e.g. opening a stream per write on the
_defaultstream), open latency grows linearly with concurrency. Measured cross-region (AWS us-west-2 → BigQuery US multi-region): a single uncontended open is ~115ms, but at 20 concurrent opens on one client, open latency rises to ~1s (≈ 115ms × workers) and throughput plateaus because opens cannot overlap. The RPC itself is fast; the mutex scope is the bottleneck. Present through the latest release andmain.Proposed fix: move the
GetWriteStreamcall outside the lock and takec.muonly for thepoolsmap lookup/insert. The lookup andcreatePoolstay atomic under the lock, so the create-once-per-location invariant is unchanged. I have a branch + regression test ready and will open a PR referencing this issue.