setAuthorizedViewIamPolicyAsync(
+ String tableId, String authorizedViewId, Policy policy) {
+ String authorizedViewName =
+ NameUtil.formatAuthorizedViewName(projectId, instanceId, tableId, authorizedViewId);
+ return setResourceIamPolicy(policy, authorizedViewName);
+ }
+
+ /**
+ * Tests whether the caller has the given permissions for the specified authorized view. Returns a
+ * subset of the specified permissions that the caller has.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * List grantedPermissions = client.testAuthorizedViewIamPermission("my-table-id", "my-authorized-view-id",
+ * "bigtable.authorizedViews.get", "bigtable.authorizedViews.delete");
+ * }
+ *
+ * System.out.println("Has get access: " +
+ * grantedPermissions.contains("bigtable.authorizedViews.get"));
+ *
+ * System.out.println("Has delete access: " +
+ * grantedPermissions.contains("bigtable.authorizedViews.delete"));
+ *
+ * @see Cloud Bigtable
+ * permissions
+ */
+ @SuppressWarnings({"WeakerAccess"})
+ public List testAuthorizedViewIamPermission(
+ String tableId, String authorizedViewId, String... permissions) {
+ return ApiExceptions.callAndTranslateApiException(
+ testAuthorizedViewIamPermissionAsync(tableId, authorizedViewId, permissions));
+ }
+
+ /**
+ * Asynchronously tests whether the caller has the given permissions for the specified authorized
+ * view. Returns a subset of the specified permissions that the caller has.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * ApiFuture> grantedPermissionsFuture = client.testAuthorizedViewIamPermissionAsync("my-table-id", "my-authorized-view-id",
+ * "bigtable.authorizedViews.get", "bigtable.authorizedViews.delete");
+ *
+ * ApiFutures.addCallback(grantedPermissionsFuture,
+ * new ApiFutureCallback>() {
+ * public void onSuccess(List grantedPermissions) {
+ * System.out.println("Has get access: " + grantedPermissions.contains("bigtable.authorizedViews.get"));
+ * System.out.println("Has delete access: " + grantedPermissions.contains("bigtable.authorizedViews.delete"));
+ * }
+ *
+ * public void onFailure(Throwable t) {
+ * t.printStackTrace();
+ * }
+ * },
+ * MoreExecutors.directExecutor());
+ * }
+ *
+ * @see Cloud Bigtable
+ * permissions
+ */
+ @SuppressWarnings({"WeakerAccess"})
+ public ApiFuture> testAuthorizedViewIamPermissionAsync(
+ String tableId, String authorizedViewId, String... permissions) {
+ String authorizedViewName =
+ NameUtil.formatAuthorizedViewName(projectId, instanceId, tableId, authorizedViewId);
+ return testResourceIamPermissions(authorizedViewName, permissions);
+ }
+
private ApiFuture getResourceIamPolicy(String name) {
GetIamPolicyRequest request = GetIamPolicyRequest.newBuilder().setResource(name).build();
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/internal/NameUtil.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/internal/NameUtil.java
index 8cccf3d578..a2b59d6b5b 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/internal/NameUtil.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/internal/NameUtil.java
@@ -34,6 +34,9 @@ public class NameUtil {
private static final Pattern BACKUP_PATTERN =
Pattern.compile("projects/([^/]+)/instances/([^/]+)/clusters/([^/]+)/backups/([^/]+)");
+ private static final Pattern AUTHORIZED_VIEW_PATTERN =
+ Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)/authorizedViews/([^/]+)");
+
public static String formatProjectName(String projectId) {
return "projects/" + projectId;
}
@@ -55,6 +58,11 @@ public static String formatBackupName(
return formatClusterName(projectId, instanceId, clusterId) + "/backups/" + backupId;
}
+ public static String formatAuthorizedViewName(
+ String projectId, String instanceId, String tableId, String viewId) {
+ return formatTableName(projectId, instanceId, tableId) + "/authorizedViews/" + viewId;
+ }
+
public static String extractTableIdFromTableName(String fullTableName) {
Matcher matcher = TABLE_PATTERN.matcher(fullTableName);
if (!matcher.matches()) {
@@ -71,6 +79,15 @@ public static String extractBackupIdFromBackupName(String fullBackupName) {
return matcher.group(4);
}
+ public static String extractAuthorizedViewIdFromAuthorizedViewName(
+ String fullAuthorizedViewName) {
+ Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
+ }
+ return matcher.group(4);
+ }
+
public static String extractZoneIdFromLocationName(String fullLocationName) {
Matcher matcher = LOCATION_PATTERN.matcher(fullLocationName);
if (!matcher.matches()) {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AuthorizedView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AuthorizedView.java
new file mode 100644
index 0000000000..33e40f6458
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/AuthorizedView.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.admin.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.api.core.InternalExtensionOnly;
+import com.google.bigtable.admin.v2.AuthorizedViewName;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+
+/**
+ * A class that wraps the {@link com.google.bigtable.admin.v2.AuthorizedView} protocol buffer
+ * object.
+ *
+ * An AuthorizedView represents subsets of a particular table based on rules. The access to each
+ * AuthorizedView can be configured separately from the Table.
+ *
+ *
Users can perform read/write operation on an AuthorizedView by providing an authorizedView id
+ * besides a table id, in which case the semantics remain identical as reading/writing on a Table
+ * except that visibility is restricted to the subset of the Table that the AuthorizedView
+ * represents.
+ */
+public final class AuthorizedView {
+ private final com.google.bigtable.admin.v2.AuthorizedView proto;
+
+ /**
+ * Wraps the protobuf. This method is considered an internal implementation detail and not meant
+ * to be used by applications.
+ */
+ @InternalApi
+ public static AuthorizedView fromProto(
+ @Nonnull com.google.bigtable.admin.v2.AuthorizedView proto) {
+ return new AuthorizedView(proto);
+ }
+
+ private AuthorizedView(@Nonnull com.google.bigtable.admin.v2.AuthorizedView proto) {
+ Preconditions.checkNotNull(proto);
+ Preconditions.checkArgument(!proto.getName().isEmpty(), "AuthorizedView must have a name");
+ Preconditions.checkArgument(
+ proto.hasSubsetView(), "AuthorizedView must have a subset_view field");
+ this.proto = proto;
+ }
+
+ /** Gets the authorized view's id. */
+ public String getId() {
+ // Constructor ensures that name is not null.
+ AuthorizedViewName fullName = AuthorizedViewName.parse(proto.getName());
+
+ //noinspection ConstantConditions
+ return fullName.getAuthorizedView();
+ }
+
+ /** Gets the id of the table that owns this authorized view. */
+ public String getTableId() {
+ // Constructor ensures that name is not null.
+ AuthorizedViewName fullName = AuthorizedViewName.parse(proto.getName());
+
+ //noinspection ConstantConditions
+ return fullName.getTable();
+ }
+
+ /** Returns whether this authorized view is deletion protected. */
+ public boolean isDeletionProtected() {
+ return proto.getDeletionProtection();
+ }
+
+ /** Gets the type of this authorized view, which currently can only be a subset view. */
+ public AuthorizedViewType getAuthorizedViewType() {
+ if (proto.hasSubsetView()) {
+ return SubsetView.fromProto(proto.getSubsetView());
+ } else {
+ // Should never happen because the constructor verifies that one must exist.
+ throw new IllegalStateException("This AuthorizedView doesn't have a valid type specified");
+ }
+ }
+
+ /**
+ * Creates the request protobuf. This method is considered an internal implementation detail and
+ * not meant to be used by applications.
+ */
+ @InternalApi
+ public com.google.bigtable.admin.v2.AuthorizedView toProto() {
+ return proto;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AuthorizedView that = (AuthorizedView) o;
+ return Objects.equal(proto, that.proto);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(proto);
+ }
+
+ /**
+ * Represents a subset of a Table. Please check the implementations of this interface for more
+ * details.
+ */
+ @InternalExtensionOnly
+ public interface AuthorizedViewType {}
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateAuthorizedViewRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateAuthorizedViewRequest.java
new file mode 100644
index 0000000000..0c251fa666
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/CreateAuthorizedViewRequest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.admin.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView.AuthorizedViewType;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+
+/**
+ * Parameters for creating a new Cloud Bigtable {@link AuthorizedView}, which represents subsets of
+ * a particular table.
+ *
+ *
Sample code:
+ *
+ *
{@code
+ * CreateAuthorizedViewRequest request =
+ * CreateAuthorizedViewRequest.of("my-table", "my-new-authorized-view")
+ * .setAuthorizedViewType(
+ * SubsetView.create()
+ * .addRowPrefix("row#")
+ * .addFamilySubsets(
+ * "my-family", FamilySubsets.create().addQualifier("column")));
+ * }
+ *
+ * @see AuthorizedView for more details.
+ */
+public final class CreateAuthorizedViewRequest {
+ private final String tableId;
+ private final com.google.bigtable.admin.v2.CreateAuthorizedViewRequest.Builder requestBuilder =
+ com.google.bigtable.admin.v2.CreateAuthorizedViewRequest.newBuilder();
+
+ public static CreateAuthorizedViewRequest of(
+ @Nonnull String tableId, @Nonnull String authorizedViewId) {
+ return new CreateAuthorizedViewRequest(tableId, authorizedViewId);
+ }
+
+ private CreateAuthorizedViewRequest(@Nonnull String tableId, @Nonnull String authorizedViewId) {
+ Preconditions.checkNotNull(tableId, "tableId must be set");
+ Preconditions.checkNotNull(authorizedViewId, "authorizedViewId must be set");
+
+ this.tableId = tableId;
+ requestBuilder.setAuthorizedViewId(authorizedViewId);
+ }
+
+ /** Configures if the authorized view is deletion protected. */
+ public CreateAuthorizedViewRequest setDeletionProtection(boolean deletionProtection) {
+ requestBuilder.getAuthorizedViewBuilder().setDeletionProtection(deletionProtection);
+ return this;
+ }
+
+ /**
+ * Sets the implementation for this authorized view.
+ *
+ * @see AuthorizedViewType for details.
+ */
+ public CreateAuthorizedViewRequest setAuthorizedViewType(
+ @Nonnull AuthorizedViewType authorizedViewType) {
+ Preconditions.checkNotNull(authorizedViewType, "authorizedViewType must be set");
+
+ if (authorizedViewType instanceof SubsetView) {
+ requestBuilder
+ .getAuthorizedViewBuilder()
+ .setSubsetView(((SubsetView) authorizedViewType).toProto());
+ } else {
+ throw new IllegalArgumentException("Unknown authorizedViewType: " + authorizedViewType);
+ }
+
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CreateAuthorizedViewRequest that = (CreateAuthorizedViewRequest) o;
+ return Objects.equal(requestBuilder.build(), that.requestBuilder.build())
+ && Objects.equal(tableId, that.tableId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(requestBuilder.build(), tableId);
+ }
+
+ /**
+ * Creates the request protobuf. This method is considered an internal implementation detail and
+ * not meant to be used by applications.
+ */
+ @InternalApi
+ public com.google.bigtable.admin.v2.CreateAuthorizedViewRequest toProto(
+ @Nonnull String projectId, @Nonnull String instanceId) {
+ return requestBuilder
+ .setParent(NameUtil.formatTableName(projectId, instanceId, tableId))
+ .build();
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/FamilySubsets.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/FamilySubsets.java
new file mode 100644
index 0000000000..e80452b2af
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/FamilySubsets.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.admin.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ByteString;
+import java.util.List;
+import javax.annotation.Nonnull;
+
+/** Represents subsets of a particular column family that are included in this authorized view. */
+public final class FamilySubsets {
+ private final com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets.Builder builder;
+
+ /**
+ * Wraps the protobuf. This method is considered an internal implementation detail and not meant
+ * to be used by applications.
+ */
+ @InternalApi
+ public static FamilySubsets fromProto(
+ @Nonnull com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets proto) {
+ return new FamilySubsets(proto);
+ }
+
+ public static FamilySubsets create() {
+ return new FamilySubsets();
+ }
+
+ private FamilySubsets(@Nonnull com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets proto) {
+ this.builder = proto.toBuilder();
+ }
+
+ private FamilySubsets() {
+ this.builder = com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets.newBuilder();
+ }
+
+ /** Gets the list of column qualifiers included in this authorized view. */
+ public List getQualifiers() {
+ return ImmutableList.copyOf(this.builder.getQualifiersList());
+ }
+
+ /** Gets the list of column qualifier prefixes included in this authorized view. */
+ public List getQualifierPrefixes() {
+ return ImmutableList.copyOf(this.builder.getQualifierPrefixesList());
+ }
+
+ /** Adds an individual column qualifier to be included in this authorized view. */
+ public FamilySubsets addQualifier(ByteString qualifier) {
+ this.builder.addQualifiers(qualifier);
+ return this;
+ }
+
+ /** Adds an individual column qualifier to be included in this authorized view. */
+ public FamilySubsets addQualifier(String qualifier) {
+ this.builder.addQualifiers(ByteString.copyFromUtf8(qualifier));
+ return this;
+ }
+
+ /**
+ * Adds a prefix for column qualifiers to be included in this authorized view. Every qualifier
+ * starting with the prefix will be included in this authorized view. An empty string ("") prefix
+ * means to provide access to all qualifiers.
+ */
+ public FamilySubsets addQualifierPrefix(ByteString qualifierPrefix) {
+ this.builder.addQualifierPrefixes(qualifierPrefix);
+ return this;
+ }
+
+ /**
+ * Adds a prefix for column qualifiers to be included in this authorized view. Every qualifier
+ * starting with the prefix will be included in this authorized view. An empty string ("") prefix
+ * means to provide access to all qualifiers.
+ */
+ public FamilySubsets addQualifierPrefix(String qualifierPrefix) {
+ this.builder.addQualifierPrefixes(ByteString.copyFromUtf8(qualifierPrefix));
+ return this;
+ }
+
+ /**
+ * Creates the request protobuf. This method is considered an internal implementation detail and
+ * not meant to be used by applications.
+ */
+ @InternalApi
+ public com.google.bigtable.admin.v2.AuthorizedView.FamilySubsets toProto() {
+ return builder.build();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ FamilySubsets that = (FamilySubsets) o;
+ return Objects.equal(builder.build(), that.builder.build());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(builder.build());
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SubsetView.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SubsetView.java
new file mode 100644
index 0000000000..6ace603e46
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/SubsetView.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.admin.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView.AuthorizedViewType;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.protobuf.ByteString;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.annotation.Nonnull;
+
+/**
+ * Defines a simple authorized view that is a subset of the underlying Table.
+ *
+ * Users can specify the rows in the form of row key prefixes, and specify the column families by
+ * adding the family id along with its familySubsets rule to the family subsets map. The subset is
+ * defined by the intersection of the specified row key prefixes and column family subsets.
+ */
+public class SubsetView implements AuthorizedViewType {
+ private final com.google.bigtable.admin.v2.AuthorizedView.SubsetView.Builder builder;
+
+ /**
+ * Wraps the protobuf. This method is considered an internal implementation detail and not meant
+ * to be used by applications.
+ */
+ @InternalApi
+ public static SubsetView fromProto(
+ @Nonnull com.google.bigtable.admin.v2.AuthorizedView.SubsetView proto) {
+ return new SubsetView(proto);
+ }
+
+ public static SubsetView create() {
+ return new SubsetView();
+ }
+
+ private SubsetView(@Nonnull com.google.bigtable.admin.v2.AuthorizedView.SubsetView proto) {
+ this.builder = proto.toBuilder();
+ }
+
+ private SubsetView() {
+ this.builder = com.google.bigtable.admin.v2.AuthorizedView.SubsetView.newBuilder();
+ }
+
+ /** Gets the row prefixes to be included in this subset view. */
+ public List getRowPrefixes() {
+ return ImmutableList.copyOf(this.builder.getRowPrefixesList());
+ }
+
+ /** Gets the map from familyId to familySubsets in this subset view. */
+ public Map getFamilySubsets() {
+ ImmutableMap.Builder familySubsets = ImmutableMap.builder();
+ for (Entry entry :
+ builder.getFamilySubsetsMap().entrySet()) {
+ familySubsets.put(entry.getKey(), FamilySubsets.fromProto(entry.getValue()));
+ }
+ return familySubsets.build();
+ }
+
+ /** Adds a new rowPrefix to the subset view. */
+ public SubsetView addRowPrefix(ByteString rowPrefix) {
+ this.builder.addRowPrefixes(rowPrefix);
+ return this;
+ }
+
+ /** Adds a new rowPrefix to the subset view. */
+ public SubsetView addRowPrefix(String rowPrefix) {
+ this.builder.addRowPrefixes(ByteString.copyFromUtf8(rowPrefix));
+ return this;
+ }
+
+ /**
+ * Adds a new familyId with its familySubsets to the subset view. Please note that calling this
+ * method with the same familyId will overwrite the previous rule set on the family.
+ */
+ public SubsetView setFamilySubsets(String familyId, FamilySubsets familySubsets) {
+ this.builder.putFamilySubsets(familyId, familySubsets.toProto());
+ return this;
+ }
+
+ /**
+ * Creates the request protobuf. This method is considered an internal implementation detail and
+ * not meant to be used by applications.
+ */
+ @InternalApi
+ public com.google.bigtable.admin.v2.AuthorizedView.SubsetView toProto() {
+ return builder.build();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SubsetView that = (SubsetView) o;
+ return Objects.equal(builder.build(), that.builder.build());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(builder.build());
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateAuthorizedViewRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateAuthorizedViewRequest.java
new file mode 100644
index 0000000000..fbb54c994e
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/admin/v2/models/UpdateAuthorizedViewRequest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.admin.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.cloud.bigtable.admin.v2.internal.NameUtil;
+import com.google.cloud.bigtable.admin.v2.models.AuthorizedView.AuthorizedViewType;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.protobuf.FieldMask;
+import com.google.protobuf.util.FieldMaskUtil;
+import javax.annotation.Nonnull;
+
+/**
+ * Parameters for updating an existing Cloud Bigtable {@link AuthorizedView}.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * AuthorizedView existingAuthorizedView = client.getAuthorizedView("my-table", "my-authorized-view");
+ * UpdateAuthorizedViewRequest request =
+ * UpdateAuthorizedViewRequest.of(existingAuthorizedView).setDeletionProtection(true);
+ * }
+ *
+ * @see AuthorizedView for more details.
+ */
+public final class UpdateAuthorizedViewRequest {
+ private final com.google.bigtable.admin.v2.UpdateAuthorizedViewRequest.Builder requestBuilder;
+ private final String tableId;
+ private final String authorizedViewId;
+
+ /** Builds a new update request using an existing authorized view. */
+ public static UpdateAuthorizedViewRequest of(@Nonnull AuthorizedView authorizedView) {
+ return new UpdateAuthorizedViewRequest(
+ authorizedView.getTableId(),
+ authorizedView.getId(),
+ com.google.bigtable.admin.v2.UpdateAuthorizedViewRequest.newBuilder()
+ .setAuthorizedView(authorizedView.toProto()));
+ }
+
+ /** Builds a new update authorized view request. */
+ public static UpdateAuthorizedViewRequest of(
+ @Nonnull String tableId, @Nonnull String authorizedViewId) {
+ return new UpdateAuthorizedViewRequest(
+ tableId,
+ authorizedViewId,
+ com.google.bigtable.admin.v2.UpdateAuthorizedViewRequest.newBuilder());
+ }
+
+ private UpdateAuthorizedViewRequest(
+ @Nonnull String tableId,
+ @Nonnull String authorizedViewId,
+ @Nonnull com.google.bigtable.admin.v2.UpdateAuthorizedViewRequest.Builder requestBuilder) {
+ Preconditions.checkNotNull(tableId, "tableId must be set");
+ Preconditions.checkNotNull(authorizedViewId, "authorizedViewId must be set");
+ Preconditions.checkNotNull(requestBuilder, "proto builder must be set");
+
+ this.tableId = tableId;
+ this.authorizedViewId = authorizedViewId;
+ this.requestBuilder = requestBuilder;
+ }
+
+ /** Changes the deletion protection of an existing authorized view. */
+ public UpdateAuthorizedViewRequest setDeletionProtection(boolean deletionProtection) {
+ requestBuilder.getAuthorizedViewBuilder().setDeletionProtection(deletionProtection);
+ updateFieldMask(com.google.bigtable.admin.v2.AuthorizedView.DELETION_PROTECTION_FIELD_NUMBER);
+ return this;
+ }
+
+ /**
+ * Updates the implementation for this authorized view.
+ *
+ * @see AuthorizedViewType for details.
+ */
+ public UpdateAuthorizedViewRequest setAuthorizedViewType(
+ @Nonnull AuthorizedViewType authorizedViewType) {
+ Preconditions.checkNotNull(authorizedViewType, "authorizedViewType must be set");
+
+ if (authorizedViewType instanceof SubsetView) {
+ requestBuilder
+ .getAuthorizedViewBuilder()
+ .setSubsetView(((SubsetView) authorizedViewType).toProto());
+ updateFieldMask(com.google.bigtable.admin.v2.AuthorizedView.SUBSET_VIEW_FIELD_NUMBER);
+ } else {
+ throw new IllegalArgumentException("Unknown authorizedViewType: " + authorizedViewType);
+ }
+
+ return this;
+ }
+
+ /**
+ * Configures if safety warnings should be disabled. If set, then updates that making the
+ * authorized view more restrictive are allowed.
+ */
+ @SuppressWarnings("WeakerAccess")
+ public UpdateAuthorizedViewRequest setIgnoreWarnings(boolean value) {
+ requestBuilder.setIgnoreWarnings(value);
+ return this;
+ }
+
+ private void updateFieldMask(int fieldNumber) {
+ FieldMask newMask =
+ FieldMaskUtil.fromFieldNumbers(
+ com.google.bigtable.admin.v2.AuthorizedView.class, fieldNumber);
+ requestBuilder.setUpdateMask(FieldMaskUtil.union(requestBuilder.getUpdateMask(), newMask));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ UpdateAuthorizedViewRequest that = (UpdateAuthorizedViewRequest) o;
+ return Objects.equal(requestBuilder.build(), that.requestBuilder.build())
+ && Objects.equal(tableId, that.tableId)
+ && Objects.equal(authorizedViewId, that.authorizedViewId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(requestBuilder.build(), tableId, authorizedViewId);
+ }
+
+ /**
+ * Creates the request protobuf. This method is considered an internal implementation detail and
+ * not meant to be used by applications.
+ */
+ @InternalApi
+ public com.google.bigtable.admin.v2.UpdateAuthorizedViewRequest toProto(
+ @Nonnull String projectId, @Nonnull String instanceId) {
+ requestBuilder
+ .getAuthorizedViewBuilder()
+ .setName(
+ NameUtil.formatAuthorizedViewName(projectId, instanceId, tableId, authorizedViewId));
+ return requestBuilder.build();
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java
index f84a5dd098..8f08f82d8a 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java
@@ -44,6 +44,9 @@
import com.google.cloud.bigtable.data.v2.models.RowAdapter;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
+import com.google.cloud.bigtable.data.v2.models.SampleRowKeysRequest;
+import com.google.cloud.bigtable.data.v2.models.TableId;
+import com.google.cloud.bigtable.data.v2.models.TargetId;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.ByteString;
@@ -206,7 +209,9 @@ static BigtableDataClient createWithClientContext(
* }
*
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @deprecated Please use {@link BigtableDataClient#exists(TargetId, String)} instead.
*/
+ @Deprecated
public boolean exists(String tableId, String rowKey) {
return ApiExceptions.callAndTranslateApiException(existsAsync(tableId, rowKey));
}
@@ -233,11 +238,71 @@ public boolean exists(String tableId, String rowKey) {
* }
*
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @deprecated Please use {@link BigtableDataClient#exists(TargetId, ByteString)} instead.
*/
+ @Deprecated
public boolean exists(String tableId, ByteString rowKey) {
return ApiExceptions.callAndTranslateApiException(existsAsync(tableId, rowKey));
}
+ /**
+ * Confirms synchronously if given row key exists or not on the specified {@link TargetId}.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ * String key = "key";
+ *
+ * boolean isRowPresent = bigtableDataClient.exists(TableId.of(tableId), key);
+ *
+ * // Do something with result, for example, display a message
+ * if(isRowPresent) {
+ * System.out.println(key + " is present");
+ * }
+ * } catch(ApiException e) {
+ * e.printStackTrace();
+ * }
+ * }
+ *
+ * @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public boolean exists(TargetId targetId, String rowKey) {
+ return ApiExceptions.callAndTranslateApiException(existsAsync(targetId, rowKey));
+ }
+
+ /**
+ * Confirms synchronously if given row key exists or not on the specified {@link TargetId}.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ * ByteString key = ByteString.copyFromUtf8("key");
+ *
+ * boolean isRowPresent = bigtableDataClient.exists(TableId.of(tableId), key);
+ *
+ * // Do something with result, for example, display a message
+ * if(isRowPresent) {
+ * System.out.println(key.toStringUtf8() + " is present");
+ * }
+ * } catch(ApiException e) {
+ * e.printStackTrace();
+ * }
+ * }
+ *
+ * @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public boolean exists(TargetId targetId, ByteString rowKey) {
+ return ApiExceptions.callAndTranslateApiException(existsAsync(targetId, rowKey));
+ }
+
/**
* Confirms asynchronously if given row key exists or not.
*
@@ -262,7 +327,10 @@ public boolean exists(String tableId, ByteString rowKey) {
* }, MoreExecutors.directExecutor());
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#existsAsync(TargetId, String)} instead.
*/
+ @Deprecated
public ApiFuture existsAsync(String tableId, String rowKey) {
return existsAsync(tableId, ByteString.copyFromUtf8(rowKey));
}
@@ -291,10 +359,77 @@ public ApiFuture existsAsync(String tableId, String rowKey) {
* }, MoreExecutors.directExecutor());
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#existsAsync(TargetId, ByteString)} instead.
*/
+ @Deprecated
public ApiFuture existsAsync(String tableId, ByteString rowKey) {
+ return existsAsync(TableId.of(tableId), rowKey);
+ }
+
+ /**
+ * Confirms asynchronously if given row key exists or not on the specified {@link TargetId}.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ * final String key = "key";
+ *
+ * ApiFuture futureResult = bigtableDataClient.existsAsync(TableId.of(tableId), key);
+ *
+ * ApiFutures.addCallback(futureResult, new ApiFutureCallback() {
+ * public void onFailure(Throwable t) {
+ * t.printStackTrace();
+ * }
+ * public void onSuccess(Boolean isRowPresent) {
+ * if(isRowPresent) {
+ * System.out.println(key + " is present");
+ * }
+ * }
+ * }, MoreExecutors.directExecutor());
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public ApiFuture existsAsync(TargetId targetId, String rowKey) {
+ return existsAsync(targetId, ByteString.copyFromUtf8(rowKey));
+ }
+
+ /**
+ * Confirms asynchronously if given row key exists or not on the specified {@link TargetId}.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ * final ByteString key = ByteString.copyFromUtf8("key");
+ *
+ * ApiFuture futureResult = bigtableDataClient.existsAsync(TableId.of(tableId), key);
+ *
+ * ApiFutures.addCallback(futureResult, new ApiFutureCallback() {
+ * public void onFailure(Throwable t) {
+ * t.printStackTrace();
+ * }
+ * public void onSuccess(Boolean isRowPresent) {
+ * if(isRowPresent) {
+ * System.out.println(key.toStringUtf8() + " is present");
+ * }
+ * }
+ * }, MoreExecutors.directExecutor());
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public ApiFuture existsAsync(TargetId targetId, ByteString rowKey) {
Query query =
- Query.create(tableId)
+ Query.create(targetId)
.rowKey(rowKey)
.filter(
FILTERS
@@ -338,7 +473,9 @@ public Boolean apply(Row row) {
* }
*
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @deprecated Please use {@link BigtableDataClient#readRow(TargetId, ByteString)} instead.
*/
+ @Deprecated
public Row readRow(String tableId, ByteString rowKey) {
return ApiExceptions.callAndTranslateApiException(readRowAsync(tableId, rowKey, null));
}
@@ -368,7 +505,9 @@ public Row readRow(String tableId, ByteString rowKey) {
* }
*
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @deprecated Please use {@link BigtableDataClient#readRow(TargetId, String)} instead.
*/
+ @Deprecated
public Row readRow(String tableId, String rowKey) {
return ApiExceptions.callAndTranslateApiException(
readRowAsync(tableId, ByteString.copyFromUtf8(rowKey), null));
@@ -404,7 +543,9 @@ public Row readRow(String tableId, String rowKey) {
* }
*
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @deprecated Please use {@link BigtableDataClient#readRow(TargetId, String, Filter)} instead.
*/
+ @Deprecated
public Row readRow(String tableId, String rowKey, @Nullable Filter filter) {
return ApiExceptions.callAndTranslateApiException(
readRowAsync(tableId, ByteString.copyFromUtf8(rowKey), filter));
@@ -440,11 +581,154 @@ public Row readRow(String tableId, String rowKey, @Nullable Filter filter) {
* }
*
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @deprecated Please use {@link BigtableDataClient#readRow(TargetId, ByteString, Filter)}
+ * instead.
*/
+ @Deprecated
public Row readRow(String tableId, ByteString rowKey, @Nullable Filter filter) {
return ApiExceptions.callAndTranslateApiException(readRowAsync(tableId, rowKey, filter));
}
+ /**
+ * Convenience method for synchronously reading a single row on the specified {@link TargetId}. If
+ * the row does not exist, the value will be null.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ *
+ * Row row = bigtableDataClient.readRow(TableId.of(tableId), ByteString.copyFromUtf8("key"));
+ * // Do something with row, for example, display all cells
+ * if(row != null) {
+ * System.out.println(row.getKey().toStringUtf8());
+ * for(RowCell cell : row.getCells()) {
+ * System.out.printf("Family: %s Qualifier: %s Value: %s", cell.getFamily(),
+ * cell.getQualifier().toStringUtf8(), cell.getValue().toStringUtf8());
+ * }
+ * }
+ * } catch(ApiException e) {
+ * e.printStackTrace();
+ * }
+ * }
+ *
+ * @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public Row readRow(TargetId targetId, ByteString rowKey) {
+ return ApiExceptions.callAndTranslateApiException(readRowAsync(targetId, rowKey, null));
+ }
+
+ /**
+ * Convenience method for synchronously reading a single row on the specified {@link TargetId}. If
+ * the row does not exist, the value will be null.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ *
+ * Row row = bigtableDataClient.readRow(TableId.of(tableId), "key");
+ * // Do something with row, for example, display all cells
+ * if(row != null) {
+ * System.out.println(row.getKey().toStringUtf8());
+ * for(RowCell cell : row.getCells()) {
+ * System.out.printf("Family: %s Qualifier: %s Value: %s", cell.getFamily(),
+ * cell.getQualifier().toStringUtf8(), cell.getValue().toStringUtf8());
+ * }
+ * }
+ * } catch(ApiException e) {
+ * e.printStackTrace();
+ * }
+ * }
+ *
+ * @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public Row readRow(TargetId targetId, String rowKey) {
+ return ApiExceptions.callAndTranslateApiException(
+ readRowAsync(targetId, ByteString.copyFromUtf8(rowKey), null));
+ }
+
+ /**
+ * Convenience method for synchronously reading a single row on the specified {@link TargetId}. If
+ * the row does not exist, the value will be null.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ *
+ * // Build the filter expression
+ * Filter filter = FILTERS.chain()
+ * .filter(FILTERS.qualifier().regex("prefix.*"))
+ * .filter(FILTERS.limit().cellsPerRow(10));
+ *
+ * Row row = bigtableDataClient.readRow(TableId.of(tableId), "key", filter);
+ * // Do something with row, for example, display all cells
+ * if(row != null) {
+ * System.out.println(row.getKey().toStringUtf8());
+ * for(RowCell cell : row.getCells()) {
+ * System.out.printf("Family: %s Qualifier: %s Value: %s", cell.getFamily(),
+ * cell.getQualifier().toStringUtf8(), cell.getValue().toStringUtf8());
+ * }
+ * }
+ * } catch(ApiException e) {
+ * e.printStackTrace();
+ * }
+ * }
+ *
+ * @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public Row readRow(TargetId targetId, String rowKey, @Nullable Filter filter) {
+ return ApiExceptions.callAndTranslateApiException(
+ readRowAsync(targetId, ByteString.copyFromUtf8(rowKey), filter));
+ }
+
+ /**
+ * Convenience method for synchronously reading a single row on the specified {@link TargetId}. If
+ * the row does not exist, the value will be null.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ *
+ * // Build the filter expression
+ * Filter filter = FILTERS.chain()
+ * .filter(FILTERS.qualifier().regex("prefix.*"))
+ * .filter(FILTERS.limit().cellsPerRow(10));
+ *
+ * Row row = bigtableDataClient.readRow(TableId.of(tableId), ByteString.copyFromUtf8("key"), filter);
+ * // Do something with row, for example, display all cells
+ * if(row != null) {
+ * System.out.println(row.getKey().toStringUtf8());
+ * for(RowCell cell : row.getCells()) {
+ * System.out.printf("Family: %s Qualifier: %s Value: %s", cell.getFamily(),
+ * cell.getQualifier().toStringUtf8(), cell.getValue().toStringUtf8());
+ * }
+ * }
+ * } catch(ApiException e) {
+ * e.printStackTrace();
+ * }
+ * }
+ *
+ * @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public Row readRow(TargetId targetId, ByteString rowKey, @Nullable Filter filter) {
+ return ApiExceptions.callAndTranslateApiException(readRowAsync(targetId, rowKey, filter));
+ }
+
/**
* Convenience method for asynchronously reading a single row. If the row does not exist, the
* future's value will be null.
@@ -473,7 +757,10 @@ public Row readRow(String tableId, ByteString rowKey, @Nullable Filter filter) {
* }, MoreExecutors.directExecutor());
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#readRowAsync(TargetId, String)} instead.
*/
+ @Deprecated
public ApiFuture readRowAsync(String tableId, String rowKey) {
return readRowAsync(tableId, ByteString.copyFromUtf8(rowKey), null);
}
@@ -506,7 +793,10 @@ public ApiFuture readRowAsync(String tableId, String rowKey) {
* }, MoreExecutors.directExecutor());
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#readRowAsync(TargetId, ByteString)} instead.
*/
+ @Deprecated
public ApiFuture readRowAsync(String tableId, ByteString rowKey) {
return readRowAsync(tableId, rowKey, null);
}
@@ -544,7 +834,11 @@ public ApiFuture readRowAsync(String tableId, ByteString rowKey) {
* }, MoreExecutors.directExecutor());
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#readRowAsync(TargetId, String, Filter)}
+ * instead.
*/
+ @Deprecated
public ApiFuture readRowAsync(String tableId, String rowKey, @Nullable Filter filter) {
return readRowAsync(tableId, ByteString.copyFromUtf8(rowKey), filter);
}
@@ -582,9 +876,168 @@ public ApiFuture readRowAsync(String tableId, String rowKey, @Nullable Filt
* }, MoreExecutors.directExecutor());
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#readRowAsync(TargetId, ByteString, Filter)}
+ * instead.
*/
+ @Deprecated
public ApiFuture readRowAsync(String tableId, ByteString rowKey, @Nullable Filter filter) {
- Query query = Query.create(tableId).rowKey(rowKey);
+ return readRowAsync(TableId.of(tableId), rowKey, filter);
+ }
+
+ /**
+ * Convenience method for asynchronously reading a single row on the specified {@link TargetId}.
+ * If the row does not exist, the future's value will be null.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ *
+ * ApiFuture futureResult = bigtableDataClient.readRowAsync(TableId.of(tableId), "key");
+ *
+ * ApiFutures.addCallback(futureResult, new ApiFutureCallback() {
+ * public void onFailure(Throwable t) {
+ * if (t instanceof NotFoundException) {
+ * System.out.println("Tried to read a non-existent table");
+ * } else {
+ * t.printStackTrace();
+ * }
+ * }
+ * public void onSuccess(Row result) {
+ * if (result != null) {
+ * System.out.println("Got row: " + result);
+ * }
+ * }
+ * }, MoreExecutors.directExecutor());
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public ApiFuture readRowAsync(TargetId targetId, String rowKey) {
+ return readRowAsync(targetId, ByteString.copyFromUtf8(rowKey), null);
+ }
+
+ /**
+ * Convenience method for asynchronously reading a single row on the specified {@link TargetId}.
+ * If the row does not exist, the future's value will be null.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ *
+ * ApiFuture futureResult = bigtableDataClient.readRowAsync(TableId.of(tableId), ByteString.copyFromUtf8("key"));
+ *
+ * ApiFutures.addCallback(futureResult, new ApiFutureCallback() {
+ * public void onFailure(Throwable t) {
+ * if (t instanceof NotFoundException) {
+ * System.out.println("Tried to read a non-existent table");
+ * } else {
+ * t.printStackTrace();
+ * }
+ * }
+ * public void onSuccess(Row result) {
+ * if (result != null) {
+ * System.out.println("Got row: " + result);
+ * }
+ * }
+ * }, MoreExecutors.directExecutor());
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public ApiFuture readRowAsync(TargetId targetId, ByteString rowKey) {
+ return readRowAsync(targetId, rowKey, null);
+ }
+
+ /**
+ * Convenience method for asynchronously reading a single row on the specified {@link TargetId}.
+ * If the row does not exist, the future's value will be null.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ *
+ * // Build the filter expression
+ * Filters.Filter filter = FILTERS.chain()
+ * .filter(FILTERS.qualifier().regex("prefix.*"))
+ * .filter(FILTERS.limit().cellsPerRow(10));
+ *
+ * ApiFuture futureResult = bigtableDataClient.readRowAsync(TableId.of(tableId), "key", filter);
+ *
+ * ApiFutures.addCallback(futureResult, new ApiFutureCallback() {
+ * public void onFailure(Throwable t) {
+ * if (t instanceof NotFoundException) {
+ * System.out.println("Tried to read a non-existent table");
+ * } else {
+ * t.printStackTrace();
+ * }
+ * }
+ * public void onSuccess(Row result) {
+ * if (result != null) {
+ * System.out.println("Got row: " + result);
+ * }
+ * }
+ * }, MoreExecutors.directExecutor());
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public ApiFuture readRowAsync(TargetId targetId, String rowKey, @Nullable Filter filter) {
+ return readRowAsync(targetId, ByteString.copyFromUtf8(rowKey), filter);
+ }
+
+ /**
+ * Convenience method for asynchronously reading a single row on the specified {@link TargetId}.
+ * If the row does not exist, the value will be null.
+ *
+ * Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE]";
+ *
+ * // Build the filter expression
+ * Filters.Filter filter = FILTERS.chain()
+ * .filter(FILTERS.qualifier().regex("prefix.*"))
+ * .filter(FILTERS.limit().cellsPerRow(10));
+ *
+ * ApiFuture futureResult = bigtableDataClient.readRowAsync(TableId.of(tableId), ByteString.copyFromUtf8("key"), filter);
+ *
+ * ApiFutures.addCallback(futureResult, new ApiFutureCallback() {
+ * public void onFailure(Throwable t) {
+ * if (t instanceof NotFoundException) {
+ * System.out.println("Tried to read a non-existent table");
+ * } else {
+ * t.printStackTrace();
+ * }
+ * }
+ * public void onSuccess(Row result) {
+ * if (result != null) {
+ * System.out.println("Got row: " + result);
+ * }
+ * }
+ * }, MoreExecutors.directExecutor());
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public ApiFuture readRowAsync(
+ TargetId targetId, ByteString rowKey, @Nullable Filter filter) {
+ Query query = Query.create(targetId).rowKey(rowKey);
if (filter != null) {
query = query.filter(filter);
}
@@ -868,7 +1321,37 @@ public ServerStreamingCallable readRowsCallable(RowAdapter keyOffsets = bigtableDataClient.sampleRowKeys(tableId);
+ * List keyOffsets = bigtableDataClient.sampleRowKeys(tableId);
+ * for(KeyOffset keyOffset : keyOffsets) {
+ * // Do something with keyOffset
+ * }
+ * } catch(ApiException e) {
+ * e.printStackTrace();
+ * }
+ * }
+ *
+ * @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
+ * @deprecated Please use {@link BigtableDataClient#sampleRowKeys(TargetId)} instead.
+ */
+ @Deprecated
+ public List sampleRowKeys(String tableId) {
+ return ApiExceptions.callAndTranslateApiException(sampleRowKeysAsync(tableId));
+ }
+
+ /**
+ * Convenience method to synchronously return a sample of row keys on the specified {@link
+ * TargetId}.
+ *
+ * The returned row keys will delimit contiguous sections of the table of approximately equal
+ * size, which can be used to break up the data for distributed tasks like mapreduces.
+ *
+ *
Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE_ID]";
+ *
+ * List keyOffsets = bigtableDataClient.sampleRowKeys(TableId.of(tableId));
* for(KeyOffset keyOffset : keyOffsets) {
* // Do something with keyOffset
* }
@@ -879,8 +1362,8 @@ public ServerStreamingCallable readRowsCallable(RowAdapter sampleRowKeys(String tableId) {
- return ApiExceptions.callAndTranslateApiException(sampleRowKeysAsync(tableId));
+ public List sampleRowKeys(TargetId targetId) {
+ return ApiExceptions.callAndTranslateApiException(sampleRowKeysAsync(targetId));
}
/**
@@ -908,9 +1391,48 @@ public List sampleRowKeys(String tableId) {
* }, MoreExecutors.directExecutor());
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#sampleRowKeysAsync(TargetId)} instead.
*/
+ @Deprecated
public ApiFuture> sampleRowKeysAsync(String tableId) {
- return sampleRowKeysCallable().futureCall(tableId);
+ return sampleRowKeysAsync(TableId.of(tableId));
+ }
+
+ /**
+ * Convenience method to asynchronously return a sample of row keys on the specified {@link
+ * TargetId}.
+ *
+ * The returned row keys will delimit contiguous sections of the table of approximately equal
+ * size, which can be used to break up the data for distributed tasks like mapreduces.
+ *
+ *
Sample code:
+ *
+ *
{@code
+ * try (BigtableClient bigtableDataClient = BigtableClient.create("[PROJECT]", "[INSTANCE]")) {
+ * String tableId = "[TABLE_ID]";
+ * ApiFuture> keyOffsetsFuture = bigtableClient.sampleRowKeysAsync(TableId.of(tableId));
+ *
+ * ApiFutures.addCallback(keyOffsetsFuture, new ApiFutureCallback>() {
+ * public void onFailure(Throwable t) {
+ * if (t instanceof NotFoundException) {
+ * System.out.println("Tried to sample keys of a non-existent table");
+ * } else {
+ * t.printStackTrace();
+ * }
+ * }
+ * public void onSuccess(List keyOffsets) {
+ * System.out.println("Got key offsets: " + keyOffsets);
+ * }
+ * }, MoreExecutors.directExecutor());
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public ApiFuture> sampleRowKeysAsync(TargetId targetId) {
+ return sampleRowKeysCallableWithRequest().futureCall(SampleRowKeysRequest.create(targetId));
}
/**
@@ -949,11 +1471,62 @@ public ApiFuture> sampleRowKeysAsync(String tableId) {
* }, MoreExecutors.directExecutor());
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#sampleRowKeysCallableWithRequest()} instead.
*/
+ @Deprecated
public UnaryCallable> sampleRowKeysCallable() {
return stub.sampleRowKeysCallable();
}
+ /**
+ * Returns a sample of row keys in the table. This callable allows takes a {@link
+ * SampleRowKeysRequest} object rather than a String input, and thus can be used to sample against
+ * a specified {@link TargetId}.
+ *
+ * The returned row keys will delimit contiguous sections of the table of approximately equal
+ * size, which can be used to break up the data for distributed tasks like mapreduces. The
+ * returned callable object allows for customization of api invocation.
+ *
+ *
Sample code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * SampleRowKeysRequest sampleRowKeys = SampleRowKeysRequest.create(TableId.of("[TABLE]"));
+ * // Synchronous invocation
+ * try {
+ * List keyOffsets = bigtableDataClient.sampleRowKeysCallableWithRequest().call(sampleRowKeys);
+ * } catch (NotFoundException e) {
+ * System.out.println("Tried to sample keys of a non-existent table");
+ * } catch (RuntimeException e) {
+ * e.printStackTrace();
+ * }
+ *
+ * // Asynchronous invocation
+ * ApiFuture> keyOffsetsFuture = bigtableDataClient.sampleRowKeysCallableWithRequest().futureCall(sampleRowKeys);
+ *
+ * ApiFutures.addCallback(keyOffsetsFuture, new ApiFutureCallback>() {
+ * public void onFailure(Throwable t) {
+ * if (t instanceof NotFoundException) {
+ * System.out.println("Tried to sample keys of a non-existent table");
+ * } else {
+ * t.printStackTrace();
+ * }
+ * }
+ * public void onSuccess(List keyOffsets) {
+ * System.out.println("Got key offsets: " + keyOffsets);
+ * }
+ * }, MoreExecutors.directExecutor());
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public UnaryCallable> sampleRowKeysCallableWithRequest() {
+ return stub.sampleRowKeysCallableWithRequest();
+ }
+
/**
* Convenience method to synchronously mutate a single row atomically. Cells already present in
* the row are left unchanged unless explicitly changed by the {@link RowMutation}.
@@ -1087,8 +1660,10 @@ public void bulkMutateRows(BulkMutation mutation) {
* // Before `batcher` is closed, all remaining(If any) mutations are applied.
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#newBulkMutationBatcher(TargetId)} instead.
*/
- @BetaApi("This surface is likely to change as the batching surface evolves.")
+ @Deprecated
public Batcher newBulkMutationBatcher(@Nonnull String tableId) {
return newBulkMutationBatcher(tableId, null);
}
@@ -1119,13 +1694,89 @@ public Batcher newBulkMutationBatcher(@Nonnull String ta
* // Before `batcher` is closed, all remaining(If any) mutations are applied.
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#newBulkMutationBatcher(TargetId,
+ * GrpcCallContext)} instead.
*/
+ @Deprecated
@BetaApi("This surface is likely to change as the batching surface evolves.")
public Batcher newBulkMutationBatcher(
@Nonnull String tableId, @Nullable GrpcCallContext ctx) {
return stub.newMutateRowsBatcher(tableId, ctx);
}
+ /**
+ * Mutates multiple rows in a batch on the specified {@link TargetId}.
+ *
+ * Each individual row is mutated atomically as in MutateRow, but the entire batch is not
+ * executed atomically. The returned Batcher instance is not threadsafe, it can only be used from
+ * single thread.
+ *
+ *
Sample Code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * try (Batcher batcher = bigtableDataClient.newBulkMutationBatcher(TableId.of("[TABLE]"))) {
+ * for (String someValue : someCollection) {
+ * ApiFuture entryFuture =
+ * batcher.add(
+ * RowMutationEntry.create("[ROW KEY]")
+ * .setCell("[FAMILY NAME]", "[QUALIFIER]", "[VALUE]"));
+ * }
+ *
+ * // Blocks until mutations are applied on all submitted row entries.
+ * batcher.flush();
+ * }
+ * // Before `batcher` is closed, all remaining(If any) mutations are applied.
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ @BetaApi("This surface is likely to change as the batching surface evolves.")
+ public Batcher newBulkMutationBatcher(TargetId targetId) {
+ return newBulkMutationBatcher(targetId, null);
+ }
+
+ /**
+ * Mutates multiple rows in a batch on the specified {@link TargetId}.
+ *
+ * Each individual row is mutated atomically as in MutateRow, but the entire batch is not
+ * executed atomically. The returned Batcher instance is not threadsafe, it can only be used from
+ * single thread. This method allows customization of the underlying RPCs by passing in a {@link
+ * com.google.api.gax.grpc.GrpcCallContext}. The same context will be reused for all batches. This
+ * can be used to customize things like per attempt timeouts.
+ *
+ *
Sample Code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ * GrpcCallContext ctx = GrpcCallContext.createDefault().withTimeout(Duration.ofSeconds(10));
+ * try (Batcher batcher = bigtableDataClient.newBulkMutationBatcher(TableId.of("[TABLE]"), ctx)) {
+ * for (String someValue : someCollection) {
+ * ApiFuture entryFuture =
+ * batcher.add(
+ * RowMutationEntry.create("[ROW KEY]")
+ * .setCell("[FAMILY NAME]", "[QUALIFIER]", "[VALUE]"));
+ * }
+ *
+ * // Blocks until mutations are applied on all submitted row entries.
+ * batcher.flush();
+ * }
+ * // Before `batcher` is closed, all remaining(If any) mutations are applied.
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ @BetaApi("This surface is likely to change as the batching surface evolves.")
+ public Batcher newBulkMutationBatcher(
+ TargetId targetId, @Nullable GrpcCallContext ctx) {
+ return stub.newMutateRowsBatcher(targetId, ctx);
+ }
+
/**
* Reads rows for given tableId in a batch. If the row does not exist, the value will be null. The
* returned Batcher instance is not threadsafe, it can only be used from a single thread.
@@ -1160,7 +1811,10 @@ public Batcher newBulkMutationBatcher(
* List actualRows = ApiFutures.allAsList(rows).get();
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#newBulkReadRowsBatcher(TargetId)} instead.
*/
+ @Deprecated
public Batcher newBulkReadRowsBatcher(String tableId) {
return newBulkReadRowsBatcher(tableId, null);
}
@@ -1206,7 +1860,11 @@ public Batcher newBulkReadRowsBatcher(String tableId) {
* List actualRows = ApiFutures.allAsList(rows).get();
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#newBulkReadRowsBatcher(TargetId, Filter)}
+ * instead.
*/
+ @Deprecated
public Batcher newBulkReadRowsBatcher(
String tableId, @Nullable Filters.Filter filter) {
return newBulkReadRowsBatcher(tableId, filter, null);
@@ -1256,12 +1914,167 @@ public Batcher newBulkReadRowsBatcher(
* List actualRows = ApiFutures.allAsList(rows).get();
* }
* }
+ *
+ * @deprecated Please use {@link BigtableDataClient#newBulkReadRowsBatcher(TargetId, Filter,
+ * GrpcCallContext)} instead.
*/
+ @Deprecated
public Batcher newBulkReadRowsBatcher(
String tableId, @Nullable Filters.Filter filter, @Nullable GrpcCallContext ctx) {
- Query query = Query.create(tableId);
+ return newBulkReadRowsBatcher(TableId.of(tableId), filter, ctx);
+ }
+
+ /**
+ * Reads rows for given tableId in a batch on the specified {@link TargetId}.
+ *
+ * If the row does not exist, the value will be null. The returned Batcher instance is not
+ * threadsafe, it can only be used from a single thread.
+ *
+ *
Performance notice: The ReadRows protocol requires that rows are sent in ascending key
+ * order, which means that the keys are processed sequentially on the server-side, so batching
+ * allows improving throughput but not latency. Lower latencies can be achieved by sending smaller
+ * requests concurrently.
+ *
+ *
Sample Code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ *
+ * List> rows = new ArrayList<>();
+ *
+ * try (Batcher batcher = bigtableDataClient.newBulkReadRowsBatcher(TableId.of("[TABLE]"))) {
+ * for (String someValue : someCollection) {
+ * ApiFuture rowFuture =
+ * batcher.add(ByteString.copyFromUtf8("[ROW KEY]"));
+ * rows.add(rowFuture);
+ * }
+ *
+ * // [Optional] Sends collected elements for batching asynchronously.
+ * batcher.sendOutstanding();
+ *
+ * // [Optional] Invokes sendOutstanding() and awaits until all pending entries are resolved.
+ * batcher.flush();
+ * }
+ * // batcher.close() invokes `flush()` which will in turn invoke `sendOutstanding()` with await for
+ * pending batches until its resolved.
+ *
+ * List actualRows = ApiFutures.allAsList(rows).get();
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public Batcher newBulkReadRowsBatcher(TargetId targetId) {
+ return newBulkReadRowsBatcher(targetId, null);
+ }
+
+ /**
+ * Reads rows for given tableId and filter criteria in a batch on the specified {@link TargetId}.
+ *
+ * If the row does not exist, the value will be null. The returned Batcher instance is not
+ * threadsafe, it can only be used from a single thread.
+ *
+ *
Performance notice: The ReadRows protocol requires that rows are sent in ascending key
+ * order, which means that the keys are processed sequentially on the server-side, so batching
+ * allows improving throughput but not latency. Lower latencies can be achieved by sending smaller
+ * requests concurrently.
+ *
+ *
Sample Code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ *
+ * // Build the filter expression
+ * Filter filter = FILTERS.chain()
+ * .filter(FILTERS.key().regex("prefix.*"))
+ * .filter(FILTERS.limit().cellsPerRow(10));
+ *
+ * List> rows = new ArrayList<>();
+ *
+ * try (Batcher batcher = bigtableDataClient.newBulkReadRowsBatcher(TableId.of("[TABLE]"), filter)) {
+ * for (String someValue : someCollection) {
+ * ApiFuture rowFuture =
+ * batcher.add(ByteString.copyFromUtf8("[ROW KEY]"));
+ * rows.add(rowFuture);
+ * }
+ *
+ * // [Optional] Sends collected elements for batching asynchronously.
+ * batcher.sendOutstanding();
+ *
+ * // [Optional] Invokes sendOutstanding() and awaits until all pending entries are resolved.
+ * batcher.flush();
+ * }
+ * // batcher.close() invokes `flush()` which will in turn invoke `sendOutstanding()` with await for
+ * pending batches until its resolved.
+ *
+ * List actualRows = ApiFutures.allAsList(rows).get();
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public Batcher newBulkReadRowsBatcher(
+ TargetId targetId, @Nullable Filter filter) {
+ return newBulkReadRowsBatcher(targetId, filter, null);
+ }
+
+ /**
+ * Reads rows for given tableId and filter criteria in a batch on the specified {@link TargetId}.
+ *
+ * If the row does not exist, the value will be null. The returned Batcher instance is not
+ * threadsafe, it can only be used from a single thread. This method allows customization of the
+ * underlying RPCs by passing in a {@link com.google.api.gax.grpc.GrpcCallContext}. The same
+ * context will be reused for all batches. This can be used to customize things like per attempt
+ * timeouts.
+ *
+ *
Performance notice: The ReadRows protocol requires that rows are sent in ascending key
+ * order, which means that the keys are processed sequentially on the server-side, so batching
+ * allows improving throughput but not latency. Lower latencies can be achieved by sending smaller
+ * requests concurrently.
+ *
+ *
Sample Code:
+ *
+ *
{@code
+ * try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
+ *
+ * // Build the filter expression
+ * Filter filter = FILTERS.chain()
+ * .filter(FILTERS.key().regex("prefix.*"))
+ * .filter(FILTERS.limit().cellsPerRow(10));
+ *
+ * List> rows = new ArrayList<>();
+ *
+ * try (Batcher batcher = bigtableDataClient.newBulkReadRowsBatcher(
+ * TableId.of("[TABLE]"), filter, GrpcCallContext.createDefault().withTimeout(Duration.ofSeconds(10)))) {
+ * for (String someValue : someCollection) {
+ * ApiFuture rowFuture =
+ * batcher.add(ByteString.copyFromUtf8("[ROW KEY]"));
+ * rows.add(rowFuture);
+ * }
+ *
+ * // [Optional] Sends collected elements for batching asynchronously.
+ * batcher.sendOutstanding();
+ *
+ * // [Optional] Invokes sendOutstanding() and awaits until all pending entries are resolved.
+ * batcher.flush();
+ * }
+ * // batcher.close() invokes `flush()` which will in turn invoke `sendOutstanding()` with await for
+ * pending batches until its resolved.
+ *
+ * List actualRows = ApiFutures.allAsList(rows).get();
+ * }
+ * }
+ *
+ * @see com.google.cloud.bigtable.data.v2.models.AuthorizedViewId
+ * @see TableId
+ */
+ public Batcher newBulkReadRowsBatcher(
+ TargetId targetId, @Nullable Filter filter, @Nullable GrpcCallContext ctx) {
+ Query query = Query.create(targetId);
if (filter != null) {
- query.filter(filter);
+ query = query.filter(filter);
}
return stub.newBulkReadRowsBatcher(query, ctx);
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/NameUtil.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/NameUtil.java
index 4744d3ef1e..68c66067b1 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/NameUtil.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/internal/NameUtil.java
@@ -16,6 +16,9 @@
package com.google.cloud.bigtable.data.v2.internal;
import com.google.api.core.InternalApi;
+import com.google.cloud.bigtable.data.v2.models.AuthorizedViewId;
+import com.google.cloud.bigtable.data.v2.models.TableId;
+import com.google.cloud.bigtable.data.v2.models.TargetId;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
@@ -30,6 +33,8 @@
public class NameUtil {
private static final Pattern TABLE_PATTERN =
Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)");
+ private static final Pattern AUTHORIZED_VIEW_PATTERN =
+ Pattern.compile("projects/([^/]+)/instances/([^/]+)/tables/([^/]+)/authorizedViews/([^/]+)");
public static String formatInstanceName(@Nonnull String projectId, @Nonnull String instanceId) {
return "projects/" + projectId + "/instances/" + instanceId;
@@ -40,6 +45,14 @@ public static String formatTableName(
return formatInstanceName(projectId, instanceId) + "/tables/" + tableId;
}
+ public static String formatAuthorizedViewName(
+ @Nonnull String projectId,
+ @Nonnull String instanceId,
+ @Nonnull String tableId,
+ @Nonnull String authorizedViewId) {
+ return formatTableName(projectId, instanceId, tableId) + "/authorizedViews/" + authorizedViewId;
+ }
+
public static String extractTableIdFromTableName(@Nonnull String fullTableName) {
Matcher matcher = TABLE_PATTERN.matcher(fullTableName);
if (!matcher.matches()) {
@@ -47,4 +60,59 @@ public static String extractTableIdFromTableName(@Nonnull String fullTableName)
}
return matcher.group(3);
}
+
+ public static String extractTableIdFromAuthorizedViewName(
+ @Nonnull String fullAuthorizedViewName) {
+ Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
+ }
+ return matcher.group(3);
+ }
+
+ public static String extractTableNameFromAuthorizedViewName(
+ @Nonnull String fullAuthorizedViewName) {
+ Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
+ }
+ return formatTableName(matcher.group(1), matcher.group(2), matcher.group(3));
+ }
+
+ public static String extractAuthorizedViewIdFromAuthorizedViewName(
+ @Nonnull String fullAuthorizedViewName) {
+ Matcher matcher = AUTHORIZED_VIEW_PATTERN.matcher(fullAuthorizedViewName);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("Invalid authorized view name: " + fullAuthorizedViewName);
+ }
+ return matcher.group(4);
+ }
+
+ /** A helper to convert fully qualified tableName and authorizedViewName to a {@link TargetId} */
+ public static TargetId extractTargetId(
+ @Nonnull String tableName, @Nonnull String authorizedViewName) {
+ if (tableName.isEmpty() && authorizedViewName.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Either table name or authorized view name must be specified. Table name: "
+ + tableName
+ + ", authorized view name: "
+ + authorizedViewName);
+ }
+ if (!tableName.isEmpty() && !authorizedViewName.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Table name and authorized view name cannot be specified at the same time. Table name: "
+ + tableName
+ + ", authorized view name: "
+ + authorizedViewName);
+ }
+
+ if (!tableName.isEmpty()) {
+ String tableId = extractTableIdFromTableName(tableName);
+ return TableId.of(tableId);
+ } else {
+ String tableId = extractTableIdFromAuthorizedViewName(authorizedViewName);
+ String authorizedViewId = extractAuthorizedViewIdFromAuthorizedViewName(authorizedViewName);
+ return AuthorizedViewId.of(tableId, authorizedViewId);
+ }
+ }
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/AuthorizedViewId.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/AuthorizedViewId.java
new file mode 100644
index 0000000000..5f64190b5c
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/AuthorizedViewId.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.data.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.auto.value.AutoValue;
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
+import com.google.common.base.Preconditions;
+
+/**
+ * An implementation of a {@link TargetId} for authorized views.
+ *
+ * See {@link com.google.cloud.bigtable.admin.v2.models.AuthorizedView} for more details about an
+ * authorized view.
+ */
+@AutoValue
+public abstract class AuthorizedViewId implements TargetId {
+ /** Constructs a new AuthorizedViewId object from the specified tableId and authorizedViewId. */
+ public static AuthorizedViewId of(String tableId, String authorizedViewId) {
+ Preconditions.checkNotNull(tableId, "table id can't be null.");
+ Preconditions.checkNotNull(authorizedViewId, "authorized view id can't be null.");
+ return new AutoValue_AuthorizedViewId(tableId, authorizedViewId);
+ }
+
+ abstract String getTableId();
+
+ abstract String getAuthorizedViewId();
+
+ @Override
+ @InternalApi
+ public String toResourceName(String projectId, String instanceId) {
+ return NameUtil.formatAuthorizedViewName(
+ projectId, instanceId, getTableId(), getAuthorizedViewId());
+ }
+
+ @Override
+ @InternalApi
+ public boolean scopedForAuthorizedView() {
+ return true;
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/BulkMutation.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/BulkMutation.java
index a269370748..f6a09d0b6d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/BulkMutation.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/BulkMutation.java
@@ -38,20 +38,31 @@
*/
public final class BulkMutation implements Serializable, Cloneable {
private static final long serialVersionUID = 3522061250439399088L;
-
- private final String tableId;
+ private final TargetId targetId;
private transient MutateRowsRequest.Builder builder;
private long mutationCountSum = 0;
+ /** @deprecated Please use {@link BulkMutation#create(TargetId)} instead. */
+ @Deprecated
public static BulkMutation create(String tableId) {
- return new BulkMutation(tableId);
+ return new BulkMutation(TableId.of(tableId));
}
- private BulkMutation(@Nonnull String tableId) {
- Preconditions.checkNotNull(tableId);
+ /**
+ * Creates a new instance of the bulk mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static BulkMutation create(TargetId targetId) {
+ return new BulkMutation(targetId);
+ }
+
+ private BulkMutation(TargetId targetId) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
- this.tableId = tableId;
+ this.targetId = targetId;
this.builder = MutateRowsRequest.newBuilder();
}
@@ -117,14 +128,15 @@ public int getEntryCount() {
@InternalApi
public MutateRowsRequest toProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
-
- return builder
- .setTableName(tableName)
- .setAppProfileId(requestContext.getAppProfileId())
- .build();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
}
/**
@@ -140,8 +152,11 @@ public MutateRowsRequest toProto(RequestContext requestContext) {
*/
@BetaApi
public static BulkMutation fromProto(@Nonnull MutateRowsRequest request) {
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
+
BulkMutation bulkMutation =
- BulkMutation.create(NameUtil.extractTableIdFromTableName(request.getTableName()));
+ BulkMutation.create(NameUtil.extractTargetId(tableName, authorizedViewName));
bulkMutation.builder = request.toBuilder();
return bulkMutation;
@@ -150,7 +165,7 @@ public static BulkMutation fromProto(@Nonnull MutateRowsRequest request) {
/** Creates a copy of {@link BulkMutation}. */
@Override
public BulkMutation clone() {
- BulkMutation bulkMutation = BulkMutation.create(tableId);
+ BulkMutation bulkMutation = BulkMutation.create(targetId);
bulkMutation.builder = this.builder.clone();
return bulkMutation;
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutation.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutation.java
index ac4c548942..14841f9f4d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutation.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ConditionalRowMutation.java
@@ -33,25 +33,49 @@
public final class ConditionalRowMutation implements Serializable {
private static final long serialVersionUID = -3699904745621909502L;
- private final String tableId;
+ private final TargetId targetId;
private transient CheckAndMutateRowRequest.Builder builder =
CheckAndMutateRowRequest.newBuilder();
- private ConditionalRowMutation(String tableId, ByteString rowKey) {
- this.tableId = tableId;
+ private ConditionalRowMutation(TargetId targetId, ByteString rowKey) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+
+ this.targetId = targetId;
builder.setRowKey(rowKey);
}
- /** Creates a new instance of the mutation builder. */
+ /** @deprecated Please use {@link ConditionalRowMutation#create(TargetId, String)} instead. */
+ @Deprecated
public static ConditionalRowMutation create(String tableId, String rowKey) {
return create(tableId, ByteString.copyFromUtf8(rowKey));
}
- /** Creates a new instance of the mutation builder. */
+ /**
+ * Creates a new instance of the mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static ConditionalRowMutation create(TargetId targetId, String rowKey) {
+ return create(targetId, ByteString.copyFromUtf8(rowKey));
+ }
+
+ /** @deprecated Please use {@link ConditionalRowMutation#create(TargetId, ByteString)} instead. */
+ @Deprecated
public static ConditionalRowMutation create(String tableId, ByteString rowKey) {
Validations.validateTableId(tableId);
- return new ConditionalRowMutation(tableId, rowKey);
+ return new ConditionalRowMutation(TableId.of(tableId), rowKey);
+ }
+
+ /**
+ * Creates a new instance of the mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static ConditionalRowMutation create(TargetId targetId, ByteString rowKey) {
+ return new ConditionalRowMutation(targetId, rowKey);
}
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
@@ -80,7 +104,8 @@ public ConditionalRowMutation condition(@Nonnull Filter condition) {
Preconditions.checkNotNull(condition);
Preconditions.checkState(
!builder.hasPredicateFilter(),
- "Can only have a single condition, please use a Filters#chain or Filters#interleave filter instead");
+ "Can only have a single condition, please use a Filters#chain or Filters#interleave filter"
+ + " instead");
// TODO: verify that the condition does not use any FILTERS.condition() filters
builder.setPredicateFilter(condition.toProto());
@@ -129,13 +154,16 @@ public CheckAndMutateRowRequest toProto(RequestContext requestContext) {
Preconditions.checkState(
!builder.getTrueMutationsList().isEmpty() || !builder.getFalseMutationsList().isEmpty(),
"ConditionalRowMutations must have `then` or `otherwise` mutations.");
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
- return builder
- .setTableName(tableName.toString())
- .setAppProfileId(requestContext.getAppProfileId())
- .build();
+
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
}
/**
@@ -146,9 +174,12 @@ public CheckAndMutateRowRequest toProto(RequestContext requestContext) {
*/
@BetaApi
public static ConditionalRowMutation fromProto(@Nonnull CheckAndMutateRowRequest request) {
- String tableId = NameUtil.extractTableIdFromTableName(request.getTableName());
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
+
ConditionalRowMutation rowMutation =
- ConditionalRowMutation.create(tableId, request.getRowKey());
+ ConditionalRowMutation.create(
+ NameUtil.extractTargetId(tableName, authorizedViewName), request.getRowKey());
rowMutation.builder = request.toBuilder();
return rowMutation;
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/MutateRowsException.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/MutateRowsException.java
index 4ae0606ab9..3eb6c6c7dd 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/MutateRowsException.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/MutateRowsException.java
@@ -33,20 +33,6 @@
* were part of that RPC.
*/
public final class MutateRowsException extends ApiException {
- // Synthetic status to use for this ApiException subclass.
- private static final StatusCode LOCAL_STATUS =
- new StatusCode() {
- @Override
- public Code getCode() {
- return Code.INTERNAL;
- }
-
- @Override
- public Object getTransportCode() {
- return null;
- }
- };
-
private final List failedMutations;
/**
@@ -56,6 +42,7 @@ public Object getTransportCode() {
@InternalApi
public static MutateRowsException create(
@Nullable Throwable rpcError,
+ StatusCode status,
@Nonnull List failedMutations,
boolean retryable) {
ErrorDetails errorDetails = null;
@@ -63,15 +50,16 @@ public static MutateRowsException create(
errorDetails = ((ApiException) rpcError).getErrorDetails();
}
- return new MutateRowsException(rpcError, failedMutations, retryable, errorDetails);
+ return new MutateRowsException(rpcError, status, failedMutations, retryable, errorDetails);
}
private MutateRowsException(
@Nullable Throwable rpcError,
+ StatusCode status,
@Nonnull List failedMutations,
boolean retryable,
@Nullable ErrorDetails errorDetails) {
- super(rpcError, LOCAL_STATUS, retryable, errorDetails);
+ super(rpcError, status, retryable, errorDetails);
Preconditions.checkNotNull(failedMutations);
Preconditions.checkArgument(!failedMutations.isEmpty(), "failedMutations can't be empty");
this.failedMutations = failedMutations;
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Query.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Query.java
index c7e69d70d4..1b4cb8d680 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Query.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/Query.java
@@ -47,20 +47,31 @@ public final class Query implements Serializable {
// bigtable can server the largest filter size of 20KB.
private static final int MAX_FILTER_SIZE = 20 * 1024;
- private final String tableId;
+ private final TargetId targetId;
private transient ReadRowsRequest.Builder builder = ReadRowsRequest.newBuilder();
+ /** @deprecated Please use {@link Query#create(TargetId)} instead. */
+ @Deprecated
+ public static Query create(String tableId) {
+ return new Query(TableId.of(tableId));
+ }
+
/**
- * Constructs a new Query object for the specified table id. The table id will be combined with
- * the instance name specified in the {@link
+ * Constructs a new Query object for the given target with targetId. The target id will be
+ * combined with the instance name specified in the {@link
* com.google.cloud.bigtable.data.v2.BigtableDataSettings}.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
*/
- public static Query create(String tableId) {
- return new Query(tableId);
+ public static Query create(TargetId targetId) {
+ return new Query(targetId);
}
- private Query(String tableId) {
- this.tableId = tableId;
+ private Query(TargetId targetId) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+
+ this.targetId = targetId;
}
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
@@ -260,7 +271,8 @@ public List shard(SortedSet splitPoints) {
List shards = Lists.newArrayListWithCapacity(shardedRowSets.size());
for (RowSet rowSet : shardedRowSets) {
- Query queryShard = new Query(tableId);
+ Query queryShard;
+ queryShard = new Query(targetId);
queryShard.builder.mergeFrom(this.builder.build());
queryShard.builder.setRows(rowSet);
shards.add(queryShard);
@@ -303,14 +315,14 @@ public ByteStringRange getBound() {
*/
@InternalApi
public ReadRowsRequest toProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
-
- return builder
- .setTableName(tableName)
- .setAppProfileId(requestContext.getAppProfileId())
- .build();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
}
/**
@@ -321,15 +333,17 @@ public ReadRowsRequest toProto(RequestContext requestContext) {
*/
public static Query fromProto(@Nonnull ReadRowsRequest request) {
Preconditions.checkArgument(request != null, "ReadRowsRequest must not be null");
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
- Query query = new Query(NameUtil.extractTableIdFromTableName(request.getTableName()));
+ Query query = new Query(NameUtil.extractTargetId(tableName, authorizedViewName));
query.builder = request.toBuilder();
return query;
}
public Query clone() {
- Query query = Query.create(tableId);
+ Query query = Query.create(targetId);
query.builder = this.builder.clone();
return query;
}
@@ -424,7 +438,7 @@ public boolean equals(Object o) {
return false;
}
Query query = (Query) o;
- return Objects.equal(tableId, query.tableId)
+ return Objects.equal(targetId, query.targetId)
&& Objects.equal(builder.getRows(), query.builder.getRows())
&& Objects.equal(builder.getFilter(), query.builder.getFilter())
&& Objects.equal(builder.getRowsLimit(), query.builder.getRowsLimit());
@@ -433,7 +447,7 @@ public boolean equals(Object o) {
@Override
public int hashCode() {
return Objects.hashCode(
- tableId, builder.getRows(), builder.getFilter(), builder.getRowsLimit());
+ targetId, builder.getRows(), builder.getFilter(), builder.getRowsLimit());
}
@Override
@@ -441,7 +455,7 @@ public String toString() {
ReadRowsRequest request = builder.build();
return MoreObjects.toStringHelper(this)
- .add("tableId", tableId)
+ .add("targetId", targetId)
.add("keys", request.getRows().getRowKeysList())
.add("ranges", request.getRows().getRowRangesList())
.add("filter", request.getFilter())
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRow.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRow.java
index 5fa483d1bd..554a0268b9 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRow.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/ReadModifyWriteRow.java
@@ -33,25 +33,49 @@
public final class ReadModifyWriteRow implements Serializable {
private static final long serialVersionUID = -8150045424541029193L;
- private final String tableId;
+ private final TargetId targetId;
private transient ReadModifyWriteRowRequest.Builder builder =
ReadModifyWriteRowRequest.newBuilder();
- private ReadModifyWriteRow(@Nonnull String tableId, @Nonnull ByteString key) {
- Preconditions.checkNotNull(tableId, "tableId can't be null.");
+ private ReadModifyWriteRow(TargetId targetId, ByteString key) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
Preconditions.checkNotNull(key, "key can't be null.");
- this.tableId = tableId;
+ this.targetId = targetId;
builder.setRowKey(key);
}
- public static ReadModifyWriteRow create(@Nonnull String tableId, @Nonnull String key) {
+ /** @deprecated Please use {@link ReadModifyWriteRow#create(TargetId, String)} instead. */
+ @Deprecated
+ public static ReadModifyWriteRow create(String tableId, String key) {
Preconditions.checkNotNull(key, "key can't be null.");
- return new ReadModifyWriteRow(tableId, ByteString.copyFromUtf8(key));
+ return new ReadModifyWriteRow(TableId.of(tableId), ByteString.copyFromUtf8(key));
}
- public static ReadModifyWriteRow create(@Nonnull String tableId, @Nonnull ByteString key) {
- return new ReadModifyWriteRow(tableId, key);
+ /**
+ * Creates a new instance of the ReadModifyWriteRow for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static ReadModifyWriteRow create(TargetId targetId, String key) {
+ return new ReadModifyWriteRow(targetId, ByteString.copyFromUtf8(key));
+ }
+
+ /** @deprecated Please use {@link ReadModifyWriteRow#create(TargetId, ByteString)} instead. */
+ @Deprecated
+ public static ReadModifyWriteRow create(String tableId, ByteString key) {
+ return new ReadModifyWriteRow(TableId.of(tableId), key);
+ }
+
+ /**
+ * Creates a new instance of the ReadModifyWriteRow for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static ReadModifyWriteRow create(TargetId targetId, ByteString key) {
+ return new ReadModifyWriteRow(targetId, key);
}
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
@@ -129,14 +153,14 @@ public ReadModifyWriteRow increment(
@InternalApi
public ReadModifyWriteRowRequest toProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
-
- return builder
- .setTableName(tableName)
- .setAppProfileId(requestContext.getAppProfileId())
- .build();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
}
/**
@@ -147,9 +171,12 @@ public ReadModifyWriteRowRequest toProto(RequestContext requestContext) {
*/
@BetaApi
public static ReadModifyWriteRow fromProto(@Nonnull ReadModifyWriteRowRequest request) {
- String tableId = NameUtil.extractTableIdFromTableName(request.getTableName());
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
- ReadModifyWriteRow row = ReadModifyWriteRow.create(tableId, request.getRowKey());
+ ReadModifyWriteRow row =
+ ReadModifyWriteRow.create(
+ NameUtil.extractTargetId(tableName, authorizedViewName), request.getRowKey());
row.builder = request.toBuilder();
return row;
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/RowMutation.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/RowMutation.java
index 940b76702c..4dfe751225 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/RowMutation.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/RowMutation.java
@@ -23,6 +23,7 @@
import com.google.cloud.bigtable.data.v2.internal.NameUtil;
import com.google.cloud.bigtable.data.v2.internal.RequestContext;
import com.google.cloud.bigtable.data.v2.models.Range.TimestampRange;
+import com.google.common.base.Preconditions;
import com.google.protobuf.ByteString;
import java.io.Serializable;
import javax.annotation.Nonnull;
@@ -34,60 +35,102 @@
public final class RowMutation implements MutationApi, Serializable {
private static final long serialVersionUID = 6529002234913236318L;
- private final String tableId;
+ private final TargetId targetId;
private final ByteString key;
private final Mutation mutation;
- private RowMutation(String tableId, ByteString key, Mutation mutation) {
- this.tableId = tableId;
+ private RowMutation(TargetId targetId, ByteString key, Mutation mutation) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+
+ this.targetId = targetId;
this.key = key;
this.mutation = mutation;
}
- /** Creates a new instance of the mutation builder. */
- public static RowMutation create(@Nonnull String tableId, @Nonnull String key) {
+ /** @deprecated Please use {@link RowMutation#create(TargetId, String)} instead. */
+ @Deprecated
+ public static RowMutation create(String tableId, String key) {
return create(tableId, ByteString.copyFromUtf8(key));
}
- /** Creates a new instance of the mutation builder. */
- public static RowMutation create(@Nonnull String tableId, @Nonnull ByteString key) {
- return new RowMutation(tableId, key, Mutation.create());
+ /**
+ * Creates a new instance of the mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static RowMutation create(TargetId targetId, String key) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+ return create(targetId, ByteString.copyFromUtf8(key));
+ }
+
+ /** @deprecated Please use {@link RowMutation#create(TargetId, ByteString)} instead. */
+ @Deprecated
+ public static RowMutation create(String tableId, ByteString key) {
+ return new RowMutation(TableId.of(tableId), key, Mutation.create());
}
/**
- * Creates new instance of mutation builder by wrapping existing set of row mutations. The builder
- * will be owned by this RowMutation and should not be used by the caller after this call. This
- * functionality is intended for advanced usage.
+ * Creates a new instance of the mutation builder for the given target with targetId.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+ public static RowMutation create(TargetId targetId, ByteString key) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+ return new RowMutation(targetId, key, Mutation.create());
+ }
+
+ /** @deprecated Please use {@link RowMutation#create(TargetId, String, Mutation)} instead. */
+ @Deprecated
+ public static RowMutation create(String tableId, String key, Mutation mutation) {
+ return create(tableId, ByteString.copyFromUtf8(key), mutation);
+ }
+
+ /**
+ * Creates new instance of mutation builder for the given target with targetId by wrapping
+ * existing set of row mutations. The builder will be owned by this RowMutation and should not be
+ * used by the caller after this call. This functionality is intended for advanced usage.
*
* Sample code:
*
*
* Mutation mutation = Mutation.create()
* .setCell("[FAMILY_NAME]", "[QUALIFIER]", [TIMESTAMP], "[VALUE]");
- * RowMutation rowMutation = RowMutation.create("[TABLE]", "[ROW_KEY]", mutation);
+ * RowMutation rowMutation = RowMutation.create(TableId.of("[TABLE]"), "[ROW_KEY]", mutation);
*
+ *
+ * @see AuthorizedViewId
+ * @see TableId
*/
- public static RowMutation create(
- @Nonnull String tableId, @Nonnull String key, @Nonnull Mutation mutation) {
- return create(tableId, ByteString.copyFromUtf8(key), mutation);
+ public static RowMutation create(TargetId targetId, String key, Mutation mutation) {
+ return create(targetId, ByteString.copyFromUtf8(key), mutation);
+ }
+
+ /** @deprecated Please use {@link RowMutation#create(TargetId, ByteString, Mutation)} instead. */
+ @Deprecated
+ public static RowMutation create(String tableId, ByteString key, Mutation mutation) {
+ return new RowMutation(TableId.of(tableId), key, mutation);
}
/**
- * Creates new instance of mutation builder by wrapping existing set of row mutations. The builder
- * will be owned by this RowMutation and should not be used by the caller after this call. This
- * functionality is intended for advanced usage.
+ * Creates new instance of mutation builder for the given target with targetId by wrapping
+ * existing set of row mutations. The builder will be owned by this RowMutation and should not be
+ * used by the caller after this call. This functionality is intended for advanced usage.
*
* Sample code:
*
*
* Mutation mutation = Mutation.create()
* .setCell("[FAMILY_NAME]", "[QUALIFIER]", [TIMESTAMP], "[VALUE]");
- * RowMutation rowMutation = RowMutation.create("[TABLE]", [BYTE_STRING_ROW_KEY], mutation);
+ * RowMutation rowMutation = RowMutation.create(TableId.of("[TABLE]"), [BYTE_STRING_ROW_KEY], mutation);
*
+ *
+ * @see AuthorizedViewId
+ * @see TableId
*/
- public static RowMutation create(
- @Nonnull String tableId, @Nonnull ByteString key, @Nonnull Mutation mutation) {
- return new RowMutation(tableId, key, mutation);
+ public static RowMutation create(TargetId targetId, ByteString key, Mutation mutation) {
+ return new RowMutation(targetId, key, mutation);
}
@Override
@@ -196,13 +239,17 @@ public RowMutation addToCell(
@InternalApi
public MutateRowRequest toProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
+ MutateRowRequest.Builder builder = MutateRowRequest.newBuilder();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
- return MutateRowRequest.newBuilder()
+ return builder
.setAppProfileId(requestContext.getAppProfileId())
- .setTableName(tableName)
.setRowKey(key)
.addAllMutations(mutation.getMutations())
.build();
@@ -214,13 +261,17 @@ public MutateRowRequest toProto(RequestContext requestContext) {
*/
@InternalApi
public MutateRowsRequest toBulkProto(RequestContext requestContext) {
- String tableName =
- NameUtil.formatTableName(
- requestContext.getProjectId(), requestContext.getInstanceId(), tableId);
+ MutateRowsRequest.Builder builder = MutateRowsRequest.newBuilder();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
- return MutateRowsRequest.newBuilder()
+ return builder
.setAppProfileId(requestContext.getAppProfileId())
- .setTableName(tableName)
.addEntries(
Entry.newBuilder().setRowKey(key).addAllMutations(mutation.getMutations()).build())
.build();
@@ -239,9 +290,12 @@ public MutateRowsRequest toBulkProto(RequestContext requestContext) {
*/
@BetaApi
public static RowMutation fromProto(@Nonnull MutateRowRequest request) {
- String tableId = NameUtil.extractTableIdFromTableName(request.getTableName());
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
return RowMutation.create(
- tableId, request.getRowKey(), Mutation.fromProto(request.getMutationsList()));
+ NameUtil.extractTargetId(tableName, authorizedViewName),
+ request.getRowKey(),
+ Mutation.fromProto(request.getMutationsList()));
}
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/SampleRowKeysRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/SampleRowKeysRequest.java
new file mode 100644
index 0000000000..08d9a3ca23
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/SampleRowKeysRequest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.data.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
+import com.google.cloud.bigtable.data.v2.internal.RequestContext;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import javax.annotation.Nonnull;
+
+/** Wraps a {@link com.google.bigtable.v2.SampleRowKeysRequest}. */
+public final class SampleRowKeysRequest implements Serializable {
+ private final TargetId targetId;
+
+ private SampleRowKeysRequest(TargetId targetId) {
+ Preconditions.checkNotNull(targetId, "target id can't be null.");
+ this.targetId = targetId;
+ }
+
+ /** Creates a new instance of the sample row keys builder for the given target with targetId */
+ public static SampleRowKeysRequest create(TargetId targetId) {
+ return new SampleRowKeysRequest(targetId);
+ }
+
+ @InternalApi
+ public com.google.bigtable.v2.SampleRowKeysRequest toProto(RequestContext requestContext) {
+ com.google.bigtable.v2.SampleRowKeysRequest.Builder builder =
+ com.google.bigtable.v2.SampleRowKeysRequest.newBuilder();
+ String resourceName =
+ targetId.toResourceName(requestContext.getProjectId(), requestContext.getInstanceId());
+ if (targetId.scopedForAuthorizedView()) {
+ builder.setAuthorizedViewName(resourceName);
+ } else {
+ builder.setTableName(resourceName);
+ }
+ return builder.setAppProfileId(requestContext.getAppProfileId()).build();
+ }
+
+ /**
+ * Wraps the protobuf {@link com.google.bigtable.v2.SampleRowKeysRequest}.
+ *
+ * WARNING: Please note that the project id & instance id in the table/authorized view name
+ * will be overwritten by the configuration in the BigtableDataClient.
+ */
+ @InternalApi
+ public static SampleRowKeysRequest fromProto(
+ @Nonnull com.google.bigtable.v2.SampleRowKeysRequest request) {
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
+
+ SampleRowKeysRequest sampleRowKeysRequest =
+ SampleRowKeysRequest.create(NameUtil.extractTargetId(tableName, authorizedViewName));
+
+ return sampleRowKeysRequest;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SampleRowKeysRequest sampleRowKeysRequest = (SampleRowKeysRequest) o;
+ return Objects.equal(targetId, sampleRowKeysRequest.targetId);
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TableId.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TableId.java
new file mode 100644
index 0000000000..15b2cd9d95
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TableId.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.data.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.auto.value.AutoValue;
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
+import com.google.common.base.Preconditions;
+
+/** An implementation of a {@link TargetId} for tables. */
+@AutoValue
+public abstract class TableId implements TargetId {
+
+ /** Constructs a new TableId object for the specified table id. */
+ public static TableId of(String tableId) {
+ Preconditions.checkNotNull(tableId, "table id can't be null.");
+ return new AutoValue_TableId(tableId);
+ }
+
+ abstract String getTableId();
+
+ @Override
+ @InternalApi
+ public String toResourceName(String projectId, String instanceId) {
+ return NameUtil.formatTableName(projectId, instanceId, getTableId());
+ }
+
+ @Override
+ @InternalApi
+ public boolean scopedForAuthorizedView() {
+ return false;
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TargetId.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TargetId.java
new file mode 100644
index 0000000000..ae5be23598
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/models/TargetId.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.data.v2.models;
+
+import com.google.api.core.InternalApi;
+import com.google.api.core.InternalExtensionOnly;
+import java.io.Serializable;
+
+/**
+ * TargetId defines the scope a data operation can be applied to.
+ *
+ * @see AuthorizedViewId
+ * @see TableId
+ */
+@InternalExtensionOnly
+public interface TargetId extends Serializable {
+ /**
+ * Combines the table or authorized view id with the projectId and instanceId to form the actual
+ * resource name in the request protobuf.
+ *
+ *
This method is considered an internal implementation detail and not meant to be used by
+ * applications.
+ */
+ @InternalApi
+ String toResourceName(String projectId, String instanceId);
+
+ /**
+ * Returns true if this TargetId object represents id for an authorized view (rather than a
+ * table).
+ */
+ @InternalApi
+ boolean scopedForAuthorizedView();
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableBatchingCallSettings.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableBatchingCallSettings.java
index 2ca5e10211..3e2b540635 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableBatchingCallSettings.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/BigtableBatchingCallSettings.java
@@ -28,6 +28,7 @@
import com.google.api.gax.rpc.UnaryCallSettings;
import com.google.cloud.bigtable.data.v2.models.BulkMutation;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
+import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsAttemptResult;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import java.util.Set;
@@ -57,11 +58,12 @@
* @see RetrySettings for retry configuration.
*/
@BetaApi("This surface is likely to change as the batching surface evolves.")
-public final class BigtableBatchingCallSettings extends UnaryCallSettings {
+public final class BigtableBatchingCallSettings
+ extends UnaryCallSettings {
// This settings is just a simple wrapper for BatchingCallSettings to allow us to add
// additional functionality.
- private final BatchingCallSettings
+ private final BatchingCallSettings
batchingCallSettings;
private final boolean isLatencyBasedThrottlingEnabled;
private final Long targetRpcLatencyMs;
@@ -89,7 +91,8 @@ public BatchingSettings getBatchingSettings() {
}
/** Returns an adapter that packs and unpacks batching elements. */
- BatchingDescriptor getBatchingDescriptor() {
+ BatchingDescriptor
+ getBatchingDescriptor() {
return batchingCallSettings.getBatchingDescriptor();
}
@@ -120,7 +123,8 @@ public boolean isServerInitiatedFlowControlEnabled() {
}
static Builder newBuilder(
- BatchingDescriptor batchingDescriptor) {
+ BatchingDescriptor
+ batchingDescriptor) {
return new Builder(batchingDescriptor);
}
@@ -148,9 +152,11 @@ public String toString() {
* A base builder class for {@link BigtableBatchingCallSettings}. See the class documentation of
* {@link BigtableBatchingCallSettings} for a description of the different values that can be set.
*/
- public static class Builder extends UnaryCallSettings.Builder {
+ public static class Builder
+ extends UnaryCallSettings.Builder {
- private BatchingDescriptor batchingDescriptor;
+ private BatchingDescriptor
+ batchingDescriptor;
private BatchingSettings batchingSettings;
private boolean isLatencyBasedThrottlingEnabled;
private Long targetRpcLatencyMs;
@@ -160,7 +166,8 @@ public static class Builder extends UnaryCallSettings.Builder batchingDescriptor) {
+ BatchingDescriptor
+ batchingDescriptor) {
this.batchingDescriptor =
Preconditions.checkNotNull(batchingDescriptor, "batching descriptor can't be null");
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
index a65c0ada92..ec15c4131a 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/EnhancedBigtableStub.java
@@ -66,10 +66,10 @@
import com.google.bigtable.v2.ReadRowsRequest;
import com.google.bigtable.v2.ReadRowsResponse;
import com.google.bigtable.v2.RowRange;
-import com.google.bigtable.v2.SampleRowKeysRequest;
import com.google.bigtable.v2.SampleRowKeysResponse;
import com.google.cloud.bigtable.Version;
import com.google.cloud.bigtable.data.v2.internal.JwtCredentialsWithAudience;
+import com.google.cloud.bigtable.data.v2.internal.NameUtil;
import com.google.cloud.bigtable.data.v2.internal.RequestContext;
import com.google.cloud.bigtable.data.v2.models.BulkMutation;
import com.google.cloud.bigtable.data.v2.models.ChangeStreamMutation;
@@ -87,6 +87,8 @@
import com.google.cloud.bigtable.data.v2.models.RowAdapter;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
+import com.google.cloud.bigtable.data.v2.models.SampleRowKeysRequest;
+import com.google.cloud.bigtable.data.v2.models.TargetId;
import com.google.cloud.bigtable.data.v2.stub.changestream.ChangeStreamRecordMergingCallable;
import com.google.cloud.bigtable.data.v2.stub.changestream.GenerateInitialChangeStreamPartitionsUserCallable;
import com.google.cloud.bigtable.data.v2.stub.changestream.ReadChangeStreamResumptionStrategy;
@@ -102,7 +104,9 @@
import com.google.cloud.bigtable.data.v2.stub.metrics.StatsHeadersUnaryCallable;
import com.google.cloud.bigtable.data.v2.stub.metrics.TracedBatcherUnaryCallable;
import com.google.cloud.bigtable.data.v2.stub.mutaterows.BulkMutateRowsUserFacingCallable;
+import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsAttemptResult;
import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsBatchingDescriptor;
+import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsPartialErrorRetryAlgorithm;
import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsRetryingCallable;
import com.google.cloud.bigtable.data.v2.stub.readrows.FilterMarkerRowsCallable;
import com.google.cloud.bigtable.data.v2.stub.readrows.ReadRowsBatchingDescriptor;
@@ -164,8 +168,11 @@ public class EnhancedBigtableStub implements AutoCloseable {
private final UnaryCallable readRowCallable;
private final UnaryCallable> bulkReadRowsCallable;
private final UnaryCallable> sampleRowKeysCallable;
+ private final UnaryCallable>
+ sampleRowKeysCallableWithRequest;
private final UnaryCallable mutateRowCallable;
- private final UnaryCallable bulkMutateRowsCallable;
+ private final UnaryCallable bulkMutateRowsCallable;
+ private final UnaryCallable externalBulkMutateRowsCallable;
private final UnaryCallable checkAndMutateRowCallable;
private final UnaryCallable readModifyWriteRowCallable;
private final UnaryCallable pingAndWarmCallable;
@@ -367,8 +374,11 @@ public EnhancedBigtableStub(
readRowCallable = createReadRowCallable(new DefaultRowAdapter());
bulkReadRowsCallable = createBulkReadRowsCallable(new DefaultRowAdapter());
sampleRowKeysCallable = createSampleRowKeysCallable();
+ sampleRowKeysCallableWithRequest = createSampleRowKeysCallableWithRequest();
mutateRowCallable = createMutateRowCallable();
- bulkMutateRowsCallable = createBulkMutateRowsCallable();
+ bulkMutateRowsCallable = createMutateRowsBaseCallable();
+ externalBulkMutateRowsCallable =
+ new MutateRowsErrorConverterUnaryCallable(bulkMutateRowsCallable);
checkAndMutateRowCallable = createCheckAndMutateRowCallable();
readModifyWriteRowCallable = createReadModifyWriteRowCallable();
generateInitialChangeStreamPartitionsCallable =
@@ -493,9 +503,17 @@ private ServerStreamingCallable createReadRo
new RequestParamsExtractor() {
@Override
public Map extract(ReadRowsRequest readRowsRequest) {
+ String tableName = readRowsRequest.getTableName();
+ String authorizedViewName = readRowsRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", readRowsRequest.getTableName(),
- "app_profile_id", readRowsRequest.getAppProfileId());
+ "table_name",
+ tableName,
+ "app_profile_id",
+ readRowsRequest.getAppProfileId());
}
})
.build(),
@@ -578,6 +596,57 @@ private UnaryCallable> createBulkReadRowsCallable(
return traced.withDefaultCallContext(clientContext.getDefaultCallContext());
}
+ /**
+ * Helper function that should only be used by createSampleRowKeysCallable() and
+ * createSampleRowKeysWithRequestCallable().
+ */
+ private UnaryCallable>
+ createSampleRowKeysBaseCallable() {
+ ServerStreamingCallable
+ base =
+ GrpcRawCallableFactory.createServerStreamingCallable(
+ GrpcCallSettings
+ .
+ newBuilder()
+ .setMethodDescriptor(BigtableGrpc.getSampleRowKeysMethod())
+ .setParamsExtractor(
+ new RequestParamsExtractor() {
+ @Override
+ public Map extract(
+ com.google.bigtable.v2.SampleRowKeysRequest sampleRowKeysRequest) {
+ String tableName = sampleRowKeysRequest.getTableName();
+ String authorizedViewName =
+ sampleRowKeysRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(
+ authorizedViewName);
+ }
+ return ImmutableMap.of(
+ "table_name",
+ tableName,
+ "app_profile_id",
+ sampleRowKeysRequest.getAppProfileId());
+ }
+ })
+ .build(),
+ settings.sampleRowKeysSettings().getRetryableCodes());
+
+ UnaryCallable>
+ spoolable = base.all();
+
+ UnaryCallable>
+ withStatsHeaders = new StatsHeadersUnaryCallable<>(spoolable);
+
+ UnaryCallable>
+ withBigtableTracer = new BigtableTracerUnaryCallable<>(withStatsHeaders);
+
+ UnaryCallable>
+ retryable = withRetries(withBigtableTracer, settings.sampleRowKeysSettings());
+
+ return retryable;
+ }
+
/**
* Creates a callable chain to handle SampleRowKeys RPcs. The chain will:
*
@@ -593,36 +662,33 @@ private UnaryCallable> createBulkReadRowsCallable(
private UnaryCallable> createSampleRowKeysCallable() {
String methodName = "SampleRowKeys";
- ServerStreamingCallable base =
- GrpcRawCallableFactory.createServerStreamingCallable(
- GrpcCallSettings.newBuilder()
- .setMethodDescriptor(BigtableGrpc.getSampleRowKeysMethod())
- .setParamsExtractor(
- new RequestParamsExtractor() {
- @Override
- public Map extract(
- SampleRowKeysRequest sampleRowKeysRequest) {
- return ImmutableMap.of(
- "table_name", sampleRowKeysRequest.getTableName(),
- "app_profile_id", sampleRowKeysRequest.getAppProfileId());
- }
- })
- .build(),
- settings.sampleRowKeysSettings().getRetryableCodes());
-
- UnaryCallable> spoolable = base.all();
-
- UnaryCallable> withStatsHeaders =
- new StatsHeadersUnaryCallable<>(spoolable);
-
- UnaryCallable> withBigtableTracer =
- new BigtableTracerUnaryCallable<>(withStatsHeaders);
+ UnaryCallable>
+ baseCallable = createSampleRowKeysBaseCallable();
+ return createUserFacingUnaryCallable(
+ methodName, new SampleRowKeysCallable(baseCallable, requestContext));
+ }
- UnaryCallable> retryable =
- withRetries(withBigtableTracer, settings.sampleRowKeysSettings());
+ /**
+ * Creates a callable chain to handle SampleRowKeys RPcs. The chain will:
+ *
+ *
+ * - Convert a {@link SampleRowKeysRequest} to a {@link
+ * com.google.bigtable.v2.SampleRowKeysRequest}.
+ *
- Dispatch the request to the GAPIC's {@link BigtableStub#sampleRowKeysCallable()}.
+ *
- Spool responses into a list.
+ *
- Retry on failure.
+ *
- Convert the responses into {@link KeyOffset}s.
+ *
- Add tracing & metrics.
+ *
+ */
+ private UnaryCallable>
+ createSampleRowKeysCallableWithRequest() {
+ String methodName = "SampleRowKeys";
+ UnaryCallable>
+ baseCallable = createSampleRowKeysBaseCallable();
return createUserFacingUnaryCallable(
- methodName, new SampleRowKeysCallable(retryable, requestContext));
+ methodName, new SampleRowKeysCallableWithRequest(baseCallable, requestContext));
}
/**
@@ -643,9 +709,17 @@ private UnaryCallable createMutateRowCallable() {
new RequestParamsExtractor() {
@Override
public Map extract(MutateRowRequest mutateRowRequest) {
+ String tableName = mutateRowRequest.getTableName();
+ String authorizedViewName = mutateRowRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", mutateRowRequest.getTableName(),
- "app_profile_id", mutateRowRequest.getAppProfileId());
+ "table_name",
+ tableName,
+ "app_profile_id",
+ mutateRowRequest.getAppProfileId());
}
})
.build(),
@@ -665,14 +739,24 @@ public Map extract(MutateRowRequest mutateRowRequest) {
}
/**
- * Internal helper to create the base MutateRows callable chain. The chain is responsible for
- * retrying individual entry in case of error.
+ * Creates a callable chain to handle MutatesRows RPCs. This is meant to be used for manual
+ * batching. The chain will:
*
- * NOTE: the caller is responsible for adding tracing & metrics.
+ *
+ * - Convert a {@link BulkMutation} into a {@link MutateRowsRequest}.
+ *
- Process the response and schedule retries. At the end of each attempt, entries that have
+ * been applied, are filtered from the next attempt. Also, any entries that failed with a
+ * nontransient error, are filtered from the next attempt. This will continue until there
+ * are no more entries or there are no more retry attempts left.
+ *
- Wrap batch failures in a {@link MutateRowsAttemptResult}.
+ *
- Add tracing & metrics.
+ *
*
- * @see MutateRowsRetryingCallable for more details
+ * This callable returns an internal type {@link MutateRowsAttemptResult}.
+ *
+ * This function should not be exposed to external users, as it could cause a data loss.
*/
- private UnaryCallable createMutateRowsBaseCallable() {
+ private UnaryCallable createMutateRowsBaseCallable() {
ServerStreamingCallable base =
GrpcRawCallableFactory.createServerStreamingCallable(
GrpcCallSettings.newBuilder()
@@ -681,9 +765,17 @@ private UnaryCallable createMutateRowsBaseCallable() {
new RequestParamsExtractor() {
@Override
public Map extract(MutateRowsRequest mutateRowsRequest) {
+ String tableName = mutateRowsRequest.getTableName();
+ String authorizedViewName = mutateRowsRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", mutateRowsRequest.getTableName(),
- "app_profile_id", mutateRowsRequest.getAppProfileId());
+ "table_name",
+ tableName,
+ "app_profile_id",
+ mutateRowsRequest.getAppProfileId());
}
})
.build(),
@@ -706,55 +798,38 @@ public Map extract(MutateRowsRequest mutateRowsRequest) {
ServerStreamingCallable withBigtableTracer =
new BigtableTracerStreamingCallable<>(convertException);
- BasicResultRetryAlgorithm resultRetryAlgorithm;
+ BasicResultRetryAlgorithm resultRetryAlgorithm;
if (settings.getEnableRetryInfo()) {
resultRetryAlgorithm = new RetryInfoRetryAlgorithm<>();
} else {
resultRetryAlgorithm = new ApiResultRetryAlgorithm<>();
}
+ MutateRowsPartialErrorRetryAlgorithm mutateRowsPartialErrorRetryAlgorithm =
+ new MutateRowsPartialErrorRetryAlgorithm(resultRetryAlgorithm);
- RetryAlgorithm retryAlgorithm =
+ RetryAlgorithm retryAlgorithm =
new RetryAlgorithm<>(
- resultRetryAlgorithm,
+ mutateRowsPartialErrorRetryAlgorithm,
new ExponentialRetryAlgorithm(
settings.bulkMutateRowsSettings().getRetrySettings(), clientContext.getClock()));
- RetryingExecutorWithContext retryingExecutor =
+ RetryingExecutorWithContext retryingExecutor =
new ScheduledRetryingExecutor<>(retryAlgorithm, clientContext.getExecutor());
+ UnaryCallable baseCallable =
+ new MutateRowsRetryingCallable(
+ clientContext.getDefaultCallContext(),
+ withBigtableTracer,
+ retryingExecutor,
+ settings.bulkMutateRowsSettings().getRetryableCodes(),
+ retryAlgorithm);
- return new MutateRowsRetryingCallable(
- clientContext.getDefaultCallContext(),
- withBigtableTracer,
- retryingExecutor,
- settings.bulkMutateRowsSettings().getRetryableCodes(),
- retryAlgorithm);
- }
-
- /**
- * Creates a callable chain to handle MutatesRows RPCs. This is meant to be used for manual
- * batching. The chain will:
- *
- *
- * - Convert a {@link BulkMutation} into a {@link MutateRowsRequest}.
- *
- Process the response and schedule retries. At the end of each attempt, entries that have
- * been applied, are filtered from the next attempt. Also, any entries that failed with a
- * nontransient error, are filtered from the next attempt. This will continue until there
- * are no more entries or there are no more retry attempts left.
- *
- Wrap batch failures in a {@link
- * com.google.cloud.bigtable.data.v2.models.MutateRowsException}.
- *
- Add tracing & metrics.
- *
- */
- private UnaryCallable createBulkMutateRowsCallable() {
- UnaryCallable baseCallable = createMutateRowsBaseCallable();
-
- UnaryCallable withCookie = baseCallable;
+ UnaryCallable withCookie = baseCallable;
if (settings.getEnableRoutingCookie()) {
withCookie = new CookiesUnaryCallable<>(baseCallable);
}
- UnaryCallable flowControlCallable = null;
+ UnaryCallable flowControlCallable = null;
if (settings.bulkMutateRowsSettings().isLatencyBasedThrottlingEnabled()) {
flowControlCallable =
new DynamicFlowControlCallable(
@@ -764,16 +839,16 @@ private UnaryCallable createBulkMutateRowsCallable() {
settings.bulkMutateRowsSettings().getTargetRpcLatencyMs(),
FLOW_CONTROL_ADJUSTING_INTERVAL_MS);
}
- UnaryCallable userFacing =
+ UnaryCallable userFacing =
new BulkMutateRowsUserFacingCallable(
flowControlCallable != null ? flowControlCallable : withCookie, requestContext);
SpanName spanName = getSpanName("MutateRows");
- UnaryCallable tracedBatcherUnaryCallable =
+ UnaryCallable tracedBatcherUnaryCallable =
new TracedBatcherUnaryCallable<>(userFacing);
- UnaryCallable traced =
+ UnaryCallable traced =
new TracedUnaryCallable<>(
tracedBatcherUnaryCallable, clientContext.getTracerFactory(), spanName);
@@ -811,6 +886,37 @@ public Batcher newMutateRowsBatcher(
MoreObjects.firstNonNull(ctx, clientContext.getDefaultCallContext()));
}
+ /**
+ * Creates a {@link BatcherImpl} to handle {@link MutateRowsRequest.Entry} mutations. This is
+ * meant to be used for automatic batching with flow control.
+ *
+ *
+ * - Uses {@link MutateRowsBatchingDescriptor} to spool the {@link RowMutationEntry} mutations
+ * and send them out as {@link BulkMutation}.
+ *
- Uses {@link #bulkMutateRowsCallable()} to perform RPC.
+ *
- Batching thresholds can be configured from {@link
+ * EnhancedBigtableStubSettings#bulkMutateRowsSettings()}.
+ *
- Process the response and schedule retries. At the end of each attempt, entries that have
+ * been applied, are filtered from the next attempt. Also, any entries that failed with a
+ * nontransient error, are filtered from the next attempt. This will continue until there
+ * are no more entries or there are no more retry attempts left.
+ *
- Wrap batch failures in a {@link
+ * com.google.cloud.bigtable.data.v2.models.MutateRowsException}.
+ *
- Split the responses using {@link MutateRowsBatchingDescriptor}.
+ *
+ */
+ public Batcher newMutateRowsBatcher(
+ TargetId targetId, @Nullable GrpcCallContext ctx) {
+ return new BatcherImpl<>(
+ settings.bulkMutateRowsSettings().getBatchingDescriptor(),
+ bulkMutateRowsCallable,
+ BulkMutation.create(targetId),
+ settings.bulkMutateRowsSettings().getBatchingSettings(),
+ clientContext.getExecutor(),
+ bulkMutationFlowController,
+ MoreObjects.firstNonNull(ctx, clientContext.getDefaultCallContext()));
+ }
+
/**
* Creates a {@link BatcherImpl} to handle {@link Query#rowKey(String)}. This is meant for bulk
* read with flow control.
@@ -859,9 +965,18 @@ private UnaryCallable createCheckAndMutateRowCa
@Override
public Map extract(
CheckAndMutateRowRequest checkAndMutateRowRequest) {
+ String tableName = checkAndMutateRowRequest.getTableName();
+ String authorizedViewName =
+ checkAndMutateRowRequest.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", checkAndMutateRowRequest.getTableName(),
- "app_profile_id", checkAndMutateRowRequest.getAppProfileId());
+ "table_name",
+ tableName,
+ "app_profile_id",
+ checkAndMutateRowRequest.getAppProfileId());
}
})
.build(),
@@ -899,9 +1014,14 @@ private UnaryCallable createReadModifyWriteRowCallable(
new RequestParamsExtractor() {
@Override
public Map extract(ReadModifyWriteRowRequest request) {
+ String tableName = request.getTableName();
+ String authorizedViewName = request.getAuthorizedViewName();
+ if (tableName.isEmpty()) {
+ tableName =
+ NameUtil.extractTableNameFromAuthorizedViewName(authorizedViewName);
+ }
return ImmutableMap.of(
- "table_name", request.getTableName(),
- "app_profile_id", request.getAppProfileId());
+ "table_name", tableName, "app_profile_id", request.getAppProfileId());
}
})
.build(),
@@ -1149,6 +1269,7 @@ private ServerStreamingCallable withR
}
return retrying;
}
+
//
//
@@ -1166,15 +1287,24 @@ public UnaryCallable> sampleRowKeysCallable() {
return sampleRowKeysCallable;
}
+ public UnaryCallable> sampleRowKeysCallableWithRequest() {
+ return sampleRowKeysCallableWithRequest;
+ }
+
public UnaryCallable mutateRowCallable() {
return mutateRowCallable;
}
/**
- * Returns the callable chain created in {@link #createBulkMutateRowsCallable()} ()} during stub
+ * Returns the callable chain created in {@link #createMutateRowsBaseCallable()} during stub
* construction.
*/
public UnaryCallable bulkMutateRowsCallable() {
+ return externalBulkMutateRowsCallable;
+ }
+
+ @InternalApi
+ public UnaryCallable internalBulkMutateRowsCallable() {
return bulkMutateRowsCallable;
}
@@ -1209,6 +1339,7 @@ public UnaryCallable readModifyWriteRowCallable() {
UnaryCallable pingAndWarmCallable() {
return pingAndWarmCallable;
}
+
//
private SpanName getSpanName(String methodName) {
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MutateRowsErrorConverterUnaryCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MutateRowsErrorConverterUnaryCallable.java
new file mode 100644
index 0000000000..2b118df61e
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/MutateRowsErrorConverterUnaryCallable.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.data.v2.stub;
+
+import com.google.api.core.ApiFuture;
+import com.google.api.core.ApiFutures;
+import com.google.api.core.InternalApi;
+import com.google.api.gax.grpc.GrpcStatusCode;
+import com.google.api.gax.rpc.ApiCallContext;
+import com.google.api.gax.rpc.UnaryCallable;
+import com.google.cloud.bigtable.data.v2.models.BulkMutation;
+import com.google.cloud.bigtable.data.v2.models.MutateRowsException;
+import com.google.cloud.bigtable.data.v2.stub.mutaterows.MutateRowsAttemptResult;
+import com.google.common.util.concurrent.MoreExecutors;
+import io.grpc.Status;
+
+/**
+ * This callable converts partial batch failures into an exception. This is necessary to make sure
+ * that the caller properly handles issues and avoids possible data loss on partial failures
+ */
+@InternalApi
+public class MutateRowsErrorConverterUnaryCallable extends UnaryCallable {
+
+ private final UnaryCallable innerCallable;
+
+ public MutateRowsErrorConverterUnaryCallable(
+ UnaryCallable callable) {
+ this.innerCallable = callable;
+ }
+
+ @Override
+ public ApiFuture futureCall(BulkMutation request, ApiCallContext context) {
+ ApiFuture future = innerCallable.futureCall(request, context);
+ return ApiFutures.transform(
+ future,
+ result -> {
+ if (!result.getFailedMutations().isEmpty()) {
+ throw MutateRowsException.create(
+ null,
+ GrpcStatusCode.of(Status.Code.OK),
+ result.getFailedMutations(),
+ result.getIsRetryable());
+ }
+ return null;
+ },
+ MoreExecutors.directExecutor());
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/SampleRowKeysCallableWithRequest.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/SampleRowKeysCallableWithRequest.java
new file mode 100644
index 0000000000..034a4048d0
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/SampleRowKeysCallableWithRequest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.bigtable.data.v2.stub;
+
+import com.google.api.core.ApiFunction;
+import com.google.api.core.ApiFuture;
+import com.google.api.core.ApiFutures;
+import com.google.api.gax.rpc.ApiCallContext;
+import com.google.api.gax.rpc.UnaryCallable;
+import com.google.bigtable.v2.SampleRowKeysResponse;
+import com.google.cloud.bigtable.data.v2.internal.RequestContext;
+import com.google.cloud.bigtable.data.v2.models.KeyOffset;
+import com.google.cloud.bigtable.data.v2.models.SampleRowKeysRequest;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.List;
+
+/** Simple wrapper for SampleRowKeys to wrap the request and response protobufs. */
+class SampleRowKeysCallableWithRequest
+ extends UnaryCallable> {
+ private final RequestContext requestContext;
+ private final UnaryCallable<
+ com.google.bigtable.v2.SampleRowKeysRequest, List>
+ inner;
+
+ SampleRowKeysCallableWithRequest(
+ UnaryCallable> inner,
+ RequestContext requestContext) {
+
+ this.requestContext = requestContext;
+ this.inner = inner;
+ }
+
+ @Override
+ public ApiFuture> futureCall(
+ SampleRowKeysRequest request, ApiCallContext context) {
+ ApiFuture> rawResponse =
+ inner.futureCall(request.toProto(requestContext), context);
+
+ return ApiFutures.transform(
+ rawResponse,
+ new ApiFunction, List>() {
+ @Override
+ public List apply(List rawResponse) {
+ return convert(rawResponse);
+ }
+ },
+ MoreExecutors.directExecutor());
+ }
+
+ private static List convert(List rawResponse) {
+ ImmutableList.Builder results = ImmutableList.builder();
+
+ for (SampleRowKeysResponse element : rawResponse) {
+ results.add(KeyOffset.create(element.getRowKey(), element.getOffsetBytes()));
+ }
+
+ return results.build();
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
index 8baf6a15f4..4c3fd7a42d 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/metrics/Util.java
@@ -22,6 +22,7 @@
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.StatusCode;
import com.google.api.gax.rpc.StatusCode.Code;
+import com.google.bigtable.v2.AuthorizedViewName;
import com.google.bigtable.v2.CheckAndMutateRowRequest;
import com.google.bigtable.v2.MutateRowRequest;
import com.google.bigtable.v2.MutateRowsRequest;
@@ -30,7 +31,6 @@
import com.google.bigtable.v2.ResponseParams;
import com.google.bigtable.v2.SampleRowKeysRequest;
import com.google.bigtable.v2.TableName;
-import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.InvalidProtocolBufferException;
import io.grpc.CallOptions;
@@ -108,20 +108,33 @@ static TagValue extractStatusFromFuture(Future> future) {
static String extractTableId(Object request) {
String tableName = null;
+ String authorizedViewName = null;
if (request instanceof ReadRowsRequest) {
tableName = ((ReadRowsRequest) request).getTableName();
+ authorizedViewName = ((ReadRowsRequest) request).getAuthorizedViewName();
} else if (request instanceof MutateRowsRequest) {
tableName = ((MutateRowsRequest) request).getTableName();
+ authorizedViewName = ((MutateRowsRequest) request).getAuthorizedViewName();
} else if (request instanceof MutateRowRequest) {
tableName = ((MutateRowRequest) request).getTableName();
+ authorizedViewName = ((MutateRowRequest) request).getAuthorizedViewName();
} else if (request instanceof SampleRowKeysRequest) {
tableName = ((SampleRowKeysRequest) request).getTableName();
+ authorizedViewName = ((SampleRowKeysRequest) request).getAuthorizedViewName();
} else if (request instanceof CheckAndMutateRowRequest) {
tableName = ((CheckAndMutateRowRequest) request).getTableName();
+ authorizedViewName = ((CheckAndMutateRowRequest) request).getAuthorizedViewName();
} else if (request instanceof ReadModifyWriteRowRequest) {
tableName = ((ReadModifyWriteRowRequest) request).getTableName();
+ authorizedViewName = ((ReadModifyWriteRowRequest) request).getAuthorizedViewName();
+ }
+ if (tableName == null && authorizedViewName == null) return "undefined";
+ if (tableName.isEmpty() && authorizedViewName.isEmpty()) return "undefined";
+ if (!tableName.isEmpty()) {
+ return TableName.parse(tableName).getTable();
+ } else {
+ return AuthorizedViewName.parse(authorizedViewName).getTable();
}
- return !Strings.isNullOrEmpty(tableName) ? TableName.parse(tableName).getTable() : "undefined";
}
/**
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/BulkMutateRowsUserFacingCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/BulkMutateRowsUserFacingCallable.java
index 8048cceaad..94980a80a2 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/BulkMutateRowsUserFacingCallable.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/BulkMutateRowsUserFacingCallable.java
@@ -30,18 +30,22 @@
* applications.
*/
@InternalApi
-public final class BulkMutateRowsUserFacingCallable extends UnaryCallable {
- private final UnaryCallable innerCallable;
+public final class BulkMutateRowsUserFacingCallable
+ extends UnaryCallable {
+
+ private final UnaryCallable innerCallable;
private final RequestContext requestContext;
public BulkMutateRowsUserFacingCallable(
- UnaryCallable innerCallable, RequestContext requestContext) {
+ UnaryCallable innerCallable,
+ RequestContext requestContext) {
this.innerCallable = innerCallable;
this.requestContext = requestContext;
}
@Override
- public ApiFuture futureCall(BulkMutation request, ApiCallContext context) {
+ public ApiFuture futureCall(
+ BulkMutation request, ApiCallContext context) {
return innerCallable.futureCall(request.toProto(requestContext), context);
}
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptCallable.java
index 155ea43211..b07e67ba94 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptCallable.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptCallable.java
@@ -87,7 +87,7 @@
*
* Package-private for internal use.
*/
-class MutateRowsAttemptCallable implements Callable {
+class MutateRowsAttemptCallable implements Callable {
// Synthetic status for Mutations that didn't get a result (because the whole RPC failed). It will
// be exposed in MutateRowsException's FailedMutations.
private static final StatusCode LOCAL_UNKNOWN_STATUS =
@@ -116,17 +116,17 @@ public Object getTransportCode() {
@Nonnull private TimedAttemptSettings attemptSettings;
// Parent controller
- private RetryingFuture externalFuture;
+ private RetryingFuture externalFuture;
// Simple wrappers for handling result futures
- private final ApiFunction, Void> attemptSuccessfulCallback =
- new ApiFunction, Void>() {
- @Override
- public Void apply(List responses) {
- handleAttemptSuccess(responses);
- return null;
- }
- };
+ private final ApiFunction, MutateRowsAttemptResult>
+ attemptSuccessfulCallback =
+ new ApiFunction, MutateRowsAttemptResult>() {
+ @Override
+ public MutateRowsAttemptResult apply(List responses) {
+ return handleAttemptSuccess(responses);
+ }
+ };
private final ApiFunction> attemptFailedCallback =
new ApiFunction>() {
@@ -153,7 +153,7 @@ public List apply(Throwable throwable) {
permanentFailures = Lists.newArrayList();
}
- public void setExternalFuture(RetryingFuture externalFuture) {
+ public void setExternalFuture(RetryingFuture externalFuture) {
this.externalFuture = externalFuture;
}
@@ -166,7 +166,7 @@ public void setExternalFuture(RetryingFuture externalFuture) {
* return of this method should just be ignored.
*/
@Override
- public Void call() {
+ public MutateRowsAttemptResult call() {
try {
// externalFuture is set from MutateRowsRetryingCallable before invoking this method. It
// shouldn't be null unless the code changed
@@ -192,7 +192,7 @@ public Void call() {
}
// Handle concurrent cancellation
- externalFuture.setAttemptFuture(new NonCancellableFuture());
+ externalFuture.setAttemptFuture(new NonCancellableFuture<>());
if (externalFuture.isDone()) {
return null;
}
@@ -208,13 +208,13 @@ public Void call() {
// Inspect the results and either propagate the success, or prepare to retry the failed
// mutations
- ApiFuture transformed =
+ ApiFuture transformed =
ApiFutures.transform(catching, attemptSuccessfulCallback, MoreExecutors.directExecutor());
// Notify the parent of the attempt
externalFuture.setAttemptFuture(transformed);
} catch (Throwable e) {
- externalFuture.setAttemptFuture(ApiFutures.immediateFailedFuture(e));
+ externalFuture.setAttemptFuture(ApiFutures.immediateFailedFuture(e));
}
return null;
@@ -257,7 +257,8 @@ private void handleAttemptError(Throwable rpcError) {
currentRequest = builder.build();
originalIndexes = newOriginalIndexes;
- throw MutateRowsException.create(rpcError, allFailures.build(), builder.getEntriesCount() > 0);
+ throw MutateRowsException.create(
+ rpcError, entryError.getStatusCode(), allFailures.build(), builder.getEntriesCount() > 0);
}
/**
@@ -267,7 +268,7 @@ private void handleAttemptError(Throwable rpcError) {
* {@link MutateRowsException}. If no errors exist, then the attempt future is successfully
* completed. We don't currently handle RetryInfo on entry level failures.
*/
- private void handleAttemptSuccess(List responses) {
+ private MutateRowsAttemptResult handleAttemptSuccess(List responses) {
List allFailures = Lists.newArrayList(permanentFailures);
MutateRowsRequest lastRequest = currentRequest;
@@ -326,8 +327,9 @@ private void handleAttemptSuccess(List responses) {
if (!allFailures.isEmpty()) {
boolean isRetryable = builder.getEntriesCount() > 0;
- throw MutateRowsException.create(null, allFailures, isRetryable);
+ return MutateRowsAttemptResult.create(allFailures, isRetryable);
}
+ return MutateRowsAttemptResult.success();
}
/**
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptResult.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptResult.java
new file mode 100644
index 0000000000..d668c2a50f
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsAttemptResult.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.data.v2.stub.mutaterows;
+
+import com.google.api.core.InternalApi;
+import com.google.auto.value.AutoValue;
+import com.google.cloud.bigtable.data.v2.models.MutateRowsException.FailedMutation;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nonnull;
+
+/**
+ * This class represents the result of a MutateRows attempt. It contains a potentially empty list of
+ * failed mutations, along with an indicator whether these errors are retryable.
+ */
+@InternalApi
+@AutoValue
+public abstract class MutateRowsAttemptResult {
+
+ public abstract List getFailedMutations();
+
+ public abstract boolean getIsRetryable();
+
+ @InternalApi
+ @Nonnull
+ public static MutateRowsAttemptResult create(
+ List failedMutations, boolean isRetryable) {
+ return new AutoValue_MutateRowsAttemptResult(failedMutations, isRetryable);
+ }
+
+ @InternalApi
+ @Nonnull
+ public static MutateRowsAttemptResult success() {
+ return new AutoValue_MutateRowsAttemptResult(new ArrayList<>(), false);
+ }
+}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsBatchingDescriptor.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsBatchingDescriptor.java
index 65cc781169..87f5c88d3e 100644
--- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsBatchingDescriptor.java
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsBatchingDescriptor.java
@@ -37,7 +37,7 @@
*/
@InternalApi("For internal use only")
public class MutateRowsBatchingDescriptor
- implements BatchingDescriptor {
+ implements BatchingDescriptor {
@Override
public BatchingRequestBuilder newRequestBuilder(
@@ -46,7 +46,15 @@ public BatchingRequestBuilder newRequestBuilder(
}
@Override
- public void splitResponse(Void response, List> entries) {
+ public void splitResponse(
+ MutateRowsAttemptResult response, List> entries) {
+ // For every failed mutation in the response, we set the exception on the matching requested
+ // mutation. It is important to set the correct error on the correct mutation. When the entry is
+ // later read, it resolves the exception first, and only later it goes to the value set by
+ // set().
+ for (FailedMutation mutation : response.getFailedMutations()) {
+ entries.get(mutation.getIndex()).getResultFuture().setException(mutation.getError());
+ }
for (BatchEntry batchResponse : entries) {
batchResponse.getResultFuture().set(null);
}
diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsPartialErrorRetryAlgorithm.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsPartialErrorRetryAlgorithm.java
new file mode 100644
index 0000000000..9c7035db96
--- /dev/null
+++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/mutaterows/MutateRowsPartialErrorRetryAlgorithm.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.cloud.bigtable.data.v2.stub.mutaterows;
+
+import com.google.api.core.InternalApi;
+import com.google.api.gax.retrying.ResultRetryAlgorithmWithContext;
+import com.google.api.gax.retrying.RetryingContext;
+import com.google.api.gax.retrying.TimedAttemptSettings;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * This algorithm will retry if there was a retryable failed mutation, or if there wasn't but the
+ * underlying algorithm allows a retry.
+ */
+@InternalApi
+public class MutateRowsPartialErrorRetryAlgorithm
+ implements ResultRetryAlgorithmWithContext {
+ private final ResultRetryAlgorithmWithContext retryAlgorithm;
+
+ public MutateRowsPartialErrorRetryAlgorithm(
+ ResultRetryAlgorithmWithContext