@@ -28,6 +28,7 @@ export type MBConsumer = {
28
28
export class MessageBus {
29
29
#client: Kafka ;
30
30
#producer: Producer | undefined ;
31
+ #producerConnectingPromise: Promise < void > | undefined ;
31
32
#consumers: Record < string , MBConsumer | undefined > ;
32
33
#shutdown = false ;
33
34
logger : Logger ;
@@ -39,16 +40,23 @@ export class MessageBus {
39
40
brokers : options . connection . split ( "," ) ,
40
41
sasl : options . saslConfig ,
41
42
logCreator : ( ) => {
42
- return ( { log } ) => {
43
+ return ( { log, label } ) => {
43
44
const { message, error, stack, retryCount } = log ;
44
- if ( stack ) this . logger . error ( `Kafka: ${ stack } ` ) ;
45
- else if ( error ) this . logger . error ( `Kafka: ${ error } (message: ${ message } )` ) ;
46
- else if ( retryCount ) this . logger . warn ( `Kafka: ${ message } (retrying ${ retryCount } ...)` ) ;
47
- else this . logger . info ( `Kafka: ${ message } ` ) ;
45
+ if ( stack ) {
46
+ this . logger . error ( `Kafka: ${ stack } ` ) ;
47
+ } else if ( error ) {
48
+ this . logger . error ( `Kafka: ${ error } (message: ${ message } )` ) ;
49
+ } else if ( retryCount ) {
50
+ this . logger . warn ( `Kafka: ${ message } (retrying ${ retryCount } ...)` ) ;
51
+ } else {
52
+ this . logger . info ( `Kafka[${ label } ]: ${ message } ` ) ;
53
+ }
48
54
} ;
49
55
} ,
50
56
} ;
51
- if ( options . ssl ) kafkaConfig . ssl = true ;
57
+ if ( options . ssl ) {
58
+ kafkaConfig . ssl = true ;
59
+ }
52
60
53
61
this . #client = new Kafka ( kafkaConfig ) ;
54
62
this . #consumers = { } ;
@@ -69,9 +77,12 @@ export class MessageBus {
69
77
) ;
70
78
}
71
79
72
- private async connectProducer ( ) {
73
- this . #producer = this . #client. producer ( ) ;
74
- await this . #producer. connect ( ) ;
80
+ private async ensureProducerConnected ( ) {
81
+ if ( ! this . #producerConnectingPromise) {
82
+ this . #producer = this . #client. producer ( ) ;
83
+ this . #producerConnectingPromise = this . #producer. connect ( ) ;
84
+ }
85
+ await this . #producerConnectingPromise;
75
86
}
76
87
77
88
private async connectConsumer ( groupId : string ) {
@@ -83,13 +94,16 @@ export class MessageBus {
83
94
}
84
95
85
96
async publish < T > ( topic : string , ...messages : T [ ] ) {
86
- if ( this . #shutdown) throw new Error ( "MessageBus is already shutdown" ) ;
97
+ if ( this . #shutdown) {
98
+ throw new Error ( "MessageBus is already shutdown" ) ;
99
+ }
87
100
88
- if ( ! this . #producer ) await this . connectProducer ( ) ;
101
+ await this . ensureProducerConnected ( ) ;
89
102
90
103
const kafkaMessages : Message [ ] = [ ] ;
91
- for ( const m of messages )
92
- kafkaMessages . push ( { value : Buffer . from ( BSON . serialize ( { data : m } ) ) } ) ; // double check
104
+ for ( const m of messages ) {
105
+ kafkaMessages . push ( { value : Buffer . from ( BSON . serialize ( { data : m } ) ) } ) ;
106
+ } // double check
93
107
94
108
await this . #producer! . send ( { topic, messages : kafkaMessages } ) ;
95
109
}
@@ -101,7 +115,9 @@ export class MessageBus {
101
115
cb : ( ( event : T ) => Promise < void > ) | ( ( event : T ) => void )
102
116
) {
103
117
this . logger . info ( `Kafka (sub): connecting to consumer group ${ groupId } ` ) ;
104
- if ( ! this . #consumers[ groupId ] ) await this . connectConsumer ( groupId ) ;
118
+ if ( ! this . #consumers[ groupId ] ) {
119
+ await this . connectConsumer ( groupId ) ;
120
+ }
105
121
106
122
this . logger . info ( `Kafka (sub): subscribing to topic ${ topic } ` ) ;
107
123
await this . #consumers[ groupId ] ! . consumer . subscribe ( {
@@ -119,7 +135,11 @@ export class MessageBus {
119
135
// eslint-disable-next-line @typescript-eslint/no-misused-promises
120
136
this . #consumers[ groupId ] ! . consumer . on ( "consumer.crash" , async ( payload ) => {
121
137
if ( this . shouldCrash ( payload . payload . error ) ) {
122
- this . logger . error ( `FATAL: not recoverable error: ${ JSON . stringify ( payload . payload . error ) } ` ) ;
138
+ this . logger . error (
139
+ `FATAL: not recoverable error: ${ JSON . stringify (
140
+ payload . payload . error
141
+ ) } `
142
+ ) ;
123
143
process . kill ( process . pid , "SIGTERM" ) ;
124
144
return ;
125
145
}
@@ -148,8 +168,9 @@ export class MessageBus {
148
168
}
149
169
150
170
async runConsumer ( groupId : string ) {
151
- if ( ! this . #consumers[ groupId ] )
171
+ if ( ! this . #consumers[ groupId ] ) {
152
172
throw new Error ( `No consumer exists with groupId: ${ groupId } ` ) ;
173
+ }
153
174
154
175
this . nonRetriableWrapper ( groupId ) ;
155
176
@@ -171,8 +192,10 @@ export class MessageBus {
171
192
`Provided callback for topic ${ topic } failed: ${ e . stack } `
172
193
) ;
173
194
} else {
174
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
175
- this . logger . warn ( `Provided callback for topic ${ topic } failed with non-Error: ${ e } ` ) ;
195
+ this . logger . warn (
196
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
197
+ `Provided callback for topic ${ topic } failed with non-Error: ${ e } `
198
+ ) ;
176
199
}
177
200
}
178
201
}
@@ -183,7 +206,8 @@ export class MessageBus {
183
206
async disconnect ( ) {
184
207
this . #shutdown = true ;
185
208
await this . #producer?. disconnect ( ) ;
186
- for ( const consumer of Object . values ( this . #consumers) )
209
+ for ( const consumer of Object . values ( this . #consumers) ) {
187
210
await consumer ?. consumer . disconnect ( ) ;
211
+ }
188
212
}
189
213
}
0 commit comments