@Override public Void run(TransactionContext transaction) throws SpannerException { client.readOnlyTransaction().getReadTimestamp(); return null; } });
/** Example of read only transaction. */ // [TARGET readOnlyTransaction()] // [VARIABLE my_singer_id] // [VARIABLE my_album_id] public String readOnlyTransaction(long singerId, long albumId) { // [START readOnlyTransaction] String singerColumn = "FirstName"; String albumColumn = "AlbumTitle"; String albumTitle = null; // ReadOnlyTransaction should be closed to prevent resource leak. try (ReadOnlyTransaction txn = dbClient.readOnlyTransaction()) { Struct singerRow = txn.readRow("Singers", Key.of(singerId), Collections.singleton(singerColumn)); Struct albumRow = txn.readRow("Albums", Key.of(singerId, albumId), Collections.singleton(albumColumn)); singerRow.getString(singerColumn); albumTitle = albumRow.getString(albumColumn); } // [END readOnlyTransaction] return albumTitle; }
@Test public void multiMinReadTimestamp() { // Cannot use bounded modes with multi-read transactions. expectedException.expect(IllegalArgumentException.class); client.readOnlyTransaction(TimestampBound.ofMinReadTimestamp(history.get(2).timestamp)); }
/** Example of read only transaction with timestamp bound. */ // [TARGET readOnlyTransaction(TimestampBound)] // [VARIABLE my_singer_id] // [VARIABLE my_album_id] public String readOnlyTransactionTimestamp(long singerId, long albumId) { // [START readOnlyTransactionTimestamp] String singerColumn = "FirstName"; String albumColumn = "AlbumTitle"; String albumTitle = null; // ReadOnlyTransaction should be closed to prevent resource leak. try (ReadOnlyTransaction txn = dbClient.readOnlyTransaction(TimestampBound.ofExactStaleness(10, TimeUnit.SECONDS))) { Struct singerRow = txn.readRow("Singers", Key.of(singerId), Collections.singleton(singerColumn)); Struct albumRow = txn.readRow("Albums", Key.of(singerId, albumId), Collections.singleton(albumColumn)); singerRow.getString(singerColumn); albumTitle = albumRow.getString(albumColumn); } // [END readOnlyTransactionTimestamp] return albumTitle; }
@Test public void multiMaxStaleness() { // Cannot use bounded modes with multi-read transactions. expectedException.expect(IllegalArgumentException.class); client.readOnlyTransaction(TimestampBound.ofMaxStaleness(1, TimeUnit.SECONDS)); } }
@Test public void multiExactStaleness() { setUpPrivateDatabase(); // See singleExactStaleness() for why we pick this timestamp. We expect to see no value. long deadlineNanoTime = System.nanoTime() + TimeUnit.MINUTES.toNanos(1); long stalenessNanos = 1 + deadlineNanoTime - history.get(0).minCommitNanoTime; try (ReadOnlyTransaction readContext = client.readOnlyTransaction( TimestampBound.ofExactStaleness(stalenessNanos, TimeUnit.NANOSECONDS))) { Struct row = readRow(readContext); assertThat(row).isNull(); assertThat(readContext.getReadTimestamp().toSqlTimestamp()) .isLessThan(history.get(0).timestamp.toSqlTimestamp()); insertAndReadAgain(readContext, readContext.getReadTimestamp(), null); } }
@Test public void multiStrong() { setUpPrivateDatabase(); History expected = history.get(history.size() - 1); try (ReadOnlyTransaction readContext = client.readOnlyTransaction()) { Struct row = readRow(readContext); assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(expected.value); assertThat(readContext.getReadTimestamp()).isAtLeast(expected.timestamp); insertAndReadAgain(readContext, readContext.getReadTimestamp(), expected.value); } }
@Test public void multiReadTimestamp() { setUpPrivateDatabase(); History expected = history.get(2); try (ReadOnlyTransaction readContext = client.readOnlyTransaction(TimestampBound.ofReadTimestamp(expected.timestamp))) { Struct row = readRow(readContext); assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(expected.value); assertThat(readContext.getReadTimestamp()).isEqualTo(expected.timestamp); insertAndReadAgain(readContext, readContext.getReadTimestamp(), expected.value); } }
@Test public void query() { // We don't exhaustively test query with all modes - the read tests give us enough confidence // that transaction options are generated appropriately. Just do one test for each type of // context to ensure that transaction options are set at all. History expected = history.get(2); TimestampBound bound = TimestampBound.ofReadTimestamp(expected.timestamp); ReadOnlyTransaction readContext = client.singleUseReadOnlyTransaction(bound); Struct row = queryRow(readContext); assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(expected.value); assertThat(readContext.getReadTimestamp()).isEqualTo(expected.timestamp); readContext = client.readOnlyTransaction(bound); row = queryRow(readContext); assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(expected.value); assertThat(readContext.getReadTimestamp()).isEqualTo(expected.timestamp); readContext.close(); row = queryRow(client.singleUse(bound)); assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(expected.value); }
static void readOnlyTransaction(DatabaseClient dbClient) { // ReadOnlyTransaction must be closed by calling close() on it to release resources held by it. // We use a try-with-resource block to automatically do so. try (ReadOnlyTransaction transaction = dbClient.readOnlyTransaction()) { ResultSet queryResultSet = transaction.executeQuery( Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums")); while (queryResultSet.next()) { System.out.printf( "%d %d %s\n", queryResultSet.getLong(0), queryResultSet.getLong(1), queryResultSet.getString(2)); } ResultSet readResultSet = transaction.read( "Albums", KeySet.all(), Arrays.asList("SingerId", "AlbumId", "AlbumTitle")); while (readResultSet.next()) { System.out.printf( "%d %d %s\n", readResultSet.getLong(0), readResultSet.getLong(1), readResultSet.getString(2)); } } } // [END spanner_read_only_transaction]
@Override public <T> T performReadOnlyTransaction(Function<SpannerTemplate, T> operations, SpannerReadOptions readOptions) { return doWithOrWithoutTransactionContext((x) -> { throw new IllegalStateException("There is already declarative transaction open. " + "Spanner does not support nested transactions"); }, () -> { SpannerReadOptions options = (readOptions != null) ? readOptions : new SpannerReadOptions(); try (ReadOnlyTransaction readOnlyTransaction = (options.getTimestamp() != null) ? this.databaseClient.readOnlyTransaction( TimestampBound.ofReadTimestamp(options.getTimestamp())) : this.databaseClient.readOnlyTransaction()) { return operations.apply(new ReadOnlyTransactionSpannerTemplate( SpannerTemplate.this.databaseClient, SpannerTemplate.this.mappingContext, SpannerTemplate.this.spannerEntityProcessor, SpannerTemplate.this.mutationFactory, SpannerTemplate.this.spannerSchemaUtils, readOnlyTransaction)); } }); }
@Override public <T> T performReadOnlyTransaction(Function<SpannerTemplate, T> operations, SpannerReadOptions readOptions) { return doWithOrWithoutTransactionContext((x) -> { throw new IllegalStateException("There is already declarative transaction open. " + "Spanner does not support nested transactions"); }, () -> { SpannerReadOptions options = (readOptions != null) ? readOptions : new SpannerReadOptions(); try (ReadOnlyTransaction readOnlyTransaction = (options.getTimestamp() != null) ? this.databaseClient.readOnlyTransaction( TimestampBound.ofReadTimestamp(options.getTimestamp())) : this.databaseClient.readOnlyTransaction()) { return operations.apply(new ReadOnlyTransactionSpannerTemplate( SpannerTemplate.this.databaseClient, SpannerTemplate.this.mappingContext, SpannerTemplate.this.spannerEntityProcessor, SpannerTemplate.this.mutationFactory, SpannerTemplate.this.spannerSchemaUtils, readOnlyTransaction)); } }); }
if (transactionDefinition.isReadOnly()) { final ReadContext targetTransactionContext = this.databaseClient .readOnlyTransaction();
if (transactionDefinition.isReadOnly()) { final ReadContext targetTransactionContext = this.databaseClient .readOnlyTransaction();
@ProcessElement @SuppressWarnings("unused") public void processElement(ProcessContext processContext) { // Save schema to GCS so it can be saved along with the exported file. LOG.info("Creating database client for schema read"); LinkedHashMap<String, String> columns; try { DatabaseClient databaseClient = getDatabaseClient(spannerConfig()); try (ReadOnlyTransaction context = databaseClient.readOnlyTransaction()) { LOG.info("Reading schema information"); columns = getAllColumns(context, table().get()); String columnJson = SpannerConverters.GSON.toJson(columns); LOG.info("Saving schema information"); saveSchema(columnJson, textWritePrefix().get() + SCHEMA_SUFFIX); } } finally { closeSpannerAccessor(); } processContext.output( ReadOperation.create() .withColumns(new ArrayList<>(columns.keySet())) .withTable(table().get())); }
@Before @SuppressWarnings("unchecked") public void setUp() throws Exception { MockitoAnnotations.initMocks(this); serviceFactory = new FakeServiceFactory(); ReadOnlyTransaction tx = mock(ReadOnlyTransaction.class); when(serviceFactory.mockDatabaseClient().readOnlyTransaction()).thenReturn(tx); // Capture batches sent to writeAtLeastOnce. when(serviceFactory.mockDatabaseClient().writeAtLeastOnce(mutationBatchesCaptor.capture())) .thenReturn(null); // Simplest schema: a table with int64 key preparePkMetadata(tx, Arrays.asList(pkMetadata("tEsT", "key", "ASC"))); prepareColumnMetadata(tx, Arrays.asList(columnMetadata("tEsT", "key", "INT64", CELLS_PER_KEY))); }
when(serviceFactory.mockDatabaseClient().readOnlyTransaction()).thenReturn(tx);
@ProcessElement public void processElement(ProcessContext c) throws Exception { SpannerSchema.Builder builder = SpannerSchema.builder(); DatabaseClient databaseClient = spannerAccessor.getDatabaseClient(); try (ReadOnlyTransaction tx = databaseClient.readOnlyTransaction()) { ResultSet resultSet = readTableInfo(tx); while (resultSet.next()) { String tableName = resultSet.getString(0); String columnName = resultSet.getString(1); String type = resultSet.getString(2); long cellsMutated = resultSet.getLong(3); builder.addColumn(tableName, columnName, type, cellsMutated); } resultSet = readPrimaryKeyInfo(tx); while (resultSet.next()) { String tableName = resultSet.getString(0); String columnName = resultSet.getString(1); String ordering = resultSet.getString(2); builder.addKeyPart(tableName, columnName, "DESC".equalsIgnoreCase(ordering)); } } c.output(builder.build()); }