Skip to content

perf(bigquery): [T1-9] replace LinkedBlockingDeque with ArrayBlockingQueue and batch-drain in ConnectionImpl - #24

Open
lqiu96 wants to merge 1 commit into
mainfrom
perf/bq-t1-09-connection-arrayblockingqueue
Open

lqiu96 wants to merge 1 commit into
mainfrom
perf/bq-t1-09-connection-arrayblockingqueue

Conversation

@lqiu96

@lqiu96 lqiu96 commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Summary & Bottleneck

  • Target files:
    • java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/ConnectionImpl.java (L547-L570, L601-L619, L1030)
    • java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryResultImpl.java (L195-L225)
  • Scaling cardinality: $O(\text{rows})$ node allocations and $O(\text{rows})$ lock/unlock cycles across producer and consumer threads.
  • Bottleneck details:
    1. ConnectionImpl used LinkedBlockingDeque for bufferFvl, bufferRow, pageCache, and rpcResponseQueue. Every buffer.put(row) allocated a 24-byte LinkedBlockingDeque$Node object on the heap and acquired a single shared ReentrantLock contended by both the producer and consumer threads.
    2. BigQueryResultImpl.BigQueryResultSet.next() called buffer.take() one row at a time, acquiring and releasing the queue lock on every single row.

Changes

  • Bounded ArrayBlockingQueue ring buffers in ConnectionImpl:
    • Replaced LinkedBlockingDeque with ArrayBlockingQueue for bufferFvl, bufferRow, pageCache, and rpcResponseQueue in tableDataList, processQueryResponseResults, and highThroughPutRead.
  • Consumer-side batch draining in BigQueryResultSet.next():
    • Added a consumer-local ArrayDeque<T> localBatch (capacity 256) in BigQueryResultSet.
    • When localBatch is empty, next() takes the first available element via buffer.take() and immediately calls buffer.drainTo(localBatch, 256) under a single lock acquisition. Subsequent next() calls serve rows from localBatch without lock contention.

Expected Impact

  • Eliminates $R$ LinkedBlockingDeque$Node allocations (24 MB per 1M rows) by pre-allocating a contiguous ring buffer array.
  • Amortizes consumer ReentrantLock acquisitions in ResultSet.next() by up to 256x when buffered rows are available.

Verification

  • Ran mvn test -pl java-bigquery/google-cloud-bigquery -Dtest=BigQueryResultImplTest,ConnectionImplTest (26 tests run, 0 failures).
  • Added BigQueryResultImplTest.testResultSetBatchDrainingPreservesFifoOrderAndEof to verify FIFO ordering and EOF handling across 600 rows (spanning multiple 256-row drain batches).

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.

1 participant