Skip to content

test: fix the flakey tests for bigtable readrows - #9450

Merged
danieljbruce merged 11 commits into
mainfrom
patch-readrows-tests
Sep 25, 2026
Merged

danieljbruce merged 11 commits into
mainfrom
patch-readrows-tests

Conversation

@danieljbruce

Copy link
Copy Markdown
Contributor

Description

Resolves flaky test timeouts and hanging streams in the Bigtable ReadRows test suite:

  • Stream Lifecycle & Backpressure Handling: In handwritten/bigtable/test-common/utils/readRowsImpl.ts, updated the backpressure wait logic to listen for stream termination events (close, error, finish) in addition to drain. This ensures pending promises unblock immediately if the stream terminates while awaiting backpressure.
  • Client Cancellation & Disconnection: Added an onStreamEnded handler to ReadRowsImpl to mark requests as cancelled and unblock waiting handlers on close and error events alongside cancelled, preventing chunk-generation loops from hanging when a client drops or finishes early.
  • Test Timeouts & Setup: In handwritten/bigtable/test/readrows.ts, increased setWindowsTestTimeout from 60s to 200s and applied it to tests processing large chunk volumes under heavy CI load (such as Windows runners). Added explicit projectId: 'fake-project' during client initialization to avoid unnecessary metadata server lookups.
  • Documentation: Added explanatory comments throughout both files clarifying stream lifecycle management, backpressure resolution, and test timeout rationale.

Impact

  • Test Stability: Eliminates intermittent timeout failures in Bigtable unit and integration tests across CI environments.
  • Resource Cleanup: Prevents leaked or hung promises in test mock servers when streaming requests are aborted or closed prematurely.
  • Scope: Internal test utilities and test suite only; no breaking changes or impact to production runtime code.

@danieljbruce
danieljbruce requested a review from a team as a code owner September 24, 2026 20:28
@github-actions
github-actions Bot requested a review from feywind September 24, 2026 20:28

@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 improves stream termination and backpressure handling in the Bigtable test utilities to prevent hanging, and increases test timeouts to reduce flakiness on slow CI environments. Feedback highlights two critical issues: a potential event listener leak due to uncleaned .once() listeners on the stream, and a race condition where onStreamEnded could be invoked multiple times and prematurely resolve subsequent backpressure promises.

Comment on lines 301 to 307
await new Promise<void>(resolve => {
this.stopWaiting = resolve;
stream.once('drain', resolve);
stream.once('close', resolve);
stream.once('error', resolve);
stream.once('finish', resolve);
});

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.

high

Registering multiple .once() listeners on the same stream to resolve a single promise can lead to an event listener leak. When one of the events (e.g., 'drain') fires, the promise resolves, but the other listeners ('close', 'error', 'finish') remain registered on the stream. Over time, this can accumulate and trigger a MaxListenersExceededWarning or cause unexpected behavior.

To prevent this, we should clean up all registered listeners when the promise resolves.

          await new Promise<void>(resolve => {
            const onEvent = () => {
              stream.off('drain', onEvent);
              stream.off('close', onEvent);
              stream.off('error', onEvent);
              stream.off('finish', onEvent);
              resolve();
            };
            this.stopWaiting = onEvent;
            stream.once('drain', onEvent);
            stream.once('close', onEvent);
            stream.once('error', onEvent);
            stream.once('finish', onEvent);
          });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved

Comment on lines +366 to +369
const onStreamEnded = () => {
readRowsRequestHandler.cancelled = true;
readRowsRequestHandler.stopWaiting();
};

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.

high

The onStreamEnded handler can be invoked multiple times. For example, when a 'cancelled' event is received, it calls onStreamEnded() and then synchronously emits an 'error' event (stream.emit('error', ...)), which triggers the 'error' listener and calls onStreamEnded() a second time.

If onStreamEnded is called multiple times, and the request handler loop has already moved on to a new backpressure promise (overwriting this.stopWaiting), the second invocation will prematurely resolve the new promise.

To prevent this race condition, use a boolean flag to ensure onStreamEnded only executes its logic once.

    let streamEnded = false;
    const onStreamEnded = () => {
      if (streamEnded) return;
      streamEnded = true;
      readRowsRequestHandler.cancelled = true;
      readRowsRequestHandler.stopWaiting();
    };

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed a change to address this.

@shivanee-p

Copy link
Copy Markdown
Contributor

/gemini review

@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 improves stream handling and test stability in the Bigtable test utilities. Key changes include listening to termination events on backpressure to prevent hanging, cleaning up event listeners to avoid leaks, and increasing test timeouts to prevent flaky CI failures. Additionally, an optimization was added to skip proto compilation if the generated files already exist. The review feedback suggests resetting 'this.stopWaiting' to a no-op function to prevent potential memory leaks, and updating the proto existence check in 'compile-protos.sh' to also verify 'protos.json' to avoid incomplete builds.

Comment thread handwritten/bigtable/test-common/utils/readRowsImpl.ts
Comment thread handwritten/bigtable/testproxy/compile-protos.sh Outdated
danieljbruce and others added 2 commits September 25, 2026 11:38
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@danieljbruce
danieljbruce enabled auto-merge (squash) September 25, 2026 15:38
@danieljbruce
danieljbruce merged commit 9941049 into main Sep 25, 2026
51 checks passed
@danieljbruce
danieljbruce deleted the patch-readrows-tests branch September 25, 2026 15:53
danieljbruce added a commit that referenced this pull request Sep 29, 2026
## Description

Resolves flaky test timeouts and hanging streams in the Bigtable
`ReadRows` test suite:

- **Stream Lifecycle & Backpressure Handling**: In
`handwritten/bigtable/test-common/utils/readRowsImpl.ts`, updated the
backpressure wait logic to listen for stream termination events
(`close`, `error`, `finish`) in addition to `drain`. This ensures
pending promises unblock immediately if the stream terminates while
awaiting backpressure.
- **Client Cancellation & Disconnection**: Added an `onStreamEnded`
handler to `ReadRowsImpl` to mark requests as cancelled and unblock
waiting handlers on `close` and `error` events alongside `cancelled`,
preventing chunk-generation loops from hanging when a client drops or
finishes early.
- **Test Timeouts & Setup**: In `handwritten/bigtable/test/readrows.ts`,
increased `setWindowsTestTimeout` from 60s to 200s and applied it to
tests processing large chunk volumes under heavy CI load (such as
Windows runners). Added explicit `projectId: 'fake-project'` during
client initialization to avoid unnecessary metadata server lookups.
- **Documentation**: Added explanatory comments throughout both files
clarifying stream lifecycle management, backpressure resolution, and
test timeout rationale.

## Impact

- **Test Stability**: Eliminates intermittent timeout failures in
Bigtable unit and integration tests across CI environments.
- **Resource Cleanup**: Prevents leaked or hung promises in test mock
servers when streaming requests are aborted or closed prematurely.
- **Scope**: Internal test utilities and test suite only; no breaking
changes or impact to production runtime code.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[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