7
7
import java .sql .Types ;
8
8
import java .util .*;
9
9
import java .util .function .BiFunction ;
10
+ import net .snowflake .client .jdbc .SnowflakeSQLException ;
10
11
import net .snowflake .client .jdbc .internal .org .bouncycastle .operator .OperatorCreationException ;
11
12
import net .snowflake .client .jdbc .internal .org .bouncycastle .pkcs .PKCSException ;
12
13
import org .embulk .config .ConfigDiff ;
@@ -124,6 +125,16 @@ public static MatchByColumnName fromString(String value) {
124
125
}
125
126
}
126
127
128
+ // error codes which need reauthenticate
129
+ // ref:
130
+ // https://github.com/snowflakedb/snowflake-jdbc/blob/v3.13.26/src/main/java/net/snowflake/client/jdbc/SnowflakeUtil.java#L42
131
+ private static final int ID_TOKEN_EXPIRED_GS_CODE = 390110 ;
132
+ private static final int SESSION_NOT_EXIST_GS_CODE = 390111 ;
133
+ private static final int MASTER_TOKEN_NOTFOUND = 390113 ;
134
+ private static final int MASTER_EXPIRED_GS_CODE = 390114 ;
135
+ private static final int MASTER_TOKEN_INVALID_GS_CODE = 390115 ;
136
+ private static final int ID_TOKEN_INVALID_LOGIN_REQUEST_GS_CODE = 390195 ;
137
+
127
138
@ Override
128
139
protected Class <? extends PluginTask > getTaskClass () {
129
140
return SnowflakePluginTask .class ;
@@ -204,12 +215,12 @@ public ConfigDiff transaction(
204
215
snowflakeCon .runCreateStage (stageIdentifier );
205
216
configDiff = super .transaction (config , schema , taskCount , control );
206
217
if (t .getDeleteStage ()) {
207
- snowflakeCon . runDropStage ( stageIdentifier );
218
+ runDropStageWithRecovery ( snowflakeCon , stageIdentifier , task );
208
219
}
209
220
} catch (Exception e ) {
210
221
if (t .getDeleteStage () && t .getDeleteStageOnError ()) {
211
222
try {
212
- snowflakeCon . runDropStage ( stageIdentifier );
223
+ runDropStageWithRecovery ( snowflakeCon , stageIdentifier , task );
213
224
} catch (SQLException ex ) {
214
225
throw new RuntimeException (ex );
215
226
}
@@ -220,6 +231,35 @@ public ConfigDiff transaction(
220
231
return configDiff ;
221
232
}
222
233
234
+ private void runDropStageWithRecovery (
235
+ SnowflakeOutputConnection snowflakeCon , StageIdentifier stageIdentifier , PluginTask task )
236
+ throws SQLException {
237
+ try {
238
+ snowflakeCon .runDropStage (stageIdentifier );
239
+ } catch (SnowflakeSQLException ex ) {
240
+ // INFO: Don't handle only SnowflakeReauthenticationRequest here
241
+ // because SnowflakeSQLException with following error codes may be thrown in some cases.
242
+
243
+ logger .info ("SnowflakeSQLException was caught: ({}) {}" , ex .getErrorCode (), ex .getMessage ());
244
+
245
+ switch (ex .getErrorCode ()) {
246
+ case ID_TOKEN_EXPIRED_GS_CODE :
247
+ case SESSION_NOT_EXIST_GS_CODE :
248
+ case MASTER_TOKEN_NOTFOUND :
249
+ case MASTER_EXPIRED_GS_CODE :
250
+ case MASTER_TOKEN_INVALID_GS_CODE :
251
+ case ID_TOKEN_INVALID_LOGIN_REQUEST_GS_CODE :
252
+ // INFO: If runCreateStage consumed a lot of time, authentication might be expired.
253
+ // In this case, retry to drop stage.
254
+ snowflakeCon = (SnowflakeOutputConnection ) getConnector (task , true ).connect (true );
255
+ snowflakeCon .runDropStage (stageIdentifier );
256
+ break ;
257
+ default :
258
+ throw ex ;
259
+ }
260
+ }
261
+ }
262
+
223
263
@ Override
224
264
public ConfigDiff resume (
225
265
TaskSource taskSource , Schema schema , int taskCount , OutputPlugin .Control control ) {
0 commit comments