@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); } }
private void setUpPrivateDatabase() { ImmutableList.Builder<History> historyBuilder = ImmutableList.builder(); client = newTestDatabase(historyBuilder); history = historyBuilder.build(); }
private void insertAndReadAgain( ReadOnlyTransaction readContext, Timestamp expectedTimestamp, @Nullable String expectedValue) { writeNewValue(client, history.size(), null); Struct row = readRow(readContext); if (expectedValue == null) { assertThat(row).isNull(); } else { assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(expectedValue); } assertThat(readContext.getReadTimestamp()).isEqualTo(expectedTimestamp); }
@Test public void singleStrong() { History expected = history.get(history.size() - 1); ReadOnlyTransaction readContext = client.singleUseReadOnlyTransaction(); Struct row = readRow(readContext); assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(expected.value); assertThat(readContext.getReadTimestamp()).isAtLeast(expected.timestamp); row = readRow(client.singleUse()); assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(expected.value); }
private static DatabaseClient newTestDatabase(ImmutableList.Builder<History> historyBuilder) { Database newDb = env.getTestHelper() .createTestDatabase( "CREATE TABLE TestTable ( StringValue STRING(MAX) ) PRIMARY KEY ()"); DatabaseClient newClient = env.getTestHelper().getDatabaseClient(newDb); for (int i = 0; i < 5; ++i) { writeNewValue(newClient, i, historyBuilder); } return newClient; }
@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); }
@Test public void singleReadTimestamp() { History expected = history.get(2); TimestampBound bound = TimestampBound.ofReadTimestamp(expected.timestamp); ReadOnlyTransaction readContext = client.singleUseReadOnlyTransaction(bound); Struct row = readRow(readContext); assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(expected.value); assertThat(readContext.getReadTimestamp()).isEqualTo(expected.timestamp); row = readRow(client.singleUse(bound)); assertThat(row).isNotNull(); assertThat(row.getString(0)).isEqualTo(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 singleMaxStaleness() { History minimum = history.get(2); NavigableMap<Timestamp, String> possibleValues = new TreeMap<>(); for (History item : history.subList(2, history.size())) { possibleValues.put(item.timestamp, item.value); } // Pick a staleness that cannot precede the second write (which, in practice, is the staleness // that exceeds the minimum commit time for the subsequent write). long stalenessNanos = System.nanoTime() - history.get(3).minCommitNanoTime; TimestampBound bound = TimestampBound.ofMaxStaleness(stalenessNanos, TimeUnit.NANOSECONDS); ReadOnlyTransaction readContext = client.singleUseReadOnlyTransaction(bound); Struct row = readRow(readContext); assertThat(row).isNotNull(); assertThat(readContext.getReadTimestamp()).isAtLeast(minimum.timestamp); assertThat(row.getString(0)) .isEqualTo(possibleValues.floorEntry(readContext.getReadTimestamp()).getValue()); row = readRow(client.singleUse(bound)); assertThat(row).isNotNull(); assertThat(row.getString(0)).isIn(possibleValues.values()); }
@BeforeClass public static void setUpSharedDatabase() { ImmutableList.Builder<History> historyBuilder = ImmutableList.builder(); sharedClient = newTestDatabase(historyBuilder); sharedHistory = historyBuilder.build(); }
@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 singleExactStaleness() { // TODO(user): Use a shorter deadline (when supported) and pass on the call to Cloud Spanner. long deadlineNanoTime = System.nanoTime() + TimeUnit.MINUTES.toNanos(1); // The only exact staleness values that can be tested reliably are before the first item or // later than the last item: we choose the former. // // Pick a staleness that is "guaranteed" not to observe the first write. Note that this // guarantee doesn't strictly hold in the absence of enforced read deadlines, but we use a // deadline large enough to make it practically true. long stalenessNanos = 1 + deadlineNanoTime - history.get(0).minCommitNanoTime; TimestampBound bound = TimestampBound.ofExactStaleness(stalenessNanos, TimeUnit.NANOSECONDS); ReadOnlyTransaction readContext = client.singleUseReadOnlyTransaction(bound); Struct row = readRow(readContext); assertThat(row).isNull(); assertThat(readContext.getReadTimestamp().toSqlTimestamp()) .isLessThan(history.get(0).timestamp.toSqlTimestamp()); row = readRow(client.singleUse(bound)); assertThat(row).isNull(); }
@Test public void singleMinReadTimestamp() { int minimumIndex = 2; History minimum = history.get(minimumIndex); NavigableMap<Timestamp, String> possibleValues = new TreeMap<>(); for (History item : history.subList(minimumIndex, history.size())) { possibleValues.put(item.timestamp, item.value); } TimestampBound bound = TimestampBound.ofMinReadTimestamp(minimum.timestamp); ReadOnlyTransaction readContext = client.singleUseReadOnlyTransaction(bound); Struct row = readRow(readContext); assertThat(row).isNotNull(); assertThat(readContext.getReadTimestamp()).isAtLeast(minimum.timestamp); assertThat(row.getString(0)) .isEqualTo(possibleValues.floorEntry(readContext.getReadTimestamp()).getValue()); row = readRow(client.singleUse(bound)); assertThat(row).isNotNull(); assertThat(row.getString(0)).isIn(possibleValues.values()); }