Skip to content

Commit 295749c

Browse files
authored
test: move backup tests into slow tests category (#1509)
* test: move backup tests into slow tests Moves ITDatabaseAdminTests into the ITBackupTest which is only executed on the nightly build * test: increase pitr test timeout Increases PITR update database ddl test timeout to 20 minutes.
1 parent 667b8b1 commit 295749c

File tree

3 files changed

+224
-251
lines changed

3 files changed

+224
-251
lines changed

google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITDatabaseAdminTest.java

Lines changed: 8 additions & 247 deletions
Original file line numberDiff line numberDiff line change
@@ -16,50 +16,27 @@
1616

1717
package com.google.cloud.spanner.it;
1818

19-
import static com.google.cloud.spanner.testing.EmulatorSpannerHelper.isUsingEmulator;
2019
import static com.google.common.truth.Truth.assertThat;
2120
import static org.junit.Assert.fail;
22-
import static org.junit.Assume.assumeFalse;
2321

24-
import com.google.api.gax.grpc.GrpcInterceptorProvider;
2522
import com.google.api.gax.longrunning.OperationFuture;
2623
import com.google.api.gax.paging.Page;
27-
import com.google.cloud.Timestamp;
28-
import com.google.cloud.spanner.Backup;
2924
import com.google.cloud.spanner.Database;
3025
import com.google.cloud.spanner.DatabaseAdminClient;
3126
import com.google.cloud.spanner.ErrorCode;
3227
import com.google.cloud.spanner.IntegrationTestEnv;
3328
import com.google.cloud.spanner.Options;
3429
import com.google.cloud.spanner.ParallelIntegrationTest;
35-
import com.google.cloud.spanner.Spanner;
3630
import com.google.cloud.spanner.SpannerException;
37-
import com.google.cloud.spanner.SpannerOptions;
3831
import com.google.cloud.spanner.testing.RemoteSpannerHelper;
3932
import com.google.common.collect.ImmutableList;
4033
import com.google.common.collect.Iterables;
4134
import com.google.common.collect.Iterators;
42-
import com.google.spanner.admin.database.v1.CreateBackupMetadata;
4335
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
44-
import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata;
4536
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
46-
import io.grpc.CallOptions;
47-
import io.grpc.Channel;
48-
import io.grpc.ClientCall;
49-
import io.grpc.ClientInterceptor;
50-
import io.grpc.ForwardingClientCall.SimpleForwardingClientCall;
51-
import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener;
52-
import io.grpc.Metadata;
53-
import io.grpc.MethodDescriptor;
54-
import io.grpc.Status;
5537
import java.util.ArrayList;
56-
import java.util.Collections;
5738
import java.util.List;
58-
import java.util.Random;
59-
import java.util.concurrent.ExecutionException;
6039
import java.util.concurrent.TimeUnit;
61-
import java.util.concurrent.atomic.AtomicBoolean;
62-
import java.util.concurrent.atomic.AtomicInteger;
6340
import org.junit.After;
6441
import org.junit.Before;
6542
import org.junit.ClassRule;
@@ -72,9 +49,7 @@
7249
@Category(ParallelIntegrationTest.class)
7350
@RunWith(JUnit4.class)
7451
public class ITDatabaseAdminTest {
75-
private static final long DATABASE_TIMEOUT_MINUTES = 5;
76-
private static final long BACKUP_TIMEOUT_MINUTES = 20;
77-
private static final long RESTORE_TIMEOUT_MINUTES = 10;
52+
private static final long TIMEOUT_MINUTES = 5;
7853
@ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv();
7954
private DatabaseAdminClient dbAdminClient;
8055
private RemoteSpannerHelper testHelper;
@@ -101,7 +76,7 @@ public void databaseOperations() throws Exception {
10176
String statement1 = "CREATE TABLE T (\n" + " K STRING(MAX),\n" + ") PRIMARY KEY(K)";
10277
OperationFuture<Database, CreateDatabaseMetadata> op =
10378
dbAdminClient.createDatabase(instanceId, dbId, ImmutableList.of(statement1));
104-
Database db = op.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
79+
Database db = op.get(TIMEOUT_MINUTES, TimeUnit.MINUTES);
10580
dbs.add(db);
10681
assertThat(db.getId().getDatabase()).isEqualTo(dbId);
10782

@@ -122,7 +97,7 @@ public void databaseOperations() throws Exception {
12297
String statement2 = "CREATE TABLE T2 (\n" + " K2 STRING(MAX),\n" + ") PRIMARY KEY(K2)";
12398
OperationFuture<?, ?> op2 =
12499
dbAdminClient.updateDatabaseDdl(instanceId, dbId, ImmutableList.of(statement2), null);
125-
op2.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
100+
op2.get(TIMEOUT_MINUTES, TimeUnit.MINUTES);
126101
List<String> statementsInDb = dbAdminClient.getDatabaseDdl(instanceId, dbId);
127102
assertThat(statementsInDb).containsExactly(statement1, statement2);
128103

@@ -143,15 +118,15 @@ public void updateDdlRetry() throws Exception {
143118
String statement1 = "CREATE TABLE T (\n" + " K STRING(MAX),\n" + ") PRIMARY KEY(K)";
144119
OperationFuture<Database, CreateDatabaseMetadata> op =
145120
dbAdminClient.createDatabase(instanceId, dbId, ImmutableList.of(statement1));
146-
Database db = op.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
121+
Database db = op.get(TIMEOUT_MINUTES, TimeUnit.MINUTES);
147122
dbs.add(db);
148123
String statement2 = "CREATE TABLE T2 (\n" + " K2 STRING(MAX),\n" + ") PRIMARY KEY(K2)";
149124
OperationFuture<Void, UpdateDatabaseDdlMetadata> op1 =
150125
dbAdminClient.updateDatabaseDdl(instanceId, dbId, ImmutableList.of(statement2), "myop");
151126
OperationFuture<Void, UpdateDatabaseDdlMetadata> op2 =
152127
dbAdminClient.updateDatabaseDdl(instanceId, dbId, ImmutableList.of(statement2), "myop");
153-
op1.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
154-
op2.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
128+
op1.get(TIMEOUT_MINUTES, TimeUnit.MINUTES);
129+
op2.get(TIMEOUT_MINUTES, TimeUnit.MINUTES);
155130

156131
// Remove the progress list from the metadata before comparing, as there could be small
157132
// differences between the two in the reported progress depending on exactly when each
@@ -170,7 +145,7 @@ public void databaseOperationsViaEntity() throws Exception {
170145
String statement1 = "CREATE TABLE T (\n" + " K STRING(MAX),\n" + ") PRIMARY KEY(K)";
171146
OperationFuture<Database, CreateDatabaseMetadata> op =
172147
dbAdminClient.createDatabase(instanceId, dbId, ImmutableList.of(statement1));
173-
Database db = op.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
148+
Database db = op.get(TIMEOUT_MINUTES, TimeUnit.MINUTES);
174149
dbs.add(db);
175150
assertThat(db.getId().getDatabase()).isEqualTo(dbId);
176151

@@ -179,7 +154,7 @@ public void databaseOperationsViaEntity() throws Exception {
179154

180155
String statement2 = "CREATE TABLE T2 (\n" + " K2 STRING(MAX),\n" + ") PRIMARY KEY(K2)";
181156
OperationFuture<?, ?> op2 = db.updateDdl(ImmutableList.of(statement2), null);
182-
op2.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
157+
op2.get(TIMEOUT_MINUTES, TimeUnit.MINUTES);
183158
Iterable<String> statementsInDb = db.getDdl();
184159
assertThat(statementsInDb).containsExactly(statement1, statement2);
185160
db.drop();
@@ -219,218 +194,4 @@ public void listPagination() throws Exception {
219194
}
220195
assertThat(dbIdsGot).containsAtLeastElementsIn(dbIds);
221196
}
222-
223-
private static final class InjectErrorInterceptorProvider implements GrpcInterceptorProvider {
224-
final AtomicBoolean injectError = new AtomicBoolean(true);
225-
final AtomicInteger getOperationCount = new AtomicInteger();
226-
final AtomicInteger methodCount = new AtomicInteger();
227-
final String methodName;
228-
229-
private InjectErrorInterceptorProvider(String methodName) {
230-
this.methodName = methodName;
231-
}
232-
233-
@Override
234-
public List<ClientInterceptor> getInterceptors() {
235-
ClientInterceptor interceptor =
236-
new ClientInterceptor() {
237-
@Override
238-
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
239-
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
240-
if (method.getFullMethodName().contains("GetOperation")) {
241-
getOperationCount.incrementAndGet();
242-
}
243-
if (!method.getFullMethodName().contains(methodName)) {
244-
return next.newCall(method, callOptions);
245-
}
246-
247-
methodCount.incrementAndGet();
248-
final AtomicBoolean errorInjected = new AtomicBoolean();
249-
final ClientCall<ReqT, RespT> clientCall = next.newCall(method, callOptions);
250-
251-
return new SimpleForwardingClientCall<ReqT, RespT>(clientCall) {
252-
@Override
253-
public void start(Listener<RespT> responseListener, Metadata headers) {
254-
super.start(
255-
new SimpleForwardingClientCallListener<RespT>(responseListener) {
256-
@Override
257-
public void onMessage(RespT message) {
258-
if (injectError.getAndSet(false)) {
259-
errorInjected.set(true);
260-
clientCall.cancel("Cancelling call for injected error", null);
261-
} else {
262-
super.onMessage(message);
263-
}
264-
}
265-
266-
@Override
267-
public void onClose(Status status, Metadata metadata) {
268-
if (errorInjected.get()) {
269-
status = Status.UNAVAILABLE.augmentDescription("INJECTED BY TEST");
270-
}
271-
super.onClose(status, metadata);
272-
}
273-
},
274-
headers);
275-
}
276-
};
277-
}
278-
};
279-
return Collections.singletonList(interceptor);
280-
}
281-
}
282-
283-
@Test
284-
public void testRetryNonIdempotentRpcsReturningLongRunningOperations() throws Exception {
285-
assumeFalse(
286-
"Querying long-running operations is not supported on the emulator", isUsingEmulator());
287-
288-
// RPCs that return a long-running operation such as CreateDatabase, CreateBackup and
289-
// RestoreDatabase are non-idempotent and can normally not be automatically retried in case of a
290-
// transient failure. The client library will however automatically query the backend to check
291-
// whether the corresponding operation was started or not, and if it was, it will pick up the
292-
// existing operation. If no operation is found, a new RPC call will be executed to start the
293-
// operation.
294-
295-
List<Database> databases = new ArrayList<>();
296-
List<Backup> backups = new ArrayList<>();
297-
String initialDatabaseId;
298-
Timestamp initialDbCreateTime;
299-
300-
try {
301-
// CreateDatabase
302-
InjectErrorInterceptorProvider createDbInterceptor =
303-
new InjectErrorInterceptorProvider("CreateDatabase");
304-
SpannerOptions options =
305-
testHelper.getOptions().toBuilder().setInterceptorProvider(createDbInterceptor).build();
306-
try (Spanner spanner = options.getService()) {
307-
initialDatabaseId = testHelper.getUniqueDatabaseId();
308-
DatabaseAdminClient client = spanner.getDatabaseAdminClient();
309-
OperationFuture<Database, CreateDatabaseMetadata> op =
310-
client.createDatabase(
311-
testHelper.getInstanceId().getInstance(),
312-
initialDatabaseId,
313-
Collections.emptyList());
314-
databases.add(op.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES));
315-
// Keep track of the original create time of this database, as we will drop this database
316-
// later and create another one with the exact same name. That means that the ListOperations
317-
// call will return at least two CreateDatabase operations. The retry logic should always
318-
// pick the last one.
319-
initialDbCreateTime = op.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES).getCreateTime();
320-
// Assert that the CreateDatabase RPC was called only once, and that the operation tracking
321-
// was resumed through a GetOperation call.
322-
assertThat(createDbInterceptor.methodCount.get()).isEqualTo(1);
323-
assertThat(createDbInterceptor.getOperationCount.get()).isAtLeast(1);
324-
}
325-
326-
// CreateBackup
327-
InjectErrorInterceptorProvider createBackupInterceptor =
328-
new InjectErrorInterceptorProvider("CreateBackup");
329-
options =
330-
testHelper
331-
.getOptions()
332-
.toBuilder()
333-
.setInterceptorProvider(createBackupInterceptor)
334-
.build();
335-
try (Spanner spanner = options.getService()) {
336-
String databaseId = databases.get(0).getId().getDatabase();
337-
String backupId = String.format("test-bck-%08d", new Random().nextInt(100000000));
338-
DatabaseAdminClient client = spanner.getDatabaseAdminClient();
339-
OperationFuture<Backup, CreateBackupMetadata> op =
340-
client.createBackup(
341-
testHelper.getInstanceId().getInstance(),
342-
backupId,
343-
databaseId,
344-
Timestamp.ofTimeSecondsAndNanos(
345-
Timestamp.now().getSeconds() + TimeUnit.SECONDS.convert(7L, TimeUnit.DAYS), 0));
346-
backups.add(op.get(BACKUP_TIMEOUT_MINUTES, TimeUnit.MINUTES));
347-
// Assert that the CreateBackup RPC was called only once, and that the operation tracking
348-
// was resumed through a GetOperation call.
349-
assertThat(createDbInterceptor.methodCount.get()).isEqualTo(1);
350-
assertThat(createDbInterceptor.getOperationCount.get()).isAtLeast(1);
351-
}
352-
353-
// RestoreBackup
354-
int attempts = 0;
355-
while (true) {
356-
InjectErrorInterceptorProvider restoreBackupInterceptor =
357-
new InjectErrorInterceptorProvider("RestoreBackup");
358-
options =
359-
testHelper
360-
.getOptions()
361-
.toBuilder()
362-
.setInterceptorProvider(restoreBackupInterceptor)
363-
.build();
364-
try (Spanner spanner = options.getService()) {
365-
String backupId = backups.get(0).getId().getBackup();
366-
String restoredDbId = testHelper.getUniqueDatabaseId();
367-
DatabaseAdminClient client = spanner.getDatabaseAdminClient();
368-
OperationFuture<Database, RestoreDatabaseMetadata> op =
369-
client.restoreDatabase(
370-
testHelper.getInstanceId().getInstance(),
371-
backupId,
372-
testHelper.getInstanceId().getInstance(),
373-
restoredDbId);
374-
databases.add(op.get(RESTORE_TIMEOUT_MINUTES, TimeUnit.MINUTES));
375-
// Assert that the RestoreDatabase RPC was called only once, and that the operation
376-
// tracking was resumed through a GetOperation call.
377-
assertThat(createDbInterceptor.methodCount.get()).isEqualTo(1);
378-
assertThat(createDbInterceptor.getOperationCount.get()).isAtLeast(1);
379-
break;
380-
} catch (ExecutionException e) {
381-
if (e.getCause() instanceof SpannerException
382-
&& ((SpannerException) e.getCause()).getErrorCode() == ErrorCode.FAILED_PRECONDITION
383-
&& e.getCause()
384-
.getMessage()
385-
.contains("Please retry the operation once the pending restores complete")) {
386-
attempts++;
387-
if (attempts == 10) {
388-
// Still same error after 10 attempts. Ignore.
389-
break;
390-
}
391-
// wait and then retry.
392-
Thread.sleep(60_000L);
393-
} else {
394-
throw e;
395-
}
396-
}
397-
}
398-
399-
// Create another database with the exact same name as the first database.
400-
createDbInterceptor = new InjectErrorInterceptorProvider("CreateDatabase");
401-
options =
402-
testHelper.getOptions().toBuilder().setInterceptorProvider(createDbInterceptor).build();
403-
try (Spanner spanner = options.getService()) {
404-
DatabaseAdminClient client = spanner.getDatabaseAdminClient();
405-
// First drop the initial database.
406-
client.dropDatabase(testHelper.getInstanceId().getInstance(), initialDatabaseId);
407-
// Now re-create a database with the exact same name.
408-
OperationFuture<Database, CreateDatabaseMetadata> op =
409-
client.createDatabase(
410-
testHelper.getInstanceId().getInstance(),
411-
initialDatabaseId,
412-
Collections.emptyList());
413-
// Check that the second database was created and has a greater creation time than the
414-
// first.
415-
Timestamp secondCreationTime =
416-
op.get(DATABASE_TIMEOUT_MINUTES, TimeUnit.MINUTES).getCreateTime();
417-
// TODO: Change this to greaterThan when the create time of a database is reported back by
418-
// the server.
419-
assertThat(secondCreationTime).isAtLeast(initialDbCreateTime);
420-
// Assert that the CreateDatabase RPC was called only once, and that the operation tracking
421-
// was resumed through a GetOperation call.
422-
assertThat(createDbInterceptor.methodCount.get()).isEqualTo(1);
423-
assertThat(createDbInterceptor.getOperationCount.get()).isAtLeast(1);
424-
}
425-
} finally {
426-
DatabaseAdminClient client = testHelper.getClient().getDatabaseAdminClient();
427-
for (Database database : databases) {
428-
client.dropDatabase(
429-
database.getId().getInstanceId().getInstance(), database.getId().getDatabase());
430-
}
431-
for (Backup backup : backups) {
432-
client.deleteBackup(backup.getInstanceId().getInstance(), backup.getId().getBackup());
433-
}
434-
}
435-
}
436197
}

google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITPitrUpdateDatabaseTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
@RunWith(JUnit4.class)
5252
public class ITPitrUpdateDatabaseTest {
5353

54-
private static final Duration OPERATION_TIMEOUT = Duration.ofMinutes(5);
54+
private static final Duration OPERATION_TIMEOUT = Duration.ofMinutes(20);
5555
private static final String VERSION_RETENTION_PERIOD = "7d";
5656

5757
@ClassRule public static IntegrationTestEnv env = new IntegrationTestEnv();

0 commit comments

Comments
 (0)