Skip to content

Commit 8ec0cf2

Browse files
fix: do not end transaction span when rolling back to savepoint (#3167)
* fix: do not end transaction span when rolling back to savepoint When a transaction is rolled back to a savepoint instead of rolled back entirely, the transaction span should not be ended, as the transaction continues to live. * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent f7891c1 commit 8ec0cf2

File tree

3 files changed

+67
-6
lines changed

3 files changed

+67
-6
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ If you are using Maven without the BOM, add this to your dependencies:
5050
If you are using Gradle 5.x or later, add this to your dependencies:
5151

5252
```Groovy
53-
implementation platform('com.google.cloud:libraries-bom:26.41.0')
53+
implementation platform('com.google.cloud:libraries-bom:26.42.0')
5454
5555
implementation 'com.google.cloud:google-cloud-spanner'
5656
```

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ReadWriteTransaction.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -1142,22 +1142,29 @@ public ApiFuture<Void> rollbackAsync(CallType callType) {
11421142
}
11431143
}
11441144

1145-
private ApiFuture<Void> rollbackAsync(CallType callType, boolean updateStatus) {
1145+
private ApiFuture<Void> rollbackAsync(CallType callType, boolean updateStatusAndEndSpan) {
11461146
ConnectionPreconditions.checkState(
11471147
state == UnitOfWorkState.STARTED || state == UnitOfWorkState.ABORTED,
11481148
"This transaction has status " + state.name());
1149-
if (updateStatus) {
1149+
if (updateStatusAndEndSpan) {
11501150
state = UnitOfWorkState.ROLLED_BACK;
1151-
asyncEndUnitOfWorkSpan();
11521151
}
11531152
if (txContextFuture != null && state != UnitOfWorkState.ABORTED) {
11541153
ApiFuture<Void> result =
11551154
executeStatementAsync(
11561155
callType, ROLLBACK_STATEMENT, rollbackCallable, SpannerGrpc.getRollbackMethod());
1157-
asyncEndUnitOfWorkSpan();
1156+
if (updateStatusAndEndSpan) {
1157+
// Note: We end the transaction span after executing the rollback to include the rollback in
1158+
// the transaction span. Even though both methods are executed asynchronously, they are both
1159+
// executed using the same single-threaded executor, meaning that the span will only be
1160+
// ended after the rollback has finished.
1161+
asyncEndUnitOfWorkSpan();
1162+
}
11581163
return result;
1159-
} else {
1164+
} else if (updateStatusAndEndSpan) {
11601165
return asyncEndUnitOfWorkSpan();
1166+
} else {
1167+
return ApiFutures.immediateFuture(null);
11611168
}
11621169
}
11631170

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/OpenTelemetryTracingTest.java

+54
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static org.junit.Assert.assertFalse;
2222
import static org.junit.Assert.assertTrue;
2323

24+
import com.google.cloud.spanner.MockSpannerServiceImpl;
2425
import com.google.cloud.spanner.ResultSet;
2526
import com.google.cloud.spanner.SpannerOptions;
2627
import com.google.cloud.spanner.SpannerOptions.SpannerEnvironment;
@@ -472,6 +473,59 @@ public void testMultiUseReadWriteAborted() {
472473
assertParent("CloudSpanner.ReadWriteTransaction", "CloudSpannerOperation.Commit", spans);
473474
}
474475

476+
@Test
477+
public void testSavepoint() {
478+
Statement statement1 = Statement.of("insert into foo (id) values (1)");
479+
Statement statement2 = Statement.of("insert into foo (id) values (2)");
480+
mockSpanner.putStatementResult(MockSpannerServiceImpl.StatementResult.update(statement1, 1));
481+
mockSpanner.putStatementResult(MockSpannerServiceImpl.StatementResult.update(statement2, 1));
482+
483+
try (Connection connection = createTestConnection()) {
484+
connection.setAutocommit(false);
485+
connection.setReadOnly(false);
486+
connection.setSavepointSupport(SavepointSupport.ENABLED);
487+
assertEquals(1L, connection.executeUpdate(statement1));
488+
connection.savepoint("test");
489+
assertEquals(1L, connection.executeUpdate(statement2));
490+
connection.rollbackToSavepoint("test");
491+
connection.commit();
492+
}
493+
assertEquals(CompletableResultCode.ofSuccess(), spanExporter.flush());
494+
List<SpanData> spans = spanExporter.getFinishedSpanItems();
495+
assertContains("CloudSpannerJdbc.ReadWriteTransaction", spans);
496+
assertContains("CloudSpanner.ReadWriteTransaction", spans);
497+
// Statement 1 is executed 2 times, because the original transaction needs to be
498+
// retried after the transaction was rolled back to the savepoint.
499+
assertContains(
500+
"CloudSpannerOperation.ExecuteUpdate",
501+
2,
502+
Attributes.of(AttributeKey.stringKey("db.statement"), statement1.getSql()),
503+
spans);
504+
assertContains(
505+
"CloudSpannerOperation.ExecuteUpdate",
506+
1,
507+
Attributes.of(AttributeKey.stringKey("db.statement"), statement2.getSql()),
508+
spans);
509+
assertContains("CloudSpannerOperation.Commit", spans);
510+
511+
// Verify that we have two Cloud Spanner transactions, and that these are both children of one
512+
// JDBC transaction.
513+
List<SpanData> transactionSpans =
514+
getSpans("CloudSpanner.ReadWriteTransaction", Attributes.empty(), spans);
515+
assertEquals(2, transactionSpans.size());
516+
assertEquals(
517+
transactionSpans.get(0).getParentSpanId(), transactionSpans.get(1).getParentSpanId());
518+
List<SpanData> jdbcTransactionSpans =
519+
getSpans("CloudSpannerJdbc.ReadWriteTransaction", Attributes.empty(), spans);
520+
assertEquals(1, jdbcTransactionSpans.size());
521+
assertEquals(
522+
jdbcTransactionSpans.get(0).getSpanId(), transactionSpans.get(0).getParentSpanId());
523+
List<SpanData> commitSpans =
524+
getSpans("CloudSpannerOperation.Commit", Attributes.empty(), spans);
525+
assertEquals(1, commitSpans.size());
526+
assertEquals(transactionSpans.get(1).getSpanId(), commitSpans.get(0).getParentSpanId());
527+
}
528+
475529
@Test
476530
public void testTransactionTag() {
477531
try (Connection connection = createTestConnection()) {

0 commit comments

Comments
 (0)