1- import { Thrift } from 'thrift ' ;
1+ import { Response } from 'node-fetch ' ;
22import TCLIService from '../../../thrift/TCLIService' ;
33import HiveDriverError from '../../errors/HiveDriverError' ;
4- import IClientContext , { ClientConfig } from '../../contracts/IClientContext' ;
5-
6- interface CommandExecutionInfo {
7- startTime : number ; // in milliseconds
8- attempt : number ;
9- }
10-
11- function getRetryDelay ( attempt : number , config : ClientConfig ) : number {
12- const scale = Math . max ( 1 , 1.5 ** ( attempt - 1 ) ) ; // ensure scale >= 1
13- return Math . min ( config . retryDelayMin * scale , config . retryDelayMax ) ;
14- }
15-
16- function delay ( milliseconds : number ) : Promise < void > {
17- return new Promise < void > ( ( resolve ) => {
18- setTimeout ( ( ) => resolve ( ) , milliseconds ) ;
19- } ) ;
20- }
4+ import RetryError , { RetryErrorCode } from '../../errors/RetryError' ;
5+ import IClientContext from '../../contracts/IClientContext' ;
216
227export default abstract class BaseCommand {
238 protected client : TCLIService . Client ;
@@ -29,57 +14,23 @@ export default abstract class BaseCommand {
2914 this . context = context ;
3015 }
3116
32- protected executeCommand < Response > ( request : object , command : Function | void ) : Promise < Response > {
33- return this . invokeWithErrorHandling < Response > ( request , command , { startTime : Date . now ( ) , attempt : 0 } ) ;
34- }
35-
36- private async invokeWithErrorHandling < Response > (
37- request : object ,
38- command : Function | void ,
39- info : CommandExecutionInfo ,
40- ) : Promise < Response > {
17+ protected async executeCommand < Response > ( request : object , command : Function | void ) : Promise < Response > {
4118 try {
4219 return await this . invokeCommand < Response > ( request , command ) ;
4320 } catch ( error ) {
44- if ( error instanceof Thrift . TApplicationException ) {
45- if ( 'statusCode' in error ) {
46- switch ( error . statusCode ) {
47- // On these status codes it's safe to retry the request. However,
48- // both error codes mean that server is overwhelmed or even down.
49- // Therefore, we need to add some delay between attempts so
50- // server can recover and more likely handle next request
51- case 429 : // Too Many Requests
52- case 503 : // Service Unavailable
53- info . attempt += 1 ;
54-
55- const clientConfig = this . context . getConfig ( ) ;
56-
57- // Delay interval depends on current attempt - the more attempts we do
58- // the longer the interval will be
59- // TODO: Respect `Retry-After` header (PECO-729)
60- const retryDelay = getRetryDelay ( info . attempt , clientConfig ) ;
61-
62- const attemptsExceeded = info . attempt >= clientConfig . retryMaxAttempts ;
63- if ( attemptsExceeded ) {
64- throw new HiveDriverError (
65- `Hive driver: ${ error . statusCode } when connecting to resource. Max retry count exceeded.` ,
66- ) ;
67- }
68-
69- const timeoutExceeded = Date . now ( ) - info . startTime + retryDelay >= clientConfig . retriesTimeout ;
70- if ( timeoutExceeded ) {
71- throw new HiveDriverError (
72- `Hive driver: ${ error . statusCode } when connecting to resource. Retry timeout exceeded.` ,
73- ) ;
74- }
75-
76- await delay ( retryDelay ) ;
77- return this . invokeWithErrorHandling ( request , command , info ) ;
78-
79- // TODO: Here we should handle other error types (see PECO-730)
80-
81- // no default
82- }
21+ if ( error instanceof RetryError ) {
22+ const statusCode = error . payload instanceof Response ? error . payload . status : undefined ;
23+
24+ switch ( error . errorCode ) {
25+ case RetryErrorCode . AttemptsExceeded :
26+ throw new HiveDriverError (
27+ `Hive driver: ${ statusCode ?? 'Error' } when connecting to resource. Max retry count exceeded.` ,
28+ ) ;
29+ case RetryErrorCode . TimeoutExceeded :
30+ throw new HiveDriverError (
31+ `Hive driver: ${ statusCode ?? 'Error' } when connecting to resource. Retry timeout exceeded.` ,
32+ ) ;
33+ // no default
8334 }
8435 }
8536
0 commit comments