@terascope/elasticsearch-api 3.4.0 → 3.5.0

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/index.js CHANGED
@@ -278,7 +278,6 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
278
278
  */
279
279
  function _filterRetryRecords(actionRecords, result) {
280
280
  const retry = [];
281
- const deadLetter = [];
282
281
  const { items } = result;
283
282
 
284
283
  let nonRetriableError = false;
@@ -312,9 +311,10 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
312
311
  reason = `${item.error.type}--${item.error.reason}`;
313
312
 
314
313
  if (config._dead_letter_action === 'kafka_dead_letter') {
315
- deadLetter.push({ doc: actionRecords[i].data, reason });
314
+ actionRecords[i].data.setMetadata('_bulk_sender_rejection', reason);
316
315
  continue;
317
316
  }
317
+
318
318
  break;
319
319
  }
320
320
  } else if (item.status == null || item.status < 400) {
@@ -323,8 +323,11 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
323
323
  }
324
324
 
325
325
  if (nonRetriableError) {
326
+ // if dlq active still attempt the retries
327
+ const retryOnError = config._dead_letter_action === 'kafka_dead_letter' ? retry : [];
328
+
326
329
  return {
327
- retry: [], successful, error: true, reason, deadLetter
330
+ retry: retryOnError, successful, error: true, reason
328
331
  };
329
332
  }
330
333
 
@@ -337,7 +340,7 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
337
340
 
338
341
  /**
339
342
  * @param data {Array<{ action: data }>}
340
- * @returns {Promise<{ recordCount: number, deadLetter: record[] }>}
343
+ * @returns {Promise<number>}
341
344
  */
342
345
  async function _bulkSend(actionRecords, previousCount = 0, previousRetryDelay = 0) {
343
346
  const body = actionRecords.flatMap((record, index) => {
@@ -349,7 +352,18 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
349
352
  throw new Error(`Bulk send record is missing the action property${dbg}`);
350
353
  }
351
354
 
352
- if (!isElasticsearch6()) return _nonEs6Prep(record);
355
+ if (!isElasticsearch6()) {
356
+ const actionKey = getFirstKey(record.action);
357
+ const { _type, ...withoutTypeAction } = record.action[actionKey];
358
+ // if data is specified return both
359
+ return record.data ? [{
360
+ ...record.action,
361
+ [actionKey]: withoutTypeAction
362
+ }, record.data] : [{
363
+ ...record.action,
364
+ [actionKey]: withoutTypeAction
365
+ }];
366
+ }
353
367
 
354
368
  // if data is specified return both
355
369
  return record.data ? [record.action, record.data] : [record.action];
@@ -358,51 +372,41 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
358
372
  const response = await _clientRequest('bulk', { body });
359
373
  const results = response.body ? response.body : response;
360
374
 
361
- if (!results.errors) return { recordCount: _affectedRowsCount(results) };
375
+ if (!results.errors) {
376
+ return results.items.reduce((c, item) => {
377
+ const [value] = Object.values(item);
378
+ // ignore non-successful status codes
379
+ if (value.status != null && value.status >= 400) return c;
380
+ return c + 1;
381
+ }, 0);
382
+ }
362
383
 
363
384
  const {
364
- retry, successful, error, reason, deadLetter
385
+ retry, successful, error, reason
365
386
  } = _filterRetryRecords(actionRecords, results);
366
387
 
367
- if (error) {
368
- if (config._dead_letter_action === 'kafka_dead_letter') {
369
- return {
370
- recordCount: previousCount + successful,
371
- deadLetter
372
- };
373
- }
374
-
388
+ if (error && config._dead_letter_action !== 'kafka_dead_letter') {
375
389
  throw new Error(`bulk send error: ${reason}`);
376
390
  }
377
391
 
378
392
  if (retry.length === 0) {
379
- return { recordCount: previousCount + successful };
393
+ return previousCount + successful;
380
394
  }
381
395
 
382
- warning();
383
-
384
- const nextRetryDelay = await _awaitRetry(previousRetryDelay);
385
- return _bulkSend(retry, previousCount + successful, nextRetryDelay);
396
+ return _handleRetries(retry, previousCount + successful, previousRetryDelay);
386
397
  }
387
398
 
388
- function _nonEs6Prep(record) {
389
- const actionKey = getFirstKey(record.action);
390
-
391
- const { _type, ...withoutTypeAction } = record.action[actionKey];
392
- // if data is specified return both
393
-
394
- const body = [{ ...record.action, [actionKey]: withoutTypeAction }];
395
-
396
- if (record.data != null) body.push(record.data);
399
+ async function _handleRetries(retry, affectedCount, previousRetryDelay) {
400
+ warning();
397
401
 
398
- return body;
402
+ const nextRetryDelay = await _awaitRetry(previousRetryDelay);
403
+ return _bulkSend(retry, affectedCount, nextRetryDelay);
399
404
  }
400
405
 
401
406
  /**
402
407
  * The new and improved bulk send with proper retry support
403
408
  *
404
- * @returns {Promise<{ recordCount: number, deadLetter: record[] }>}
405
- * the number of affected rows and records for kafka dead letter queue
409
+ * @returns {Promise<number>} the number of affected rows
406
410
  */
407
411
  function bulkSend(data) {
408
412
  if (!Array.isArray(data)) {
@@ -412,15 +416,6 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
412
416
  return Promise.resolve(_bulkSend(data));
413
417
  }
414
418
 
415
- function _affectedRowsCount(results) {
416
- return results.items.reduce((c, item) => {
417
- const [value] = Object.values(item);
418
- // ignore non-successful status codes
419
- if (value.status != null && value.status >= 400) return c;
420
- return c + 1;
421
- }, 0);
422
- }
423
-
424
419
  function _warn(warnLogger, msg) {
425
420
  let _lastTime = null;
426
421
  return () => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@terascope/elasticsearch-api",
3
3
  "displayName": "Elasticsearch API",
4
- "version": "3.4.0",
4
+ "version": "3.5.0",
5
5
  "description": "Elasticsearch client api used across multiple services, handles retries and exponential backoff",
6
6
  "homepage": "https://github.com/terascope/teraslice/tree/master/packages/elasticsearch-api#readme",
7
7
  "bugs": {
package/test/api-spec.js CHANGED
@@ -768,7 +768,7 @@ describe('elasticsearch-api', () => {
768
768
  { delete: { _index: 'some_index', _type: 'events', _id: 5 } }
769
769
  ]
770
770
  });
771
- return expect(result).toEqual({ recordCount: 2 });
771
+ return expect(result).toBe(2);
772
772
  });
773
773
 
774
774
  it('can remove type from bulkSend', async () => {
@@ -877,7 +877,7 @@ describe('elasticsearch-api', () => {
877
877
  });
878
878
  const result = await api.bulkSend(myBulkData);
879
879
 
880
- expect(result).toEqual({ recordCount: 2 });
880
+ expect(result).toBe(2);
881
881
 
882
882
  bulkError = ['some_thing_else', 'some_thing_else'];
883
883
 
@@ -72,35 +72,48 @@ describe('bulkSend', () => {
72
72
  await cleanupIndex(client, index);
73
73
  });
74
74
 
75
- it('returns records that cannot be tried again if dlq config is set', async () => {
75
+ it('should send records if no issues and dlq not set', async () => {
76
+ const diffApi = elasticsearchAPI(client, logger);
77
+
78
+ const docs = cloneDeep(EvenDateData.data.slice(0, 2));
79
+
80
+ const result = await diffApi.bulkSend(formatUploadData(index, docs, isElasticsearch8));
81
+
82
+ expect(result).toBe(2);
83
+ });
84
+
85
+ it('should throw an error if dlq is not set', async () => {
86
+ const diffApi = elasticsearchAPI(client, logger);
87
+
76
88
  const docs = cloneDeep(EvenDateData.data.slice(0, 2));
77
89
 
78
90
  docs[0].bytes = 'this is a bad value';
79
91
 
80
- const result = await api.bulkSend(formatUploadData(index, docs, isElasticsearch8));
92
+ await expect(diffApi.bulkSend(formatUploadData(index, docs, isElasticsearch8)))
93
+ .rejects.toThrow();
94
+ });
95
+
96
+ it('should update metadata for records that are not retryable and return results for successful updates if dlq is set', async () => {
97
+ const docs = cloneDeep(EvenDateData.data.slice(0, 2))
98
+ .map((doc, i) => DataEntity.make(doc, { _key: i + 1 }));
81
99
 
82
- expect(result.recordCount).toBe(1);
100
+ docs[0].bytes = 'this is a bad value';
101
+
102
+ const result = await api.bulkSend(formatUploadData(index, docs, isElasticsearch8));
83
103
 
84
- expect(result.deadLetter[0].doc).toEqual(DataEntity.make({
85
- ip: '120.67.248.156',
86
- userAgent: 'Mozilla/5.0 (Windows; U; Windows NT 6.1) AppleWebKit/533.1.2 (KHTML, like Gecko) Chrome/35.0.894.0 Safari/533.1.2',
87
- url: 'http://lucious.biz',
88
- uuid: 'b23a8550-0081-453f-9e80-93a90782a5bd',
89
- created: '2019-04-26T15:00:23.225+00:00',
90
- ipv6: '9e79:7798:585a:b847:f1c4:81eb:0c3d:7eb8',
91
- location: '50.15003, -94.89355',
92
- bytes: 'this is a bad value'
93
- }));
104
+ // 1 good doc - so only 1 row affected
105
+ expect(result).toBe(1);
94
106
 
95
- expect(result.deadLetter[0].reason).toBeDefined();
107
+ expect(docs[0].getMetadata('_bulk_sender_rejection')).toInclude('mapper_parsing_exception--failed to parse field [bytes]');
108
+ expect(docs[1].getMetadata('_bulk_sender_rejection')).toBeUndefined();
96
109
  });
97
110
 
98
- it('should return a count if not un-retryable records', async () => {
111
+ it('should return a count if not un-retryable records if dlq is set', async () => {
99
112
  const docs = cloneDeep(EvenDateData.data.slice(0, 2));
100
113
 
101
114
  const result = await api.bulkSend(formatUploadData(index, docs, isElasticsearch8));
102
115
 
103
- expect(result).toEqual({ recordCount: 2 });
116
+ expect(result).toBe(2);
104
117
  });
105
118
  });
106
119
  });
package/types/index.d.ts CHANGED
@@ -32,9 +32,9 @@ declare namespace elasticsearchAPI {
32
32
  /**
33
33
  * The new and improved bulk send with proper retry support
34
34
  *
35
- * @returns the number of affected rows, and deadLetter records if config is set
35
+ * @returns the number of affected rows
36
36
  */
37
- bulkSend: (data: BulkRecord[]) => Promise<{ recordCount: number; deadLetter?: any[]; }>;
37
+ bulkSend: (data: BulkRecord[]) => Promise<number>;
38
38
  nodeInfo: (query: any) => Promise<any>;
39
39
  nodeStats: (query: any) => Promise<any>;
40
40
  buildQuery: (opConfig: Config, msg: any) => ClientParams.SearchParams;