File tree

2 files changed

+121
-294
lines changed

2 files changed

+121
-294
lines changed
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@
3737
import com.google.cloud.spanner.ErrorCode;
3838
import com.google.cloud.spanner.Instance;
3939
import com.google.cloud.spanner.InstanceAdminClient;
40+
import com.google.cloud.spanner.InstanceId;
4041
import com.google.cloud.spanner.IntegrationTestEnv;
4142
import com.google.cloud.spanner.Mutation;
4243
import com.google.cloud.spanner.Options;
4344
import com.google.cloud.spanner.ParallelIntegrationTest;
45+
import com.google.cloud.spanner.ResultSet;
4446
import com.google.cloud.spanner.SpannerException;
4547
import com.google.cloud.spanner.SpannerExceptionFactory;
48+
import com.google.cloud.spanner.Statement;
4649
import com.google.cloud.spanner.testing.RemoteSpannerHelper;
4750
import com.google.common.base.Predicate;
4851
import com.google.common.base.Stopwatch;
@@ -92,6 +95,8 @@ public class ITBackupTest {
9295
private List<String> databases = new ArrayList<>();
9396
private List<String> backups = new ArrayList<>();
9497
private final Random random = new Random();
98+
private String projectId;
99+
private String instanceId;
95100

96101
@BeforeClass
97102
public static void doNotRunOnEmulator() {
@@ -105,6 +110,8 @@ public void setUp() {
105110
dbAdminClient = testHelper.getClient().getDatabaseAdminClient();
106111
instanceAdminClient = testHelper.getClient().getInstanceAdminClient();
107112
instance = instanceAdminClient.getInstance(testHelper.getInstanceId().getInstance());
113+
projectId = testHelper.getInstanceId().getProject();
114+
instanceId = testHelper.getInstanceId().getInstance();
108115
logger.info("Finished setup");
109116

110117
// Cancel any backup operation that has been started by this integration test if it has been
@@ -226,19 +233,25 @@ public void testBackups() throws InterruptedException, ExecutionException {
226233
String backupId1 = testHelper.getUniqueBackupId() + "_bck1";
227234
String backupId2 = testHelper.getUniqueBackupId() + "_bck2";
228235
Timestamp expireTime = afterDays(7);
236+
Timestamp versionTime = getCurrentTimestamp(client);
229237
logger.info(String.format("Creating backups %s and %s in parallel", backupId1, backupId2));
230-
OperationFuture<Backup, CreateBackupMetadata> op1 =
231-
dbAdminClient.createBackup(
232-
testHelper.getInstanceId().getInstance(),
233-
backupId1,
234-
db1.getId().getDatabase(),
235-
expireTime);
236-
OperationFuture<Backup, CreateBackupMetadata> op2 =
237-
dbAdminClient.createBackup(
238-
testHelper.getInstanceId().getInstance(),
239-
backupId2,
240-
db2.getId().getDatabase(),
241-
expireTime);
238+
// This backup has the version time specified as the server's current timestamp
239+
final Backup backupToCreate1 =
240+
dbAdminClient
241+
.newBackupBuilder(BackupId.of(projectId, instanceId, backupId1))
242+
.setDatabase(db1.getId())
243+
.setExpireTime(expireTime)
244+
.setVersionTime(versionTime)
245+
.build();
246+
// This backup has no version time specified
247+
final Backup backupToCreate2 =
248+
dbAdminClient
249+
.newBackupBuilder(BackupId.of(projectId, instanceId, backupId2))
250+
.setDatabase(db2.getId())
251+
.setExpireTime(expireTime)
252+
.build();
253+
OperationFuture<Backup, CreateBackupMetadata> op1 = dbAdminClient.createBackup(backupToCreate1);
254+
OperationFuture<Backup, CreateBackupMetadata> op2 = dbAdminClient.createBackup(backupToCreate2);
242255
backups.add(backupId1);
243256
backups.add(backupId2);
244257

@@ -274,9 +287,13 @@ public void testBackups() throws InterruptedException, ExecutionException {
274287
"Backup2 still not finished. Test is giving up waiting for it.");
275288
}
276289
logger.info("Long-running operations finished. Getting backups by id.");
277-
backup1 = dbAdminClient.getBackup(instance.getId().getInstance(), backupId1);
278-
backup2 = dbAdminClient.getBackup(instance.getId().getInstance(), backupId2);
290+
backup1 = dbAdminClient.getBackup(this.instance.getId().getInstance(), backupId1);
291+
backup2 = dbAdminClient.getBackup(this.instance.getId().getInstance(), backupId2);
279292
}
293+
294+
// Verifies that backup version time is the specified one
295+
testBackupVersionTime(backup1, versionTime);
296+
280297
// Insert some more data into db2 to get a timestamp from the server.
281298
Timestamp commitTs =
282299
client.writeAtLeastOnce(
@@ -291,54 +308,40 @@ public void testBackups() throws InterruptedException, ExecutionException {
291308
// Test listing operations.
292309
// List all backups.
293310
logger.info("Listing all backups");
294-
assertThat(instance.listBackups().iterateAll()).containsAtLeast(backup1, backup2);
311+
assertThat(this.instance.listBackups().iterateAll()).containsAtLeast(backup1, backup2);
295312
// List all backups whose names contain 'bck1'.
296313
logger.info("Listing backups with name bck1");
297314
assertThat(
298315
dbAdminClient
299316
.listBackups(
300-
testHelper.getInstanceId().getInstance(),
301-
Options.filter(String.format("name:%s", backup1.getId().getName())))
317+
instanceId, Options.filter(String.format("name:%s", backup1.getId().getName())))
302318
.iterateAll())
303319
.containsExactly(backup1);
304320
logger.info("Listing ready backups");
305321
Iterable<Backup> readyBackups =
306-
dbAdminClient
307-
.listBackups(testHelper.getInstanceId().getInstance(), Options.filter("state:READY"))
308-
.iterateAll();
322+
dbAdminClient.listBackups(instanceId, Options.filter("state:READY")).iterateAll();
309323
assertThat(readyBackups).containsAtLeast(backup1, backup2);
310324
// List all backups for databases whose names contain 'db1'.
311325
logger.info("Listing backups for database db1");
312326
assertThat(
313327
dbAdminClient
314328
.listBackups(
315-
testHelper.getInstanceId().getInstance(),
316-
Options.filter(String.format("database:%s", db1.getId().getName())))
329+
instanceId, Options.filter(String.format("database:%s", db1.getId().getName())))
317330
.iterateAll())
318331
.containsExactly(backup1);
319332
// List all backups that were created before a certain time.
320333
Timestamp ts = Timestamp.ofTimeSecondsAndNanos(commitTs.getSeconds(), 0);
321334
logger.info(String.format("Listing backups created before %s", ts));
322335
assertThat(
323336
dbAdminClient
324-
.listBackups(
325-
testHelper.getInstanceId().getInstance(),
326-
Options.filter(String.format("create_time<\"%s\"", ts)))
337+
.listBackups(instanceId, Options.filter(String.format("create_time<\"%s\"", ts)))
327338
.iterateAll())
328339
.containsAtLeast(backup1, backup2);
329340
// List all backups with a size > 0.
330341
logger.info("Listing backups with size>0");
331-
assertThat(
332-
dbAdminClient
333-
.listBackups(
334-
testHelper.getInstanceId().getInstance(), Options.filter("size_bytes>0"))
335-
.iterateAll())
342+
assertThat(dbAdminClient.listBackups(instanceId, Options.filter("size_bytes>0")).iterateAll())
336343
.contains(backup2);
337-
assertThat(
338-
dbAdminClient
339-
.listBackups(
340-
testHelper.getInstanceId().getInstance(), Options.filter("size_bytes>0"))
341-
.iterateAll())
344+
assertThat(dbAdminClient.listBackups(instanceId, Options.filter("size_bytes>0")).iterateAll())
342345
.doesNotContain(backup1);
343346

344347
// Test pagination.
@@ -349,14 +352,79 @@ public void testBackups() throws InterruptedException, ExecutionException {
349352
testGetBackup(db2, backupId2, expireTime);
350353
testUpdateBackup(backup1);
351354
testCreateInvalidExpirationDate(db1);
352-
testRestore(backup1, op1);
355+
testRestore(backup1, op1, versionTime);
353356

354357
testDelete(backupId2);
355358
testCancelBackupOperation(db1);
356359
// Finished all tests.
357360
logger.info("Finished all backup tests");
358361
}
359362

363+
@Test(expected = SpannerException.class)
364+
public void backupCreationWithVersionTimeTooFarInThePastFails() throws Exception {
365+
final Database testDatabase = testHelper.createTestDatabase();
366+
final DatabaseId databaseId = testDatabase.getId();
367+
final InstanceId instanceId = databaseId.getInstanceId();
368+
final String backupId = testHelper.getUniqueBackupId();
369+
final Timestamp expireTime = afterDays(7);
370+
final Timestamp versionTime = daysAgo(30);
371+
final Backup backupToCreate =
372+
dbAdminClient
373+
.newBackupBuilder(BackupId.of(instanceId, backupId))
374+
.setDatabase(databaseId)
375+
.setExpireTime(expireTime)
376+
.setVersionTime(versionTime)
377+
.build();
378+
379+
getOrThrow(dbAdminClient.createBackup(backupToCreate));
380+
}
381+
382+
@Test(expected = SpannerException.class)
383+
public void backupCreationWithVersionTimeInTheFutureFails() throws Exception {
384+
final Database testDatabase = testHelper.createTestDatabase();
385+
final DatabaseId databaseId = testDatabase.getId();
386+
final InstanceId instanceId = databaseId.getInstanceId();
387+
final String backupId = testHelper.getUniqueBackupId();
388+
final Timestamp expireTime = afterDays(7);
389+
final Timestamp versionTime = afterDays(1);
390+
final Backup backupToCreate =
391+
dbAdminClient
392+
.newBackupBuilder(BackupId.of(instanceId, backupId))
393+
.setDatabase(databaseId)
394+
.setExpireTime(expireTime)
395+
.setVersionTime(versionTime)
396+
.build();
397+
398+
getOrThrow(dbAdminClient.createBackup(backupToCreate));
399+
}
400+
401+
private <T> T getOrThrow(OperationFuture<T, ?> operation)
402+
throws InterruptedException, ExecutionException {
403+
try {
404+
return operation.get();
405+
} catch (ExecutionException e) {
406+
if (e.getCause() instanceof SpannerException) {
407+
throw (SpannerException) e.getCause();
408+
} else {
409+
throw e;
410+
}
411+
}
412+
}
413+
414+
private Timestamp getCurrentTimestamp(DatabaseClient client) {
415+
try (ResultSet resultSet =
416+
client.singleUse().executeQuery(Statement.of("SELECT CURRENT_TIMESTAMP()"))) {
417+
resultSet.next();
418+
return resultSet.getTimestamp(0);
419+
}
420+
}
421+
422+
private void testBackupVersionTime(Backup backup, Timestamp versionTime) {
423+
logger.info("Verifying backup version time for " + backup.getId());
424+
assertThat(backup.getVersionTime()).isEqualTo(versionTime);
425+
logger.info("Done verifying backup version time for " + backup.getId());
426+
}
427+
360428
private void testMetadata(
361429
OperationFuture<Backup, CreateBackupMetadata> op1,
362430
OperationFuture<Backup, CreateBackupMetadata> op2,
@@ -391,11 +459,7 @@ private void testCreateInvalidExpirationDate(Database db) throws InterruptedExce
391459
String backupId = testHelper.getUniqueBackupId();
392460
logger.info(String.format("Creating backup %s with invalid expiration date", backupId));
393461
OperationFuture<Backup, CreateBackupMetadata> op =
394-
dbAdminClient.createBackup(
395-
testHelper.getInstanceId().getInstance(),
396-
backupId,
397-
db.getId().getDatabase(),
398-
expireTime);
462+
dbAdminClient.createBackup(instanceId, backupId, db.getId().getDatabase(), expireTime);
399463
backups.add(backupId);
400464
try {
401465
op.get();
@@ -414,11 +478,7 @@ private void testCancelBackupOperation(Database db)
414478
String backupId = testHelper.getUniqueBackupId();
415479
logger.info(String.format("Starting to create backup %s", backupId));
416480
OperationFuture<Backup, CreateBackupMetadata> op =
417-
dbAdminClient.createBackup(
418-
testHelper.getInstanceId().getInstance(),
419-
backupId,
420-
db.getId().getDatabase(),
421-
expireTime);
481+
dbAdminClient.createBackup(instanceId, backupId, db.getId().getDatabase(), expireTime);
422482
backups.add(backupId);
423483
// Cancel the backup operation.
424484
logger.info(String.format("Cancelling the creation of backup %s", backupId));
@@ -428,8 +488,7 @@ private void testCancelBackupOperation(Database db)
428488
for (Operation operation :
429489
dbAdminClient
430490
.listBackupOperations(
431-
testHelper.getInstanceId().getInstance(),
432-
Options.filter(String.format("name:%s", op.getName())))
491+
instanceId, Options.filter(String.format("name:%s", op.getName())))
433492
.iterateAll()) {
434493
assertThat(operation.getError().getCode()).isEqualTo(Status.Code.CANCELLED.value());
435494
operationFound = true;
@@ -481,18 +540,15 @@ private void testPagination(int expectedMinimumTotalBackups) {
481540
logger.info("Listing backups using pagination");
482541
int numBackups = 0;
483542
logger.info("Fetching first page");
484-
Page<Backup> page =
485-
dbAdminClient.listBackups(testHelper.getInstanceId().getInstance(), Options.pageSize(1));
543+
Page<Backup> page = dbAdminClient.listBackups(instanceId, Options.pageSize(1));
486544
assertThat(page.getValues()).hasSize(1);
487545
numBackups++;
488546
assertThat(page.hasNextPage()).isTrue();
489547
while (page.hasNextPage()) {
490548
logger.info(String.format("Fetching page %d", numBackups + 1));
491549
page =
492550
dbAdminClient.listBackups(
493-
testHelper.getInstanceId().getInstance(),
494-
Options.pageToken(page.getNextPageToken()),
495-
Options.pageSize(1));
551+
instanceId, Options.pageToken(page.getNextPageToken()), Options.pageSize(1));
496552
assertThat(page.getValues()).hasSize(1);
497553
numBackups++;
498554
}
@@ -521,7 +577,8 @@ private void testDelete(String backupId) throws InterruptedException {
521577
logger.info("Finished delete tests");
522578
}
523579

524-
private void testRestore(Backup backup, OperationFuture<Backup, CreateBackupMetadata> backupOp)
580+
private void testRestore(
581+
Backup backup, OperationFuture<Backup, CreateBackupMetadata> backupOp, Timestamp versionTime)
525582
throws InterruptedException, ExecutionException {
526583
// Restore the backup to a new database.
527584
String restoredDb = testHelper.getUniqueDatabaseId();
@@ -565,6 +622,8 @@ private void testRestore(Backup backup, OperationFuture<Backup, CreateBackupMeta
565622
assertThat(metadata.getSourceType()).isEqualTo(RestoreSourceType.BACKUP);
566623
assertThat(metadata.getName())
567624
.isEqualTo(DatabaseId.of(testHelper.getInstanceId(), restoredDb).getName());
625+
assertThat(Timestamp.fromProto(metadata.getBackupInfo().getVersionTime()))
626+
.isEqualTo(versionTime);
568627

569628
// Ensure the operations show up in the right collections.
570629
// TODO: Re-enable when it is clear why this fails on the CI environment.
@@ -573,6 +632,14 @@ private void testRestore(Backup backup, OperationFuture<Backup, CreateBackupMeta
573632
// Wait until the restore operation has finished successfully.
574633
Database database = restoreOp.get();
575634
assertThat(database.getId().getDatabase()).isEqualTo(restoredDb);
635+
636+
// Reloads the database
637+
final Database reloadedDatabase = database.reload();
638+
assertThat(
639+
Timestamp.fromProto(
640+
reloadedDatabase.getProto().getRestoreInfo().getBackupInfo().getVersionTime()))
641+
.isEqualTo(versionTime);
642+
576643
// Restoring the backup to an existing database should fail.
577644
try {
578645
logger.info(

0 commit comments

Comments
 (0)