37
37
import java .util .Map ;
38
38
39
39
/**
40
- * A {@link BigQueryWriter} capable of updating BigQuery table schemas.
40
+ * A {@link BigQueryWriter} capable of updating BigQuery table schemas and creating non-existed tables automatically .
41
41
*/
42
42
public class AdaptiveBigQueryWriter extends BigQueryWriter {
43
43
private static final Logger logger = LoggerFactory .getLogger (AdaptiveBigQueryWriter .class );
44
44
45
45
// The maximum number of retries we will attempt to write rows after updating a BQ table schema.
46
- private static final int AFTER_UPDATE_RETY_LIMIT = 5 ;
46
+ private static final int AFTER_UPDATE_RETRY_LIMIT = 5 ;
47
47
48
48
private final BigQuery bigQuery ;
49
49
private final SchemaManager schemaManager ;
50
+ private final boolean updateSchemas ;
51
+ private final boolean autoCreateTables ;
50
52
51
53
/**
52
54
* @param bigQuery Used to send write requests to BigQuery.
@@ -57,10 +59,14 @@ public class AdaptiveBigQueryWriter extends BigQueryWriter {
57
59
public AdaptiveBigQueryWriter (BigQuery bigQuery ,
58
60
SchemaManager schemaManager ,
59
61
int retry ,
60
- long retryWait ) {
62
+ long retryWait ,
63
+ boolean updateSchemas ,
64
+ boolean autoCreateTables ) {
61
65
super (retry , retryWait );
62
66
this .bigQuery = bigQuery ;
63
67
this .schemaManager = schemaManager ;
68
+ this .updateSchemas = updateSchemas ;
69
+ this .autoCreateTables = autoCreateTables ;
64
70
}
65
71
66
72
private boolean isTableMissingSchema (BigQueryException exception ) {
@@ -69,6 +75,12 @@ private boolean isTableMissingSchema(BigQueryException exception) {
69
75
return exception .getReason () != null && exception .getReason ().equalsIgnoreCase ("invalid" );
70
76
}
71
77
78
+ private boolean isTableNotExisted (BigQueryException exception ) {
79
+ // If a table does not exist, it will raise a BigQueryException that the input is notFound
80
+ // Referring to Google Cloud Error Codes Doc: https://cloud.google.com/bigquery/docs/error-messages?hl=en
81
+ return exception .getReason () != null && exception .getReason ().equalsIgnoreCase ("notFound" );
82
+ }
83
+
72
84
/**
73
85
* Sends the request to BigQuery, then checks the response to see if any errors have occurred. If
74
86
* any have, and all errors can be blamed upon invalid columns in the rows sent, attempts to
@@ -89,11 +101,13 @@ public Map<Long, List<BigQueryError>> performWriteRequest(
89
101
// Should only perform one schema update attempt; may have to continue insert attempts due to
90
102
// BigQuery schema updates taking up to two minutes to take effect
91
103
if (writeResponse .hasErrors ()
92
- && onlyContainsInvalidSchemaErrors (writeResponse .getInsertErrors ())) {
104
+ && onlyContainsInvalidSchemaErrors (writeResponse .getInsertErrors ()) && updateSchemas ) {
93
105
attemptSchemaUpdate (tableId , topic );
94
106
}
95
107
} catch (BigQueryException exception ) {
96
- if (isTableMissingSchema (exception )) {
108
+ if (isTableNotExisted (exception ) && autoCreateTables ) {
109
+ attemptTableCreate (tableId , topic );
110
+ } else if (isTableMissingSchema (exception ) && updateSchemas ) {
97
111
attemptSchemaUpdate (tableId , topic );
98
112
} else {
99
113
throw exception ;
@@ -117,10 +131,10 @@ && onlyContainsInvalidSchemaErrors(writeResponse.getInsertErrors())) {
117
131
return writeResponse .getInsertErrors ();
118
132
}
119
133
attemptCount ++;
120
- if (attemptCount >= AFTER_UPDATE_RETY_LIMIT ) {
134
+ if (attemptCount >= AFTER_UPDATE_RETRY_LIMIT ) {
121
135
throw new BigQueryConnectException (
122
136
"Failed to write rows after BQ schema update within "
123
- + AFTER_UPDATE_RETY_LIMIT + " attempts for: " + tableId .getBaseTableId ());
137
+ + AFTER_UPDATE_RETRY_LIMIT + " attempts for: " + tableId .getBaseTableId ());
124
138
}
125
139
}
126
140
logger .debug ("table insertion completed successfully" );
@@ -136,6 +150,16 @@ private void attemptSchemaUpdate(PartitionedTableId tableId, String topic) {
136
150
}
137
151
}
138
152
153
+ private void attemptTableCreate (PartitionedTableId tableId , String topic ) {
154
+ try {
155
+ schemaManager .createTable (tableId .getBaseTableId (), topic );
156
+ logger .info ("Table {} does not exist, auto-created table for topic {}" , tableId .getBaseTableName (), topic );
157
+ } catch (BigQueryException exception ) {
158
+ throw new BigQueryConnectException (
159
+ "Failed to create table " + tableId .getBaseTableName (), exception );
160
+ }
161
+ }
162
+
139
163
/*
140
164
* Currently, the only way to determine the cause of an insert all failure is by examining the map
141
165
* object returned by the insertErrors() method of an insert all response. The only way to
0 commit comments