Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow restore backup to different instance #515

Merged
merged 4 commits into from Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -20,24 +20,47 @@
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/** Fluent wrapper for {@link com.google.bigtable.admin.v2.RestoreTableRequest} */
public final class RestoreTableRequest {
private final com.google.bigtable.admin.v2.RestoreTableRequest.Builder requestBuilder =
com.google.bigtable.admin.v2.RestoreTableRequest.newBuilder();
private final String backupId;
private final String clusterId;
private final String sourceBackupId;
private final String sourceClusterId;
private final String sourceInstanceId;

public static RestoreTableRequest of(String clusterId, String backupId) {
RestoreTableRequest request = new RestoreTableRequest(clusterId, backupId);
/**
* Create a {@link RestoreTableRequest} object. It assumes the source backup locates in the same
* instance as the destination table. To restore a table from a backup in another instance, use
* {@link #of(String, String, String) of} method.
*/
public static RestoreTableRequest of(String sourceClusterId, String sourceBackupId) {
RestoreTableRequest request = new RestoreTableRequest(null, sourceClusterId, sourceBackupId);
return request;
}

private RestoreTableRequest(String clusterId, String backupId) {
Preconditions.checkNotNull(clusterId);
Preconditions.checkNotNull(backupId);
this.backupId = backupId;
this.clusterId = clusterId;
/**
* Create a {@link RestoreTableRequest} object. It assumes the source backup locates in the same
liubonan marked this conversation as resolved.
Show resolved Hide resolved
* instance as the destination table. To restore a table from a backup in another instance, use
* {@link #of(String, String, String) of} method.
*/
public static RestoreTableRequest of(
String sourceInstanceId, String sourceClusterId, String sourceBackupId) {
kolea2 marked this conversation as resolved.
Show resolved Hide resolved
liubonan marked this conversation as resolved.
Show resolved Hide resolved
RestoreTableRequest request =
new RestoreTableRequest(sourceInstanceId, sourceClusterId, sourceBackupId);
return request;
}

private RestoreTableRequest(
@Nullable String sourceInstanceId,
@Nonnull String sourceClusterId,
@Nonnull String sourceBackupId) {
Preconditions.checkNotNull(sourceClusterId);
Preconditions.checkNotNull(sourceBackupId);
this.sourceBackupId = sourceBackupId;
this.sourceInstanceId = sourceInstanceId;
this.sourceClusterId = sourceClusterId;
}

public RestoreTableRequest setTableId(String tableId) {
Expand All @@ -56,13 +79,15 @@ public boolean equals(Object o) {
}
RestoreTableRequest that = (RestoreTableRequest) o;
return Objects.equal(requestBuilder.getTableId(), that.requestBuilder.getTableId())
&& Objects.equal(clusterId, that.clusterId)
&& Objects.equal(backupId, that.backupId);
&& Objects.equal(sourceInstanceId, that.sourceInstanceId)
&& Objects.equal(sourceClusterId, that.sourceClusterId)
&& Objects.equal(sourceBackupId, that.sourceBackupId);
}

@Override
public int hashCode() {
return Objects.hashCode(requestBuilder.getTableId(), clusterId, backupId);
return Objects.hashCode(
requestBuilder.getTableId(), sourceInstanceId, sourceClusterId, sourceBackupId);
}

@InternalApi
Expand All @@ -73,7 +98,12 @@ public com.google.bigtable.admin.v2.RestoreTableRequest toProto(

return requestBuilder
.setParent(NameUtil.formatInstanceName(projectId, instanceId))
.setBackup(NameUtil.formatBackupName(projectId, instanceId, clusterId, backupId))
.setBackup(
NameUtil.formatBackupName(
projectId,
sourceInstanceId == null ? instanceId : sourceInstanceId,
sourceClusterId,
sourceBackupId))
.build();
}
}
Expand Up @@ -266,32 +266,82 @@ public void deleteBackupTest() throws InterruptedException {
@Test
public void restoreTableTest() throws InterruptedException, ExecutionException {
String backupId = generateId("restore-" + TEST_BACKUP_SUFFIX);
String tableId = generateId("restored-table");
String restoredTableId = generateId("restored-table");
tableAdmin.createBackup(createBackupRequest(backupId));

// Wait 2 minutes so that the RestoreTable API will trigger an optimize restored
// table operation.
Thread.sleep(120 * 1000);

try {
RestoreTableRequest req = RestoreTableRequest.of(targetCluster, backupId).setTableId(tableId);
RestoreTableRequest req =
RestoreTableRequest.of(targetCluster, backupId).setTableId(restoredTableId);
RestoredTableResult result = tableAdmin.restoreTable(req);
assertWithMessage("Incorrect restored table id")
.that(result.getTable().getId())
.isEqualTo(tableId);
.isEqualTo(restoredTableId);

if (result.getOptimizeRestoredTableOperationToken() != null) {
// The assertion might be missing if the test is running against a HDD cluster or an
// optimization is not necessary.
tableAdmin.awaitOptimizeRestoredTable(result.getOptimizeRestoredTableOperationToken());
Table restoredTable = tableAdmin.getTable(tableId);
Table restoredTable = tableAdmin.getTable(restoredTableId);
assertWithMessage("Incorrect restored table id")
.that(restoredTable.getId())
.isEqualTo(tableId);
.isEqualTo(restoredTableId);
}
} finally {
tableAdmin.deleteBackup(targetCluster, backupId);
tableAdmin.deleteTable(tableId);
tableAdmin.deleteTable(restoredTableId);
}
}

@Test
liubonan marked this conversation as resolved.
Show resolved Hide resolved
public void crossInstanceRestoreTest()
throws InterruptedException, IOException, ExecutionException {
String backupId = generateId("cross-" + TEST_BACKUP_SUFFIX);
String restoredTableId = generateId("restored-table");
tableAdmin.createBackup(createBackupRequest(backupId));

// Wait 2 minutes so that the RestoreTable API will trigger an optimize restored
// table operation.
Thread.sleep(120 * 1000);

// Set up a new instance to test cross-instance restore.
String destInstance =
AbstractTestEnv.TEST_INSTANCE_PREFIX + "backup-" + Instant.now().getEpochSecond();
String destCluster = AbstractTestEnv.TEST_CLUSTER_PREFIX + Instant.now().getEpochSecond();
instanceAdmin.createInstance(
CreateInstanceRequest.of(destInstance)
.addCluster(destCluster, testEnvRule.env().getSecondaryZone(), 3, StorageType.SSD)
.setDisplayName("backups-dest-test-instance")
.addLabel("state", "readytodelete")
.setType(Type.PRODUCTION));
BigtableTableAdminClient destTableAdmin =
testEnvRule.env().getTableAdminClientForInstance(destInstance);

try {
RestoreTableRequest req =
RestoreTableRequest.of(targetInstance, targetCluster, backupId)
.setTableId(restoredTableId);
RestoredTableResult result = destTableAdmin.restoreTable(req);
assertWithMessage("Incorrect restored table id")
.that(result.getTable().getId())
.isEqualTo(restoredTableId);
assertWithMessage("Incorrect instance id")
.that(result.getTable().getInstanceId())
.isEqualTo(destInstance);

// The assertion might be missing if the test is running against a HDD cluster or an
// optimization is not necessary.
assertWithMessage("Empty OptimizeRestoredTable token")
.that(result.getOptimizeRestoredTableOperationToken())
.isNotNull();
tableAdmin.awaitOptimizeRestoredTable(result.getOptimizeRestoredTableOperationToken());
destTableAdmin.getTable(restoredTableId);
} finally {
tableAdmin.deleteBackup(targetCluster, backupId);
instanceAdmin.deleteInstance(destInstance);
}
}

Expand Down