1818import com .google .api .core .InternalApi ;
1919import com .google .api .gax .batching .PartitionKey ;
2020import com .google .api .gax .batching .RequestBuilder ;
21- import com .google .api .gax .grpc .GrpcStatusCode ;
2221import com .google .api .gax .rpc .ApiException ;
23- import com .google .api .gax .rpc .ApiExceptionFactory ;
2422import com .google .api .gax .rpc .BatchedRequestIssuer ;
2523import com .google .api .gax .rpc .BatchingDescriptor ;
26- import com .google .api .gax .rpc .StatusCode ;
2724import com .google .bigtable .v2 .MutateRowsRequest ;
28- import com .google .bigtable .v2 .MutateRowsResponse ;
29- import com .google .common .base .Preconditions ;
30- import com .google .common .collect .ImmutableSet ;
31- import com .google .common .primitives .Ints ;
32- import com .google .rpc .Code ;
33- import com .google .rpc .Status ;
34- import io .grpc .StatusException ;
35- import io .grpc .StatusRuntimeException ;
25+ import com .google .cloud .bigtable .data .v2 .models .MutateRowsException ;
26+ import com .google .cloud .bigtable .data .v2 .models .MutateRowsException .FailedMutation ;
27+ import com .google .common .base .Function ;
28+ import com .google .common .collect .Maps ;
3629import java .util .Collection ;
37- import java .util .Set ;
30+ import java .util .List ;
31+ import java .util .Map ;
32+ import javax .annotation .Nullable ;
3833
3934/**
40- * A custom implementation of a {@link BatchingDescriptor} to split individual results of a bulk
41- * MutateRowsResponse. Each individual result will be matched with its issuer. Since the embedded
42- * results bypass gax's result processing chains, this class is responsible for wrapping errors in
43- * {@link ApiException}s and marking each error as retryable.
35+ * A custom implementation of a {@link BatchingDescriptor} to split individual results in a {@link
36+ * MutateRowsException}. Each individual result will be matched with its issuer.
4437 *
4538 * <p>This class is considered an internal implementation detail and not meant to be used by
4639 * applications directly.
4740 */
4841@ InternalApi
49- public class MutateRowsBatchingDescriptor
50- implements BatchingDescriptor <MutateRowsRequest , MutateRowsResponse > {
51-
52- // Shared response to notify individual issuers of a successful mutation.
53- private static final MutateRowsResponse OK_RESPONSE =
54- MutateRowsResponse .newBuilder ()
55- .addEntries (
56- MutateRowsResponse .Entry .newBuilder ()
57- .setIndex (0 )
58- .setStatus (Status .newBuilder ().setCode (Code .OK_VALUE )))
59- .build ();
60-
61- private final ImmutableSet <StatusCode .Code > retryableCodes ;
62-
63- public MutateRowsBatchingDescriptor (Set <StatusCode .Code > retryableCodes ) {
64- this .retryableCodes = ImmutableSet .copyOf (retryableCodes );
65- }
66-
42+ public class MutateRowsBatchingDescriptor implements BatchingDescriptor <MutateRowsRequest , Void > {
6743 /** Return the target table name. This will be used to combine batcheable requests */
6844 @ Override
6945 public PartitionKey getBatchPartitionKey (MutateRowsRequest request ) {
@@ -76,44 +52,49 @@ public RequestBuilder<MutateRowsRequest> getRequestBuilder() {
7652 return new MyRequestBuilder ();
7753 }
7854
79- /** {@inheritDoc} */
8055 @ Override
8156 public void splitResponse (
82- MutateRowsResponse batchResponse ,
83- Collection <? extends BatchedRequestIssuer <MutateRowsResponse >> batch ) {
57+ Void batchResponse , Collection <? extends BatchedRequestIssuer <Void >> batch ) {
8458
85- // Sort the result entries by index.
86- Status [] sortedEntries = new Status [batchResponse .getEntriesCount ()];
87-
88- for (MutateRowsResponse .Entry entry : batchResponse .getEntriesList ()) {
89- int index = Ints .checkedCast (entry .getIndex ());
90- Preconditions .checkState (
91- sortedEntries [index ] == null , "Got multiple results for the same sub-mutation" );
92- sortedEntries [index ] = entry .getStatus ();
59+ for (BatchedRequestIssuer <Void > issuer : batch ) {
60+ issuer .setResponse (null );
9361 }
62+ }
9463
95- // Notify all of issuers of the corresponding result.
96- int i = 0 ;
97- for (BatchedRequestIssuer <MutateRowsResponse > issuer : batch ) {
98- Status entry = sortedEntries [i ++];
99- Preconditions .checkState (entry != null , "Missing result for entry" );
64+ @ Override
65+ public void splitException (
66+ Throwable throwable , Collection <? extends BatchedRequestIssuer <Void >> batch ) {
10067
101- if (entry .getCode () == Code .OK_VALUE ) {
102- issuer .setResponse (OK_RESPONSE );
103- } else {
104- issuer .setException (createElementException (entry ));
68+ if (!(throwable instanceof MutateRowsException )) {
69+ for (BatchedRequestIssuer <Void > issuer : batch ) {
70+ issuer .setException (throwable );
10571 }
72+ return ;
10673 }
107- }
10874
109- /** {@inheritDoc} */
110- @ Override
111- public void splitException (
112- Throwable throwable , Collection <? extends BatchedRequestIssuer <MutateRowsResponse >> batch ) {
113- throwable = createElementException (throwable );
75+ List <FailedMutation > failedMutations = ((MutateRowsException ) throwable ).getFailedMutations ();
76+
77+ Map <Integer , FailedMutation > errorsByIndex =
78+ Maps .uniqueIndex (
79+ failedMutations ,
80+ new Function <FailedMutation , Integer >() {
81+ @ Nullable
82+ @ Override
83+ public Integer apply (@ Nullable FailedMutation input ) {
84+ return input .getIndex ();
85+ }
86+ });
11487
115- for (BatchedRequestIssuer <MutateRowsResponse > responder : batch ) {
116- responder .setException (throwable );
88+ int i = 0 ;
89+ for (BatchedRequestIssuer <Void > issuer : batch ) {
90+ for (int j = 0 ; j < issuer .getMessageCount (); j ++) {
91+ FailedMutation failure = errorsByIndex .get (i ++);
92+ if (failure == null ) {
93+ issuer .setResponse (null );
94+ } else {
95+ issuer .setException (failure .getError ());
96+ }
97+ }
11798 }
11899 }
119100
@@ -129,38 +110,6 @@ public long countBytes(MutateRowsRequest request) {
129110 return request .getSerializedSize ();
130111 }
131112
132- /** Convert an element error Status into an ApiException */
133- private ApiException createElementException (Status protoStatus ) {
134- Preconditions .checkArgument (protoStatus .getCode () != Code .OK_VALUE , "OK is not an error" );
135-
136- StatusRuntimeException throwable =
137- io .grpc .Status .fromCodeValue (protoStatus .getCode ())
138- .withDescription (protoStatus .getMessage ())
139- .asRuntimeException ();
140-
141- return createElementException (throwable );
142- }
143-
144- /** Convert a Throwable into an ApiException, marking it as retryable when appropriate. */
145- private ApiException createElementException (Throwable throwable ) {
146- final io .grpc .Status .Code code ;
147-
148- if (throwable instanceof ApiException ) {
149- return (ApiException ) throwable ;
150- } else if (throwable instanceof StatusRuntimeException ) {
151- code = ((StatusRuntimeException ) throwable ).getStatus ().getCode ();
152- } else if (throwable instanceof StatusException ) {
153- code = ((StatusException ) throwable ).getStatus ().getCode ();
154- } else {
155- code = io .grpc .Status .Code .UNKNOWN ;
156- }
157-
158- GrpcStatusCode gaxStatusCode = GrpcStatusCode .of (code );
159- boolean isRetryable = retryableCodes .contains (gaxStatusCode .getCode ());
160-
161- return ApiExceptionFactory .createException (throwable , gaxStatusCode , isRetryable );
162- }
163-
164113 /** A {@link com.google.api.gax.batching.RequestBuilder} that can aggregate MutateRowsRequest */
165114 static class MyRequestBuilder implements RequestBuilder <MutateRowsRequest > {
166115 private MutateRowsRequest .Builder builder ;
0 commit comments