elasticio-sailor-nodejs 3.0.0-sailor-proxy-dev1 → 3.0.0-sailor-proxy-dev2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,8 @@ const {
12
12
  HTTP2_HEADER_PATH,
13
13
  HTTP2_HEADER_METHOD,
14
14
  HTTP2_HEADER_AUTHORIZATION,
15
- HTTP2_HEADER_STATUS
15
+ HTTP2_HEADER_STATUS,
16
+ NGHTTP2_NO_ERROR
16
17
  } = http2.constants;
17
18
 
18
19
  const HEADER_ROUTING_KEY = 'x-eio-routing-key';
@@ -29,10 +30,12 @@ class ProxyClient {
29
30
  this.settings = settings;
30
31
  this._encryptor = new Encryptor(this.settings.MESSAGE_CRYPTO_PASSWORD, this.settings.MESSAGE_CRYPTO_IV);
31
32
  this.closed = true;
33
+ this.listeningForMessages = true;
32
34
  this.clientSession = null;
33
35
  this.reconnecting = false;
34
36
  this.reconnectAttempts = 0;
35
37
  this.reconnectTimer = null;
38
+ this.getMessageStreams = new Set();
36
39
 
37
40
  const username = settings.API_USERNAME;
38
41
  const password = settings.API_KEY;
@@ -100,24 +103,27 @@ class ProxyClient {
100
103
 
101
104
  // Handle connection close
102
105
  this.clientSession.on('close', () => {
103
- log.warn({ closed: this.closed, reconnecting: this.reconnecting }, 'HTTP2 session closed');
106
+ log.debug({ closed: this.closed, reconnecting: this.reconnecting }, 'HTTP2 session closed');
104
107
  if (!this.closed && !this.reconnecting) {
108
+ log.warn('HTTP2 session closed unexpectedly, initiating reconnection');
105
109
  this._handleDisconnection('close');
106
110
  }
107
111
  });
108
112
 
109
113
  // Handle GOAWAY frames (server-initiated shutdown)
110
114
  this.clientSession.on('goaway', (errorCode, lastStreamID, opaqueData) => {
111
- log.warn({ errorCode, lastStreamID, closed: this.closed }, 'Received GOAWAY from server');
115
+ log.debug({ errorCode, lastStreamID, closed: this.closed }, 'Received GOAWAY from server');
112
116
  if (!this.closed) {
117
+ log.warn('Received GOAWAY frame from server, initiating reconnection');
113
118
  this._handleDisconnection('goaway', { errorCode, lastStreamID });
114
119
  }
115
120
  });
116
121
 
117
122
  // Handle timeout
118
123
  this.clientSession.on('timeout', () => {
119
- log.warn({ closed: this.closed }, 'HTTP2 session timeout');
124
+ log.debug({ closed: this.closed }, 'HTTP2 session timeout');
120
125
  if (!this.closed) {
126
+ log.warn('HTTP2 session timeout, initiating reconnection');
121
127
  this._handleDisconnection('timeout');
122
128
  }
123
129
  });
@@ -192,11 +198,13 @@ class ProxyClient {
192
198
 
193
199
  // Clear any pending reconnection timers
194
200
  if (this.reconnectTimer) {
201
+ log.debug('Clearing pending reconnection timer');
195
202
  clearTimeout(this.reconnectTimer);
196
203
  this.reconnectTimer = null;
197
204
  }
198
205
 
199
206
  return new Promise((resolve) => {
207
+ log.debug('Disconnecting from Sailor Proxy');
200
208
  if (!this.clientSession || this.clientSession.destroyed) {
201
209
  log.debug('Session already destroyed');
202
210
  return resolve();
@@ -391,23 +399,27 @@ class ProxyClient {
391
399
  }
392
400
 
393
401
  async listenForMessages(messageHandler) {
394
- while (!this.closed) {
402
+ while (this.listeningForMessages) {
395
403
  try {
404
+ log.debug('Starting to listen for messages from proxy');
396
405
  await this._ensureConnection();
397
406
 
398
407
  const stepId = this.settings.STEP_ID;
399
408
  const prefetch = this.settings.PROXY_PREFETCH_SAILOR;
409
+ // TODO: When prefetch > 1, what if one message takes a long time to process - do we want to wait for it before requesting the next one?
400
410
  await Promise.all(new Array(prefetch).fill().map(async () => {
401
411
  const queryParams = new URLSearchParams({
402
412
  stepId,
403
413
  prefetch
404
414
  }).toString();
405
415
  log.info({ stepId, prefetch }, 'Requesting message from proxy');
416
+ // TODO: what timeout is here? e.g. when flow is realtime - we might need to wait a long time...
406
417
  const getMessageStream = this.clientSession.request({
407
418
  [HTTP2_HEADER_PATH]: `/message?${queryParams}`,
408
419
  [HTTP2_HEADER_METHOD]: 'GET',
409
420
  [HTTP2_HEADER_AUTHORIZATION]: this.authHeader
410
421
  });
422
+ this.getMessageStreams.add(getMessageStream);
411
423
 
412
424
  const { headers, body } = await new Promise((resolve, reject) => {
413
425
  getMessageStream.on('response', (headers, flags) => {
@@ -459,10 +471,30 @@ class ProxyClient {
459
471
  log.debug('Currently reconnecting, will retry listening for messages after reconnection');
460
472
  }
461
473
  await new Promise(resolve => setTimeout(resolve, 1000));
474
+ } finally {
475
+ log.debug('Cleaning up message streams');
476
+ this.getMessageStreams.clear();
462
477
  }
463
478
  }
464
479
  }
465
480
 
481
+ async stopListeningForMessages() {
482
+ log.info('Stopping listening for messages');
483
+ await Promise.all(Array.from(this.getMessageStreams).map(stream => {
484
+ if (stream.closed || stream.destroyed) {
485
+ log.debug({ closed: stream.closed, destroyed: stream.destroyed }, 'Message stream is already closed or destroyed');
486
+ return Promise.resolve();
487
+ }
488
+ return new Promise((resolve) => {
489
+ stream.close(NGHTTP2_NO_ERROR, () => {
490
+ log.debug('Closed message stream');
491
+ resolve();
492
+ });
493
+ });
494
+ }));
495
+ this.listeningForMessages = false;
496
+ }
497
+
466
498
  async sendMessage({
467
499
  incomingMessageId,
468
500
  type,
package/lib/sailor.js CHANGED
@@ -80,7 +80,6 @@ class Sailor {
80
80
  }
81
81
 
82
82
  async disconnect() {
83
- // TODO: delete if not needed (currently used only in old tests)
84
83
  log.debug('Disconnecting, %s messages in processing', this.messagesCount);
85
84
  return this.proxyClient.disconnect();
86
85
  }
@@ -171,10 +170,13 @@ class Sailor {
171
170
 
172
171
  async processMessageAndMaybeShutdownCallback(headers, body) {
173
172
  try {
174
- return await this.processMessage(headers, body);
173
+ await this.processMessage(headers, body);
175
174
  } catch (e) {
176
175
  log.error(e, 'Something very bad happened during message processing');
177
176
  } finally {
177
+ log.debug({
178
+ messagesCount: this.messagesCount
179
+ }, 'Finished processing message, checking if shutdownCallback should be called');
178
180
  if (this.shutdownCallback) {
179
181
  if (this.messagesCount === 0) {
180
182
  // there is no another processMessage invocation, so it's time to call shutdownCallback
@@ -195,7 +197,7 @@ class Sailor {
195
197
  return new Promise(resolve => this.shutdownCallback = resolve);
196
198
  }
197
199
 
198
- await this.proxyClient.disconnect();
200
+ await this.proxyClient.stopListeningForMessages();
199
201
  if (this.messagesCount === 0) {
200
202
  // there is no unfinished processMessage invocation, let's just resolve scheduleShutdown now
201
203
  log.debug('scheduleShutdown – about to shutdown immediately');
@@ -443,7 +445,7 @@ class Sailor {
443
445
  }
444
446
  }
445
447
 
446
- function onEnd() {
448
+ async function onEnd() {
447
449
  if (endWasEmitted) {
448
450
  logger.warn({
449
451
  messagesCount: that.messagesCount,
@@ -455,7 +457,7 @@ class Sailor {
455
457
 
456
458
  endWasEmitted = true;
457
459
 
458
- that.proxyClient.finishProcessing(
460
+ await that.proxyClient.finishProcessing(
459
461
  incomingMessageHeaders,
460
462
  taskExec.errorCount > 0
461
463
  ? MESSAGE_PROCESSING_STATUS.ERROR
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "elasticio-sailor-nodejs",
3
3
  "description": "The official elastic.io library for bootstrapping and executing for Node.js connectors",
4
- "version": "3.0.0-sailor-proxy-dev1",
4
+ "version": "3.0.0-sailor-proxy-dev2",
5
5
  "main": "run.js",
6
6
  "scripts": {
7
7
  "build": "tsc",
package/run.js CHANGED
@@ -74,8 +74,10 @@ async function gracefulShutdown() {
74
74
  logger.trace('Waited an init before graceful shutdown');
75
75
 
76
76
  try {
77
- logger.info('Disconnecting...');
77
+ logger.info('Scheduling shutdown...');
78
78
  await sailor.scheduleShutdown();
79
+ logger.info('Finished shutdown. Disconnecting...');
80
+ await sailor.disconnect();
79
81
  logger.info('Successfully disconnected');
80
82
  process.exit();
81
83
  } catch (err) {
@@ -88,7 +90,6 @@ async function run(settings, ipc) {
88
90
  prepareSandbox();
89
91
  try {
90
92
  await putOutToSea(settings, ipc);
91
- logger.info('Fully initialized and waiting for messages');
92
93
  } catch (e) {
93
94
  if (sailor && !sailor.isConnected()) {
94
95
  await sailor.reportError(e);
@@ -97,6 +98,21 @@ async function run(settings, ipc) {
97
98
  }
98
99
  }
99
100
 
101
+ function addProcessListeners() {
102
+ process.on('SIGTERM', function onSigterm() {
103
+ logger.info('Received SIGTERM');
104
+ gracefulShutdown();
105
+ });
106
+
107
+ process.on('SIGINT', function onSigint() {
108
+ logger.info('Received SIGINT');
109
+ gracefulShutdown();
110
+ });
111
+
112
+ process.on('uncaughtException', logger.criticalErrorAndExit.bind(logger, 'process.uncaughtException'));
113
+ process.on('unhandledRejection', (err) => logger.error(err, 'process.unhandledRejection'));
114
+ }
115
+
100
116
  exports.__test__ = {
101
117
  disconnectOnly: function disconnectOnly() {
102
118
  if (!disconnectRequired) {
@@ -110,22 +126,10 @@ exports.__test__ = {
110
126
  };
111
127
  exports.run = run;
112
128
  exports.putOutToSea = putOutToSea;
129
+ exports.addProcessListeners = addProcessListeners;
113
130
 
114
131
  if (require.main === module || process.mainModule.filename === __filename) {
115
- process.on('SIGTERM', function onSigterm() {
116
- logger.info('Received SIGTERM');
117
- gracefulShutdown();
118
- });
119
-
120
- process.on('SIGINT', function onSigint() {
121
- logger.info('Received SIGINT');
122
- gracefulShutdown();
123
- });
124
-
125
- process.on('uncaughtException', logger.criticalErrorAndExit.bind(logger, 'process.uncaughtException'));
126
- process.on('unhandledRejection', (err) => logger.error(err, 'process.unhandledRejection'));
127
-
132
+ addProcessListeners();
128
133
  const ipc = new IPC();
129
-
130
134
  run(settings.readFrom(process.env), ipc);
131
135
  }
package/run.local.js CHANGED
@@ -8,7 +8,9 @@ function setEnvVars() {
8
8
  setEnvVars();
9
9
 
10
10
  const { IPC } = require('./lib/ipc');
11
- const { run } = require('./run');
11
+ const { addProcessListeners, run } = require('./run');
12
12
  const settings = require('./lib/settings.js');
13
13
  const ipc = new IPC();
14
+
15
+ addProcessListeners();
14
16
  run(settings.readFrom(process.env), ipc);
package/mise.toml DELETED
@@ -1,2 +0,0 @@
1
- [tools]
2
- node = "18.16.1"