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: implement cross-project table restore #1536

Merged
merged 3 commits into from Dec 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@ -29,37 +29,57 @@ public final class RestoreTableRequest {
private final String sourceBackupId;
private final String sourceClusterId;
private final String sourceInstanceId;
private final String sourceProjectId;

/**
* 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.
* instance and project as the destination table. To restore a table from a backup in another
* instance, use {@link #of(String, String, String) of} method. To restore a table from a backup
* in another project, use {@link #of(String, String, String, String) of} method.
*/
public static RestoreTableRequest of(String sourceClusterId, String sourceBackupId) {
RestoreTableRequest request = new RestoreTableRequest(null, sourceClusterId, sourceBackupId);
RestoreTableRequest request =
new RestoreTableRequest(null, sourceClusterId, sourceBackupId, null);
return request;
}

/**
* Create a {@link RestoreTableRequest} object. The source backup could locate in a the same or a
* different instance.
* Create a {@link RestoreTableRequest} object. The source backup could locate in the same or a
* different instance but the same project as the destination table. To restore a table from a
* backup in another project, use {@link #of(String, String, String, String) of} method.
*/
public static RestoreTableRequest of(
String sourceInstanceId, String sourceClusterId, String sourceBackupId) {
RestoreTableRequest request =
new RestoreTableRequest(sourceInstanceId, sourceClusterId, sourceBackupId);
new RestoreTableRequest(sourceInstanceId, sourceClusterId, sourceBackupId, null);
return request;
}

/**
* Create a {@link RestoreTableRequest} object. The source backup could locate in the same or a
* different instance and/or project.
*/
public static RestoreTableRequest of(
String sourceInstanceId,
String sourceClusterId,
String sourceBackupId,
String sourceProjectId) {
RestoreTableRequest request =
new RestoreTableRequest(sourceInstanceId, sourceClusterId, sourceBackupId, sourceProjectId);
return request;
}

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

public RestoreTableRequest setTableId(String tableId) {
Expand All @@ -80,13 +100,18 @@ public boolean equals(Object o) {
return Objects.equal(requestBuilder.getTableId(), that.requestBuilder.getTableId())
&& Objects.equal(sourceInstanceId, that.sourceInstanceId)
&& Objects.equal(sourceClusterId, that.sourceClusterId)
&& Objects.equal(sourceBackupId, that.sourceBackupId);
&& Objects.equal(sourceBackupId, that.sourceBackupId)
&& Objects.equal(sourceProjectId, that.sourceProjectId);
}

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

@InternalApi
Expand All @@ -99,7 +124,7 @@ public com.google.bigtable.admin.v2.RestoreTableRequest toProto(
.setParent(NameUtil.formatInstanceName(projectId, instanceId))
.setBackup(
NameUtil.formatBackupName(
projectId,
sourceProjectId == null ? projectId : sourceProjectId,
sourceInstanceId == null ? instanceId : sourceInstanceId,
sourceClusterId,
sourceBackupId))
Expand Down
Expand Up @@ -613,6 +613,54 @@ public void testRestoreTable() throws ExecutionException, InterruptedException {
assertThat(actualResult.getTable().getId()).isEqualTo(TABLE_ID);
}

@Test
public void testRestoreTableCrossProject() throws ExecutionException, InterruptedException {
// Setup
Mockito.when(mockStub.restoreTableOperationCallable())
.thenReturn(mockRestoreTableOperationCallable);

Timestamp startTime = Timestamp.newBuilder().setSeconds(1234).build();
Timestamp endTime = Timestamp.newBuilder().setSeconds(5678).build();
String operationName = "my-operation";

// Use existing adminClient as destination project:
String dstProjectId = PROJECT_ID;
String dstInstanceId = INSTANCE_ID;
String dstTableName = TABLE_NAME;

// Create RestoreTableRequest from different source project:
String srcProjectId = "src-project";
String srcInstanceId = "src-instance";
String srcClusterId = "src-cluster";

RestoreTableRequest req =
RestoreTableRequest.of(srcInstanceId, srcClusterId, BACKUP_ID, srcProjectId)
.setTableId(TABLE_ID);
mockOperationResult(
mockRestoreTableOperationCallable,
req.toProto(dstProjectId, dstInstanceId),
com.google.bigtable.admin.v2.Table.newBuilder().setName(dstTableName).build(),
RestoreTableMetadata.newBuilder()
.setName(dstTableName)
.setOptimizeTableOperationName(operationName)
.setSourceType(RestoreSourceType.BACKUP)
.setBackupInfo(
BackupInfo.newBuilder()
.setBackup(BACKUP_ID)
.setSourceTable(NameUtil.formatTableName(srcProjectId, srcInstanceId, TABLE_ID))
.setStartTime(startTime)
.setEndTime(endTime)
.build())
.build());

// Execute
RestoredTableResult actualResult = adminClient.restoreTable(req);

// Verify
assertThat(actualResult.getTable().getId()).isEqualTo(TABLE_ID);
assertThat(actualResult.getTable().getInstanceId()).isEqualTo(dstInstanceId);
}

@Test
public void testDeleteBackup() {
// Setup
Expand Down
Expand Up @@ -31,6 +31,7 @@ public class RestoreTableRequestTest {
private static final String INSTANCE_ID = "my-instance";
private static final String CLUSTER_ID = "my-cluster";
private static final String SOURCE_INSTANCE_ID = "source-instance-id";
private static final String SOURCE_PROJECT_ID = "source-project-id";

@Test
public void testToProto() {
Expand Down Expand Up @@ -61,6 +62,23 @@ public void testToProtoCrossInstance() {
assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto);
}

@Test
public void testToProtoCrossProject() {
RestoreTableRequest request =
RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID)
.setTableId(TABLE_ID);

com.google.bigtable.admin.v2.RestoreTableRequest requestProto =
com.google.bigtable.admin.v2.RestoreTableRequest.newBuilder()
.setParent(NameUtil.formatInstanceName(PROJECT_ID, INSTANCE_ID))
.setBackup(
NameUtil.formatBackupName(
SOURCE_PROJECT_ID, SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID))
.setTableId(TABLE_ID)
.build();
assertThat(request.toProto(PROJECT_ID, INSTANCE_ID)).isEqualTo(requestProto);
}

@Test
public void testEquality() {
RestoreTableRequest request =
Expand Down Expand Up @@ -88,6 +106,24 @@ public void testEqualityCrossInstance() {
.setTableId("another-table"));
}

@Test
public void testEqualityCrossProject() {
RestoreTableRequest request =
RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID)
.setTableId(TABLE_ID);

assertThat(request)
.isEqualTo(
RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID)
.setTableId(TABLE_ID));
assertThat(request)
.isNotEqualTo(RestoreTableRequest.of(CLUSTER_ID, BACKUP_ID).setTableId(TABLE_ID));
assertThat(request)
.isNotEqualTo(
RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID)
.setTableId("another-table"));
}

@Test
public void testHashCode() {
RestoreTableRequest request =
Expand Down Expand Up @@ -117,4 +153,24 @@ public void testHashCodeCrossInstance() {
.setTableId("another-table")
.hashCode());
}

@Test
public void testHashCodeCrossProject() {
RestoreTableRequest request =
RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID)
.setTableId(TABLE_ID);
assertThat(request.hashCode())
.isEqualTo(
RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID)
.setTableId(TABLE_ID)
.hashCode());
assertThat(request.hashCode())
.isNotEqualTo(
RestoreTableRequest.of(CLUSTER_ID, BACKUP_ID).setTableId(TABLE_ID).hashCode());
assertThat(request.hashCode())
.isNotEqualTo(
RestoreTableRequest.of(SOURCE_INSTANCE_ID, CLUSTER_ID, BACKUP_ID, SOURCE_PROJECT_ID)
.setTableId("another-table")
.hashCode());
}
}