17
17
package com .example .bigquerystorage ;
18
18
19
19
// [START bigquerystorage_jsonstreamwriter_default]
20
-
21
20
import com .google .api .core .ApiFuture ;
22
21
import com .google .api .core .ApiFutureCallback ;
23
22
import com .google .api .core .ApiFutures ;
24
23
import com .google .api .gax .core .FixedExecutorProvider ;
24
+ import com .google .api .gax .retrying .RetrySettings ;
25
25
import com .google .cloud .bigquery .BigQuery ;
26
26
import com .google .cloud .bigquery .BigQueryOptions ;
27
27
import com .google .cloud .bigquery .QueryJobConfiguration ;
35
35
import com .google .cloud .bigquery .storage .v1 .Exceptions .StorageException ;
36
36
import com .google .cloud .bigquery .storage .v1 .JsonStreamWriter ;
37
37
import com .google .cloud .bigquery .storage .v1 .TableName ;
38
- import com .google .common .collect .ImmutableList ;
39
38
import com .google .common .util .concurrent .MoreExecutors ;
40
39
import com .google .protobuf .ByteString ;
41
40
import com .google .protobuf .Descriptors .DescriptorValidationException ;
42
- import io .grpc .Status ;
43
- import io .grpc .Status .Code ;
44
41
import java .io .IOException ;
45
42
import java .util .Map ;
46
43
import java .util .concurrent .Executors ;
49
46
import javax .annotation .concurrent .GuardedBy ;
50
47
import org .json .JSONArray ;
51
48
import org .json .JSONObject ;
49
+ import org .threeten .bp .Duration ;
52
50
53
51
public class WriteToDefaultStream {
54
52
@@ -97,7 +95,7 @@ public static void writeToDefaultStream(String projectId, String datasetName, St
97
95
jsonArr .put (record );
98
96
}
99
97
100
- writer .append (new AppendContext (jsonArr , 0 ));
98
+ writer .append (new AppendContext (jsonArr ));
101
99
}
102
100
103
101
// Final cleanup for the stream during worker teardown.
@@ -130,26 +128,15 @@ private static void verifyExpectedRowCount(TableName parentTable, int expectedRo
130
128
private static class AppendContext {
131
129
132
130
JSONArray data ;
133
- int retryCount = 0 ;
134
131
135
- AppendContext (JSONArray data , int retryCount ) {
132
+ AppendContext (JSONArray data ) {
136
133
this .data = data ;
137
- this .retryCount = retryCount ;
138
134
}
139
135
}
140
136
141
137
private static class DataWriter {
142
138
143
- private static final int MAX_RETRY_COUNT = 3 ;
144
139
private static final int MAX_RECREATE_COUNT = 3 ;
145
- private static final ImmutableList <Code > RETRIABLE_ERROR_CODES =
146
- ImmutableList .of (
147
- Code .INTERNAL ,
148
- Code .ABORTED ,
149
- Code .CANCELLED ,
150
- Code .FAILED_PRECONDITION ,
151
- Code .DEADLINE_EXCEEDED ,
152
- Code .UNAVAILABLE );
153
140
154
141
// Track the number of in-flight requests to wait for all responses before shutting down.
155
142
private final Phaser inflightRequestCount = new Phaser (1 );
@@ -163,6 +150,19 @@ private static class DataWriter {
163
150
164
151
public void initialize (TableName parentTable )
165
152
throws DescriptorValidationException , IOException , InterruptedException {
153
+ // Configure in-stream automatic retry settings.
154
+ // Error codes that are immediately retried:
155
+ // * ABORTED, UNAVAILABLE, CANCELLED, INTERNAL, DEADLINE_EXCEEDED
156
+ // Error codes that are retried with exponential backoff:
157
+ // * RESOURCE_EXHAUSTED
158
+ RetrySettings retrySettings =
159
+ RetrySettings .newBuilder ()
160
+ .setInitialRetryDelay (Duration .ofMillis (500 ))
161
+ .setRetryDelayMultiplier (1.1 )
162
+ .setMaxAttempts (5 )
163
+ .setMaxRetryDelay (Duration .ofMinutes (1 ))
164
+ .build ();
165
+
166
166
// Use the JSON stream writer to send records in JSON format. Specify the table name to write
167
167
// to the default stream.
168
168
// For more information about JsonStreamWriter, see:
@@ -183,6 +183,7 @@ public void initialize(TableName parentTable)
183
183
// column, apply the default value to the missing value field.
184
184
.setDefaultMissingValueInterpretation (
185
185
AppendRowsRequest .MissingValueInterpretation .DEFAULT_VALUE )
186
+ .setRetrySettings (retrySettings )
186
187
.build ();
187
188
}
188
189
@@ -244,26 +245,6 @@ public void onSuccess(AppendRowsResponse response) {
244
245
}
245
246
246
247
public void onFailure (Throwable throwable ) {
247
- // If the wrapped exception is a StatusRuntimeException, check the state of the operation.
248
- // If the state is INTERNAL, CANCELLED, or ABORTED, you can retry. For more information,
249
- // see: https://grpc.github.io/grpc-java/javadoc/io/grpc/StatusRuntimeException.html
250
- Status status = Status .fromThrowable (throwable );
251
- if (appendContext .retryCount < MAX_RETRY_COUNT
252
- && RETRIABLE_ERROR_CODES .contains (status .getCode ())) {
253
- appendContext .retryCount ++;
254
- try {
255
- // Since default stream appends are not ordered, we can simply retry the appends.
256
- // Retrying with exclusive streams requires more careful consideration.
257
- this .parent .append (appendContext );
258
- // Mark the existing attempt as done since it's being retried.
259
- done ();
260
- return ;
261
- } catch (Exception e ) {
262
- // Fall through to return error.
263
- System .out .format ("Failed to retry append: %s\n " , e );
264
- }
265
- }
266
-
267
248
if (throwable instanceof AppendSerializationError ) {
268
249
AppendSerializationError ase = (AppendSerializationError ) throwable ;
269
250
Map <Integer , String > rowIndexToErrorMessage = ase .getRowIndexToErrorMessage ();
@@ -282,7 +263,7 @@ public void onFailure(Throwable throwable) {
282
263
// avoid potentially blocking while we are in a callback.
283
264
if (dataNew .length () > 0 ) {
284
265
try {
285
- this .parent .append (new AppendContext (dataNew , 0 ));
266
+ this .parent .append (new AppendContext (dataNew ));
286
267
} catch (DescriptorValidationException e ) {
287
268
throw new RuntimeException (e );
288
269
} catch (IOException e ) {
0 commit comments