@@ -5,7 +5,7 @@ import { Call } from './Call.js';
5
5
import { simplifyEvent } from './minimizer.js' ;
6
6
import { v4 as uuidv4 } from 'uuid' ;
7
7
import { openSync , writeSync } from "node:fs" ;
8
- import { CompressionTypes , Kafka , KafkaConfig } from 'kafkajs' ;
8
+ import { CompressionTypes , Kafka , KafkaConfig , Partitioners , Producer } from 'kafkajs' ;
9
9
10
10
enum bucketTypes {
11
11
HOUR = 'HOUR' ,
@@ -47,7 +47,7 @@ export interface Environment {
47
47
KAFKA_HOST : string | undefined ;
48
48
KAFKA_USER : string | undefined ;
49
49
KAFKA_PASSWORD : string | undefined ;
50
- KAFKA_TOPIC : string | undefined ;
50
+ KAFKA_TOPIC : string ;
51
51
KAFKA_CLIENT_ID : string | undefined ;
52
52
}
53
53
@@ -68,6 +68,7 @@ export const errorCodeMappings: { [id: string] : string; } = {
68
68
'1013' : 'Try Again Later' ,
69
69
'1014' : 'Bad Gateway' ,
70
70
'1015' : 'TLS Handshake' ,
71
+ '4408' : 'Connection initialisation timeout' ,
71
72
'4409' : 'Too many requests' ,
72
73
} ;
73
74
@@ -92,7 +93,7 @@ export class GsClient {
92
93
private client : Client | undefined ;
93
94
private client_id : any = undefined ;
94
95
private kafka : Kafka | undefined ;
95
- private kafkaProducer : any | undefined ;
96
+ private kafkaProducer : Producer | undefined ;
96
97
97
98
public constructor ( env : Environment , wsUrl : string , oauthUrl : string , oauthOptions : OAuthOptions , call : Call ) {
98
99
this . offset = env . OFFSET ;
@@ -110,50 +111,55 @@ export class GsClient {
110
111
this . json_file = openSync ( './events.json' , 'w' ) ;
111
112
112
113
if ( this . env . KAFKA_ENABLED && this . env . KAFKA_HOST ) {
114
+ log . debug ( 'Setting up Kafka connection' )
113
115
let kafkaConfig : KafkaConfig = {
114
116
brokers : this . env . KAFKA_HOST . split ( ',' ) ,
115
- clientId : 'ohip-shovel'
117
+ clientId :this . env . KAFKA_CLIENT_ID ,
118
+ connectionTimeout : 30000 ,
116
119
} ;
117
120
if ( this . env . KAFKA_USER !== undefined && this . env . KAFKA_PASSWORD !== undefined ) {
118
121
kafkaConfig [ 'sasl' ] = {
119
122
mechanism : 'plain' ,
120
- username : this . env . KAFKA_USER ,
121
- password : this . env . KAFKA_PASSWORD ,
123
+ username : this . env . KAFKA_USER ,
124
+ password : this . env . KAFKA_PASSWORD ,
122
125
}
123
126
}
124
127
this . kafka = new Kafka ( kafkaConfig )
125
128
this . kafkaProducer = this . getProducer ( this . kafka ) ;
129
+ log . debug ( `Kafka configured to write to topic ${ this . env . KAFKA_TOPIC } ` )
126
130
}
127
131
this . registerShutdownHook ( ) ; // make sure to dispose and terminate the client on shutdown
128
132
}
129
133
130
- public getProducer ( kafka : Kafka ) : any {
134
+ public getProducer ( kafka : Kafka ) : Producer {
131
135
let kafkaProducer = kafka . producer ( {
132
136
allowAutoTopicCreation : true ,
133
137
transactionTimeout : 30000 ,
134
- maxInFlightRequests : 1
138
+ maxInFlightRequests : 1 ,
139
+ createPartitioner : Partitioners . LegacyPartitioner ,
135
140
} )
136
- kafkaProducer . connect ( )
137
141
return kafkaProducer
138
142
}
139
143
140
- public registerShutdownHook ( ) : void {
141
- process . on ( 'SIGINT' , ( ) => {
144
+ public async registerShutdownHook ( ) : Promise < void > {
145
+ process . on ( 'SIGINT' , async ( ) => {
142
146
log . info ( 'Received SIGINT signal' ) ;
143
- this . terminateClient ( 'SIGINT' ) ;
147
+ await this . terminateClient ( 'SIGINT' ) ;
144
148
setTimeout ( process . exit ( 0 ) , 2000 ) ;
145
149
} ) ;
146
- process . on ( 'SIGTERM' , ( ) => {
150
+ process . on ( 'SIGTERM' , async ( ) => {
147
151
log . info ( 'Received SIGTERM signal' ) ;
148
- this . terminateClient ( 'SIGTERM' ) ;
152
+ await this . terminateClient ( 'SIGTERM' ) ;
149
153
setTimeout ( process . exit ( 0 ) , 2000 ) ;
150
154
} ) ;
151
155
}
152
156
153
- public terminateClient ( reason : string ) : void {
157
+ public async terminateClient ( reason : string ) : Promise < void > {
154
158
log . info ( `Terminating client: ${ reason } ` )
155
159
if ( this . kafkaProducer !== undefined ) {
156
- this . kafkaProducer . disconnect ( ) ;
160
+ log . info ( 'Disconnecting from Kafka' )
161
+ await this . kafkaProducer . disconnect ( ) ;
162
+ log . info ( 'Disconnected from Kafka' )
157
163
}
158
164
if ( this . offset !== undefined ) {
159
165
log . info ( `Last offset processed: ${ this . offset } ` ) ;
@@ -311,7 +317,7 @@ export class GsClient {
311
317
}
312
318
313
319
private setStat ( eventName : string ) : void {
314
- this . windowCount = this . windowCount + 1 ;
320
+ this . windowCount = this . windowCount + 1 ;
315
321
// total events per event type
316
322
if ( ! this . statsSummary [ eventName ] ) {
317
323
this . statsSummary [ eventName ] = 1 ;
@@ -355,16 +361,17 @@ export class GsClient {
355
361
}
356
362
357
363
public async startConsuming ( reconnect : boolean = false , reason : string = '' ) {
358
- this . terminateClient ( `Refreshing connection with new token` ) ;
364
+ await this . terminateClient ( `Refreshing connection with new token` ) ;
359
365
if ( reconnect ) {
360
366
log . debug ( `Refreshing an existing connection in ${ this . env . TIMER } ms (${ reason } )` ) ;
361
367
await this . delay ( this . env . TIMER ) ;
362
368
} else {
363
369
log . debug ( 'Initiating a new connection' ) ;
364
370
}
365
371
this . client = this . getClient ( ) ;
366
- if ( this . kafka )
367
- this . kafkaProducer = this . getProducer ( this . kafka ) ;
372
+ if ( this . kafkaProducer !== undefined )
373
+ await this . kafkaProducer . connect ( )
374
+
368
375
try {
369
376
await this . subscribe ( this . client ) ;
370
377
} catch ( error ) {
@@ -376,7 +383,9 @@ export class GsClient {
376
383
377
384
public async stopConsuming ( reconnect : boolean = false ) {
378
385
try {
379
- if ( ! reconnect ) this . terminateClient ( 'Application stopped by user' ) ;
386
+ if ( ! reconnect ) {
387
+ await this . terminateClient ( 'Application stopped by user' ) ;
388
+ }
380
389
process . exit ( 0 ) ;
381
390
} catch ( error ) {
382
391
log . error ( error ) ;
0 commit comments