Skip to content

Commit c411f6c

Browse files
committed
examples: add example integration test
1 parent 80d023a commit c411f6c

File tree

6 files changed

+457
-31
lines changed

6 files changed

+457
-31
lines changed

google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncResultSetImpl.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -476,20 +476,23 @@ private CreateListCallback(
476476

477477
@Override
478478
public CallbackResponse cursorReady(AsyncResultSet resultSet) {
479-
CursorState state;
480479
try {
481-
while ((state = resultSet.tryNext()) == CursorState.OK) {
482-
builder.add(transformer.apply(resultSet));
480+
while (true) {
481+
switch (resultSet.tryNext()) {
482+
case DONE:
483+
future.set(builder.build());
484+
return CallbackResponse.DONE;
485+
case NOT_READY:
486+
return CallbackResponse.CONTINUE;
487+
case OK:
488+
builder.add(transformer.apply(resultSet));
489+
break;
490+
}
483491
}
484-
} catch (SpannerException e) {
485-
future.setException(e);
486-
return CallbackResponse.DONE;
487-
}
488-
if (state == CursorState.DONE) {
489-
future.set(builder.build());
492+
} catch (Throwable t) {
493+
future.setException(t);
490494
return CallbackResponse.DONE;
491495
}
492-
return CallbackResponse.CONTINUE;
493496
}
494497
}
495498

google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClient.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,37 @@ public interface DatabaseClient {
278278
*/
279279
TransactionManager transactionManager();
280280

281+
/**
282+
* Returns an asynchronous transaction runner for executing a single logical transaction with
283+
* retries. The returned runner can only be used once.
284+
*
285+
* <p>Example of a read write transaction.
286+
*
287+
* <pre> <code>
288+
* Executor executor = Executors.newSingleThreadExecutor();
289+
* final long singerId = my_singer_id;
290+
* AsyncRunner runner = client.runAsync();
291+
* ApiFuture<Long> rowCount =
292+
* runner.runAsync(
293+
* new AsyncWork<Long>() {
294+
* @Override
295+
* public ApiFuture<Long> doWorkAsync(TransactionContext txn) {
296+
* String column = "FirstName";
297+
* Struct row =
298+
* txn.readRow("Singers", Key.of(singerId), Collections.singleton("Name"));
299+
* String name = row.getString("Name");
300+
* return txn.executeUpdateAsync(
301+
* Statement.newBuilder("UPDATE Singers SET Name=@name WHERE SingerId=@id")
302+
* .bind("id")
303+
* .to(singerId)
304+
* .bind("name")
305+
* .to(name.toUpperCase())
306+
* .build());
307+
* }
308+
* },
309+
* executor);
310+
* </code></pre>
311+
*/
281312
AsyncRunner runAsync();
282313

283314
/**

google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ private AutoClosingReadContext(
219219
}
220220

221221
T getReadContextDelegate() {
222-
if (readContextDelegate == null) {
223-
synchronized (lock) {
222+
synchronized (lock) {
223+
if (readContextDelegate == null) {
224224
while (true) {
225225
try {
226226
this.readContextDelegate = readContextDelegateSupplier.apply(this.session);

google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerTestUtil.java

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,22 +108,17 @@ public class MockSpannerTestUtil {
108108
.setMetadata(READ_KEY_VALUE_METADATA)
109109
.build();
110110
static final com.google.spanner.v1.ResultSet READ_MULTIPLE_KEY_VALUE_RESULTSET =
111-
com.google.spanner.v1.ResultSet.newBuilder()
112-
.addRows(
113-
ListValue.newBuilder()
114-
.addValues(com.google.protobuf.Value.newBuilder().setStringValue("k1").build())
115-
.addValues(com.google.protobuf.Value.newBuilder().setStringValue("v1").build())
116-
.build())
117-
.addRows(
118-
ListValue.newBuilder()
119-
.addValues(com.google.protobuf.Value.newBuilder().setStringValue("k2").build())
120-
.addValues(com.google.protobuf.Value.newBuilder().setStringValue("v2").build())
121-
.build())
122-
.addRows(
123-
ListValue.newBuilder()
124-
.addValues(com.google.protobuf.Value.newBuilder().setStringValue("k3").build())
125-
.addValues(com.google.protobuf.Value.newBuilder().setStringValue("v3").build())
126-
.build())
127-
.setMetadata(READ_KEY_VALUE_METADATA)
128-
.build();
111+
generateKeyValueResultSet(1, 3);
112+
113+
static com.google.spanner.v1.ResultSet generateKeyValueResultSet(int beginRow, int endRow) {
114+
com.google.spanner.v1.ResultSet.Builder builder = com.google.spanner.v1.ResultSet.newBuilder();
115+
for (int row = beginRow; row <= endRow; row++) {
116+
builder.addRows(
117+
ListValue.newBuilder()
118+
.addValues(com.google.protobuf.Value.newBuilder().setStringValue("k" + row).build())
119+
.addValues(com.google.protobuf.Value.newBuilder().setStringValue("v" + row).build())
120+
.build());
121+
}
122+
return builder.setMetadata(READ_KEY_VALUE_METADATA).build();
123+
}
129124
}

google-cloud-spanner/src/test/java/com/google/cloud/spanner/ReadAsyncTest.java

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,26 @@
2020
import static com.google.common.truth.Truth.assertThat;
2121
import static org.junit.Assert.fail;
2222

23+
import com.google.api.core.ApiFunction;
2324
import com.google.api.core.ApiFuture;
25+
import com.google.api.core.ApiFutures;
2426
import com.google.api.core.SettableApiFuture;
2527
import com.google.api.gax.grpc.testing.LocalChannelProvider;
2628
import com.google.cloud.NoCredentials;
2729
import com.google.cloud.spanner.AsyncResultSet.CallbackResponse;
2830
import com.google.cloud.spanner.AsyncResultSet.ReadyCallback;
2931
import com.google.cloud.spanner.MockSpannerServiceImpl.SimulatedExecutionTime;
3032
import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult;
33+
import com.google.common.base.Function;
34+
import com.google.common.collect.ImmutableList;
35+
import com.google.common.collect.Iterables;
3136
import com.google.common.util.concurrent.SettableFuture;
3237
import io.grpc.Server;
3338
import io.grpc.Status;
3439
import io.grpc.inprocess.InProcessServerBuilder;
3540
import java.util.ArrayList;
41+
import java.util.Arrays;
42+
import java.util.Comparator;
3643
import java.util.List;
3744
import java.util.concurrent.BlockingQueue;
3845
import java.util.concurrent.CountDownLatch;
@@ -95,7 +102,8 @@ public void before() {
95102
.setProjectId(TEST_PROJECT)
96103
.setChannelProvider(channelProvider)
97104
.setCredentials(NoCredentials.getInstance())
98-
.setSessionPoolOption(SessionPoolOptions.newBuilder().setFailOnSessionLeak().build())
105+
.setSessionPoolOption(
106+
SessionPoolOptions.newBuilder().setFailOnSessionLeak().setMinSessions(0).build())
99107
.build()
100108
.getService();
101109
client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE));
@@ -267,4 +275,62 @@ public CallbackResponse cursorReady(AsyncResultSet resultSet) {
267275
Thread.sleep(10L);
268276
assertThat(clientImpl.pool.getNumberOfSessionsInUse()).isEqualTo(0);
269277
}
278+
279+
@Test
280+
public void readOnlyTransaction() throws Exception {
281+
Statement statement1 =
282+
Statement.of("SELECT * FROM TestTable WHERE Key IN ('k10', 'k11', 'k12')");
283+
Statement statement2 = Statement.of("SELECT * FROM TestTable WHERE Key IN ('k1', 'k2', 'k3");
284+
mockSpanner.putStatementResult(
285+
StatementResult.query(statement1, generateKeyValueResultSet(10, 12)));
286+
mockSpanner.putStatementResult(
287+
StatementResult.query(statement2, generateKeyValueResultSet(1, 3)));
288+
289+
ApiFuture<ImmutableList<String>> values1;
290+
ApiFuture<ImmutableList<String>> values2;
291+
try (ReadOnlyTransaction tx = client.readOnlyTransaction()) {
292+
try (AsyncResultSet rs = tx.executeQueryAsync(statement1)) {
293+
values1 =
294+
rs.toListAsync(
295+
new Function<StructReader, String>() {
296+
@Override
297+
public String apply(StructReader input) {
298+
return input.getString("Value");
299+
}
300+
},
301+
executor);
302+
}
303+
try (AsyncResultSet rs = tx.executeQueryAsync(statement2)) {
304+
values2 =
305+
rs.toListAsync(
306+
new Function<StructReader, String>() {
307+
@Override
308+
public String apply(StructReader input) {
309+
return input.getString("Value");
310+
}
311+
},
312+
executor);
313+
}
314+
}
315+
ApiFuture<Iterable<String>> allValues =
316+
ApiFutures.transform(
317+
ApiFutures.allAsList(Arrays.asList(values1, values2)),
318+
new ApiFunction<List<ImmutableList<String>>, Iterable<String>>() {
319+
@Override
320+
public Iterable<String> apply(List<ImmutableList<String>> input) {
321+
return Iterables.mergeSorted(
322+
input,
323+
new Comparator<String>() {
324+
@Override
325+
public int compare(String o1, String o2) {
326+
// Return in numerical order (i.e. without the preceding 'v').
327+
return Integer.valueOf(o1.substring(1))
328+
.compareTo(Integer.valueOf(o2.substring(1)));
329+
}
330+
});
331+
}
332+
},
333+
executor);
334+
assertThat(allValues.get()).containsExactly("v1", "v2", "v3", "v10", "v11", "v12");
335+
}
270336
}

0 commit comments

Comments
 (0)