elasticio-sailor-nodejs 3.0.0-dev2 → 3.0.0-dev4
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.
- package/config/local.json +4 -4
- package/lib/proxy-client.js +157 -59
- package/lib/sailor.js +2 -0
- package/lib/settings.js +4 -0
- package/package.json +1 -1
package/config/local.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"LOG_LEVEL": "trace",
|
|
3
|
-
"ELASTICIO_FLOW_ID": "
|
|
3
|
+
"ELASTICIO_FLOW_ID": "697b56aca8e2e9b35e1952da",
|
|
4
4
|
"ELASTICIO_EXEC_ID": "exec_67890",
|
|
5
5
|
"ELASTICIO_STEP_ID": "step_1",
|
|
6
6
|
"ELASTICIO_CONTAINER_ID": "container_abcde",
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"ELASTICIO_COMP_ID": "component_3344",
|
|
10
10
|
"ELASTICIO_FUNCTION": "data_trigger",
|
|
11
11
|
"ELASTICIO_API_URI": "http://localhost:9000",
|
|
12
|
-
"ELASTICIO_API_USERNAME": "task-
|
|
13
|
-
"ELASTICIO_API_KEY": "
|
|
12
|
+
"ELASTICIO_API_USERNAME": "task-697b56aca8e2e9b35e1952da",
|
|
13
|
+
"ELASTICIO_API_KEY": "9b2e0465-b00c-4b57-af91-bfc3a830306c",
|
|
14
14
|
"ELASTICIO_MESSAGE_CRYPTO_IV": "0.03091345790184",
|
|
15
15
|
"ELASTICIO_MESSAGE_CRYPTO_PASSWORD": "password",
|
|
16
|
-
"ELASTICIO_SAILOR_PROXY_URI": "http://localhost:
|
|
16
|
+
"ELASTICIO_SAILOR_PROXY_URI": "http://localhost:4001",
|
|
17
17
|
"ELASTICIO_COMPONENT_PATH": "./spec/component",
|
|
18
18
|
"ELASTICIO_EMIT_LIGHTWEIGHT_MESSAGE": "true"
|
|
19
19
|
}
|
package/lib/proxy-client.js
CHANGED
|
@@ -262,34 +262,74 @@ class ProxyClient {
|
|
|
262
262
|
|
|
263
263
|
logger.info('Going to fetch message body.', { objectId });
|
|
264
264
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
265
|
+
const maxRetries = this.settings.PROXY_OBJECT_REQUEST_RETRY_ATTEMPTS;
|
|
266
|
+
const retryDelay = this.settings.PROXY_OBJECT_REQUEST_RETRY_DELAY;
|
|
267
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
268
|
+
try {
|
|
269
|
+
object = await new Promise((resolve, reject) => {
|
|
270
|
+
const getObjectStream = this.clientSession.request({
|
|
271
|
+
[HTTP2_HEADER_PATH]: `/object/${objectId}`,
|
|
272
|
+
[HTTP2_HEADER_METHOD]: 'GET',
|
|
273
|
+
[HTTP2_HEADER_AUTHORIZATION]: this.authHeader
|
|
274
|
+
}).pipe(this._encryptor.createDecipher());
|
|
275
|
+
|
|
276
|
+
const chunks = [];
|
|
277
|
+
getObjectStream.on('data', chunk => {
|
|
278
|
+
chunks.push(chunk);
|
|
279
|
+
});
|
|
280
|
+
getObjectStream.on('error', (err) => {
|
|
281
|
+
logger.error(err, 'Error during fetching message body');
|
|
282
|
+
reject(err);
|
|
283
|
+
});
|
|
284
|
+
getObjectStream.on('end', () => {
|
|
285
|
+
logger.info('Message stream ended by server');
|
|
286
|
+
const buffer = Buffer.concat(chunks);
|
|
287
|
+
logger.info({ messageSize: buffer.length }, 'Received complete message from server');
|
|
288
|
+
resolve({ data: JSON.parse(buffer.toString()) });
|
|
289
|
+
});
|
|
286
290
|
});
|
|
287
|
-
});
|
|
288
|
-
} catch (e) {
|
|
289
|
-
log.error(e);
|
|
290
|
-
throw new Error(`Failed to get message body with id=${objectId}`);
|
|
291
|
-
}
|
|
292
291
|
|
|
292
|
+
if (attempt > 0) {
|
|
293
|
+
log.info({ attempt, maxRetries }, 'Fetch message body succeeded after retry');
|
|
294
|
+
}
|
|
295
|
+
return objectId;
|
|
296
|
+
} catch (error) {
|
|
297
|
+
const isLastAttempt = attempt === maxRetries;
|
|
298
|
+
const isRetryable = error.isNetworkError ||
|
|
299
|
+
(error.statusCode && error.statusCode >= 500) ||
|
|
300
|
+
error.code === 'ECONNRESET' ||
|
|
301
|
+
error.code === 'ETIMEDOUT';
|
|
302
|
+
|
|
303
|
+
if (!isRetryable || isLastAttempt) {
|
|
304
|
+
log.error({
|
|
305
|
+
attempt,
|
|
306
|
+
maxRetries,
|
|
307
|
+
error: error.message,
|
|
308
|
+
isRetryable
|
|
309
|
+
}, 'Fetch message body failed, no more retries');
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const delay = Math.min(
|
|
314
|
+
retryDelay * Math.pow(2, attempt),
|
|
315
|
+
this.settings.PROXY_OBJECT_REQUEST_MAX_RETRY_DELAY
|
|
316
|
+
);
|
|
317
|
+
log.warn({
|
|
318
|
+
attempt,
|
|
319
|
+
maxRetries,
|
|
320
|
+
error: error.message,
|
|
321
|
+
nextRetryIn: delay
|
|
322
|
+
}, 'Fetch message body failed, retrying...');
|
|
323
|
+
|
|
324
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
325
|
+
|
|
326
|
+
// Ensure connection is still valid before retry
|
|
327
|
+
if (!this.isConnected()) {
|
|
328
|
+
log.info('Reconnecting before retry...');
|
|
329
|
+
await this.connect();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
293
333
|
logger.info('Successfully obtained message body.', { objectId });
|
|
294
334
|
logger.trace('Message body object received');
|
|
295
335
|
|
|
@@ -299,43 +339,101 @@ class ProxyClient {
|
|
|
299
339
|
async uploadMessageBody(bodyBuf) {
|
|
300
340
|
await this._ensureConnection();
|
|
301
341
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
342
|
+
const maxRetries = this.settings.PROXY_OBJECT_REQUEST_RETRY_ATTEMPTS;
|
|
343
|
+
const retryDelay = this.settings.PROXY_OBJECT_REQUEST_RETRY_DELAY;
|
|
344
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
345
|
+
try {
|
|
346
|
+
const objectId = await new Promise((resolve, reject) => {
|
|
347
|
+
const postMessageStream = this.clientSession.request({
|
|
348
|
+
[HTTP2_HEADER_PATH]: '/object',
|
|
349
|
+
[HTTP2_HEADER_METHOD]: 'POST',
|
|
350
|
+
[HTTP2_HEADER_AUTHORIZATION]: this.authHeader
|
|
351
|
+
});
|
|
309
352
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
353
|
+
let responseData = '';
|
|
354
|
+
let statusCode = null;
|
|
355
|
+
|
|
356
|
+
postMessageStream.on('response', (headers, flags) => {
|
|
357
|
+
statusCode = headers[http2.constants.HTTP2_HEADER_STATUS];
|
|
358
|
+
if (statusCode !== 200) {
|
|
359
|
+
const error = new Error(`Failed to upload message body, status code: ${statusCode}`);
|
|
360
|
+
error.statusCode = statusCode;
|
|
361
|
+
return reject(error);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
postMessageStream.on('data', chunk => {
|
|
366
|
+
responseData += chunk;
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
postMessageStream.on('error', (err) => {
|
|
370
|
+
log.error(err, 'Error during upload message body');
|
|
371
|
+
err.isNetworkError = true;
|
|
372
|
+
reject(err);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
postMessageStream.on('end', () => {
|
|
376
|
+
if (!responseData) {
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
const responseJson = JSON.parse(responseData);
|
|
381
|
+
resolve(responseJson.objectId);
|
|
382
|
+
} catch (e) {
|
|
383
|
+
log.error(e, 'Failed to parse upload message body response');
|
|
384
|
+
reject(e);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
const cipher = this._encryptor.createCipher();
|
|
389
|
+
cipher.pipe(postMessageStream);
|
|
390
|
+
cipher.write(bodyBuf);
|
|
391
|
+
cipher.end();
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// Success - return the objectId
|
|
395
|
+
if (attempt > 0) {
|
|
396
|
+
log.info({ attempt, maxRetries }, 'Upload message body succeeded after retry');
|
|
314
397
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
398
|
+
return objectId;
|
|
399
|
+
|
|
400
|
+
} catch (error) {
|
|
401
|
+
const isLastAttempt = attempt === maxRetries;
|
|
402
|
+
const isRetryable = error.isNetworkError ||
|
|
403
|
+
(error.statusCode && error.statusCode >= 500) ||
|
|
404
|
+
error.code === 'ECONNRESET' ||
|
|
405
|
+
error.code === 'ETIMEDOUT';
|
|
406
|
+
|
|
407
|
+
if (!isRetryable || isLastAttempt) {
|
|
408
|
+
log.error({
|
|
409
|
+
attempt,
|
|
410
|
+
maxRetries,
|
|
411
|
+
error: error.message,
|
|
412
|
+
isRetryable
|
|
413
|
+
}, 'Upload message body failed, no more retries');
|
|
414
|
+
throw error;
|
|
331
415
|
}
|
|
332
|
-
});
|
|
333
416
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
417
|
+
const delay = Math.min(
|
|
418
|
+
retryDelay * Math.pow(2, attempt),
|
|
419
|
+
this.settings.PROXY_OBJECT_REQUEST_MAX_RETRY_DELAY
|
|
420
|
+
);
|
|
421
|
+
log.warn({
|
|
422
|
+
attempt,
|
|
423
|
+
maxRetries,
|
|
424
|
+
error: error.message,
|
|
425
|
+
nextRetryIn: delay
|
|
426
|
+
}, 'Upload message body failed, retrying...');
|
|
427
|
+
|
|
428
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
429
|
+
|
|
430
|
+
// Ensure connection is still valid before retry
|
|
431
|
+
if (!this.isConnected()) {
|
|
432
|
+
log.info('Reconnecting before retry...');
|
|
433
|
+
await this.connect();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
339
437
|
}
|
|
340
438
|
|
|
341
439
|
async listenForMessages(messageHandler) {
|
package/lib/sailor.js
CHANGED
|
@@ -35,6 +35,8 @@ class Sailor {
|
|
|
35
35
|
//eslint-disable-next-line new-cap
|
|
36
36
|
this.apiClient = RestApiClient(
|
|
37
37
|
settings.API_USERNAME,
|
|
38
|
+
// TODO find a way to make username and key consistent (without running api tests and looking up
|
|
39
|
+
// correct values in MongoDB)
|
|
38
40
|
settings.API_KEY,
|
|
39
41
|
{
|
|
40
42
|
retryCount: settings.API_REQUEST_RETRY_ATTEMPTS,
|
package/lib/settings.js
CHANGED
|
@@ -15,6 +15,9 @@ function getOptionalEnvVars(envVars) {
|
|
|
15
15
|
PROXY_RECONNECT_MAX_DELAY: 30 * 1000, // 30 seconds
|
|
16
16
|
PROXY_RECONNECT_BACKOFF_MULTIPLIER: 2,
|
|
17
17
|
PROXY_RECONNECT_JITTER_FACTOR: 0.3,
|
|
18
|
+
PROXY_OBJECT_REQUEST_RETRY_ATTEMPTS: Infinity,
|
|
19
|
+
PROXY_OBJECT_REQUEST_RETRY_DELAY: 100,
|
|
20
|
+
PROXY_OBJECT_REQUEST_MAX_RETRY_DELAY: 5 * 60 * 1000, // 5 mins
|
|
18
21
|
|
|
19
22
|
// TODO: Move to proxy?
|
|
20
23
|
DATA_RATE_LIMIT: 10, // 10 data events every 100ms
|
|
@@ -28,6 +31,7 @@ function getOptionalEnvVars(envVars) {
|
|
|
28
31
|
// Should be defaulted to true and moved to proxy
|
|
29
32
|
AMQP_PERSISTENT_MESSAGES: false,
|
|
30
33
|
|
|
34
|
+
OBJECT_STORAGE_SIZE_THRESHOLD: 1048576,
|
|
31
35
|
OUTGOING_MESSAGE_SIZE_LIMIT: 10485760,
|
|
32
36
|
NO_SELF_PASSTRHOUGH: false,
|
|
33
37
|
PROTOCOL_VERSION: 1,
|
package/package.json
CHANGED