diff --git a/pom.xml b/pom.xml
index fc761c48dd..11352e6b43 100644
--- a/pom.xml
+++ b/pom.xml
@@ -98,6 +98,7 @@
1.7.5
3.3.2
0.23.1
+ 3.3.2
${project.basedir}/src/test/java/
@@ -836,6 +837,11 @@
+
+ dev.failsafe
+ failsafe
+ ${failsafe.version}
+
diff --git a/src/main/java/io/cdap/plugin/gcp/bigquery/action/BigQueryExecute.java b/src/main/java/io/cdap/plugin/gcp/bigquery/action/BigQueryExecute.java
index 78455fdb4d..fc1806d857 100644
--- a/src/main/java/io/cdap/plugin/gcp/bigquery/action/BigQueryExecute.java
+++ b/src/main/java/io/cdap/plugin/gcp/bigquery/action/BigQueryExecute.java
@@ -35,6 +35,9 @@
import com.google.cloud.kms.v1.CryptoKeyName;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import dev.failsafe.Failsafe;
+import dev.failsafe.RetryPolicy;
import io.cdap.cdap.api.annotation.Description;
import io.cdap.cdap.api.annotation.Macro;
import io.cdap.cdap.api.annotation.Name;
@@ -43,16 +46,20 @@
import io.cdap.cdap.etl.api.action.Action;
import io.cdap.cdap.etl.api.action.ActionContext;
import io.cdap.cdap.etl.common.Constants;
+import io.cdap.plugin.gcp.bigquery.exception.BigQueryJobExecutionException;
import io.cdap.plugin.gcp.bigquery.sink.BigQuerySinkUtils;
import io.cdap.plugin.gcp.bigquery.util.BigQueryUtil;
import io.cdap.plugin.gcp.common.CmekUtils;
import io.cdap.plugin.gcp.common.GCPUtils;
+import org.jetbrains.annotations.TestOnly;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.time.Duration;
import java.util.Collections;
import java.util.Map;
+import java.util.Set;
import javax.annotation.Nullable;
/**
@@ -69,8 +76,13 @@ public final class BigQueryExecute extends AbstractBigQueryAction {
private static final Logger LOG = LoggerFactory.getLogger(BigQueryExecute.class);
public static final String NAME = "BigQueryExecute";
private static final String RECORDS_PROCESSED = "records.processed";
+ private final Config config;
+ private static final Set retryOnReason = ImmutableSet.of("jobBackendError", "jobInternalError");
- private Config config;
+ @TestOnly
+ public BigQueryExecute(Config config) {
+ this.config = config;
+ }
@Override
public void run(ActionContext context) throws Exception {
@@ -103,9 +115,6 @@ public void run(ActionContext context) throws Exception {
// Enable legacy SQL
builder.setUseLegacySql(config.isLegacySQL());
- // Location must match that of the dataset(s) referenced in the query.
- JobId jobId = JobId.newBuilder().setRandomJob().setLocation(config.getLocation()).build();
-
// API request - starts the query.
Credentials credentials = config.getServiceAccount() == null ?
null : GCPUtils.loadServiceAccountCredentials(config.getServiceAccount(),
@@ -129,19 +138,62 @@ public void run(ActionContext context) throws Exception {
QueryJobConfiguration queryConfig = builder.build();
- Job queryJob = bigQuery.create(JobInfo.newBuilder(queryConfig).setJobId(jobId).build());
+ // Exponential backoff with initial retry of 1 second and max retry of 32 seconds.
+ executeQueryWithExponentialBackoff(bigQuery, queryConfig, context);
+ }
- LOG.info("Executing SQL as job {}.", jobId.getJob());
- LOG.debug("The BigQuery SQL is {}", config.getSql());
+ protected void executeQueryWithExponentialBackoff(BigQuery bigQuery,
+ QueryJobConfiguration queryConfig, ActionContext context) {
+ Failsafe.with(getRetryPolicy()).run(() -> executeQuery(bigQuery, queryConfig, context, retryOnReason));
+ }
- // Wait for the query to complete
- queryJob = queryJob.waitFor();
+ private RetryPolicy