Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public void testCreateSessionDeadlineExceededWithNoSessionCreateWaitTime() throw
// Simulate a problem with the CreateSession RPC making it slow.
mockSpanner.setCreateSessionExecutionTime(
SimulatedExecutionTime.ofException(Status.DEADLINE_EXCEEDED.asRuntimeException()));
mockSpanner.freezeAfter(1);
mockSpanner.freeze();

Spanner testSpanner =
SpannerOptions.newBuilder()
Expand All @@ -119,17 +119,22 @@ public void testCreateSessionDeadlineExceededWithNoSessionCreateWaitTime() throw
DatabaseClientImpl client =
(DatabaseClientImpl) testSpanner.getDatabaseClient(DatabaseId.of("p", "i", "d"));

// The first attempt should lead to a DEADLINE_EXCEEDED error being propagated from the
// CreateSession attempt.
try (ResultSet resultSet = client.singleUse().executeQuery(STATEMENT)) {
SpannerException exception = assertThrows(SpannerException.class, resultSet::next);
assertEquals(ErrorCode.DEADLINE_EXCEEDED, exception.getErrorCode());
// Wait until the initial CreateSession request has reached the mock server and is frozen.
mockSpanner.waitForRequestsToContain(CreateSessionRequest.class, 5000);

// Acquire the transaction while the initial CreateSession request is in progress.
// This guarantees that this transaction binds to the initial (failing) attempt.
try (ReadContext readContext = client.singleUse()) {
// The first attempt should lead to a DEADLINE_EXCEEDED error being propagated from the
// CreateSession attempt.
try (ResultSet resultSet = readContext.executeQuery(STATEMENT)) {
mockSpanner.unfreeze();
SpannerException exception = assertThrows(SpannerException.class, resultSet::next);
assertEquals(ErrorCode.DEADLINE_EXCEEDED, exception.getErrorCode());
}
}
Comment on lines +127 to 135

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.

medium

Calling mockSpanner.unfreeze() before readContext.executeQuery(STATEMENT) introduces a potential race condition. If the mock server processes the unfrozen CreateSession request and the client receives the DEADLINE_EXCEEDED failure before executeQuery is executed, the client might clear the failed session attempt and initiate a new CreateSession request for the query. Since the simulated exception is not sticky, this new request would succeed, causing the test to fail.

To guarantee that the query binds to the initial failing attempt, we should call readContext.executeQuery(STATEMENT) while the server is still frozen, and then call mockSpanner.unfreeze() before calling resultSet.next() (which is where the blocking and exception propagation actually happen).

    try (ReadContext readContext = client.singleUse()) {
      // The first attempt should lead to a DEADLINE_EXCEEDED error being propagated from the
      // CreateSession attempt.
      try (ResultSet resultSet = readContext.executeQuery(STATEMENT)) {
        mockSpanner.unfreeze();
        SpannerException exception = assertThrows(SpannerException.class, resultSet::next);
        assertEquals(ErrorCode.DEADLINE_EXCEEDED, exception.getErrorCode());
      }
    }

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.

Done


// The next attempt should then succeed.
mockSpanner.unfreeze();
assertNotNull(client.multiplexedSessionDatabaseClient.getCurrentSessionReference());

try (ResultSet resultSet = client.singleUse().executeQuery(STATEMENT)) {
//noinspection StatementWithEmptyBody
while (resultSet.next()) {}
Expand Down
Loading