@terascope/elasticsearch-api 3.3.7 → 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
@@ -309,6 +309,12 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
309
309
  ) {
310
310
  nonRetriableError = true;
311
311
  reason = `${item.error.type}--${item.error.reason}`;
312
+
313
+ if (config._dead_letter_action === 'kafka_dead_letter') {
314
+ actionRecords[i].data.setMetadata('_bulk_sender_rejection', reason);
315
+ continue;
316
+ }
317
+
312
318
  break;
313
319
  }
314
320
  } else if (item.status == null || item.status < 400) {
@@ -317,8 +323,11 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
317
323
  }
318
324
 
319
325
  if (nonRetriableError) {
326
+ // if dlq active still attempt the retries
327
+ const retryOnError = config._dead_letter_action === 'kafka_dead_letter' ? retry : [];
328
+
320
329
  return {
321
- retry: [], successful, error: true, reason
330
+ retry: retryOnError, successful, error: true, reason
322
331
  };
323
332
  }
324
333
 
@@ -376,7 +385,7 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
376
385
  retry, successful, error, reason
377
386
  } = _filterRetryRecords(actionRecords, results);
378
387
 
379
- if (error) {
388
+ if (error && config._dead_letter_action !== 'kafka_dead_letter') {
380
389
  throw new Error(`bulk send error: ${reason}`);
381
390
  }
382
391
 
@@ -384,10 +393,14 @@ module.exports = function elasticsearchApi(client, logger, _opConfig) {
384
393
  return previousCount + successful;
385
394
  }
386
395
 
396
+ return _handleRetries(retry, previousCount + successful, previousRetryDelay);
397
+ }
398
+
399
+ async function _handleRetries(retry, affectedCount, previousRetryDelay) {
387
400
  warning();
388
401
 
389
402
  const nextRetryDelay = await _awaitRetry(previousRetryDelay);
390
- return _bulkSend(retry, previousCount + successful, nextRetryDelay);
403
+ return _bulkSend(retry, affectedCount, nextRetryDelay);
391
404
  }
392
405
 
393
406
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@terascope/elasticsearch-api",
3
3
  "displayName": "Elasticsearch API",
4
- "version": "3.3.7",
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": {
@@ -0,0 +1,119 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ debugLogger,
5
+ cloneDeep,
6
+ DataEntity
7
+ } = require('@terascope/utils');
8
+ const { ElasticsearchTestHelpers } = require('elasticsearch-store');
9
+ const elasticsearchAPI = require('../index');
10
+
11
+ const {
12
+ makeClient, cleanupIndex,
13
+ EvenDateData, TEST_INDEX_PREFIX,
14
+ createMappingFromDatatype
15
+ } = ElasticsearchTestHelpers;
16
+
17
+ jest.setTimeout(10000);
18
+
19
+ function formatUploadData(
20
+ index, data, isES8ClientTest = false
21
+ ) {
22
+ const results = [];
23
+
24
+ data.forEach((record, i) => {
25
+ const meta = { _index: index, _id: i + 1 };
26
+
27
+ if (!isES8ClientTest) {
28
+ meta._type = '_doc';
29
+ }
30
+
31
+ results.push({ action: { index: meta }, data: record });
32
+ });
33
+
34
+ return results;
35
+ }
36
+
37
+ describe('bulkSend', () => {
38
+ let client;
39
+ let api;
40
+ let isElasticsearch8 = false;
41
+
42
+ beforeAll(async () => {
43
+ client = await makeClient();
44
+ });
45
+
46
+ describe('can return non-retryable records', () => {
47
+ const logger = debugLogger('congested_test');
48
+ const index = `${TEST_INDEX_PREFIX}_non-retryable-records`;
49
+
50
+ beforeAll(async () => {
51
+ await cleanupIndex(client, index);
52
+ api = elasticsearchAPI(client, logger, { _dead_letter_action: 'kafka_dead_letter' });
53
+ isElasticsearch8 = api.isElasticsearch8();
54
+
55
+ const overrides = {
56
+ settings: {
57
+ 'index.number_of_shards': 1,
58
+ 'index.number_of_replicas': 0,
59
+ },
60
+ };
61
+
62
+ const mapping = await createMappingFromDatatype(
63
+ client, EvenDateData.EvenDataType, '_doc', overrides
64
+ );
65
+
66
+ mapping.index = index;
67
+
68
+ await client.indices.create(mapping);
69
+ });
70
+
71
+ afterAll(async () => {
72
+ await cleanupIndex(client, index);
73
+ });
74
+
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
+
88
+ const docs = cloneDeep(EvenDateData.data.slice(0, 2));
89
+
90
+ docs[0].bytes = 'this is a bad value';
91
+
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 }));
99
+
100
+ docs[0].bytes = 'this is a bad value';
101
+
102
+ const result = await api.bulkSend(formatUploadData(index, docs, isElasticsearch8));
103
+
104
+ // 1 good doc - so only 1 row affected
105
+ expect(result).toBe(1);
106
+
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();
109
+ });
110
+
111
+ it('should return a count if not un-retryable records if dlq is set', async () => {
112
+ const docs = cloneDeep(EvenDateData.data.slice(0, 2));
113
+
114
+ const result = await api.bulkSend(formatUploadData(index, docs, isElasticsearch8));
115
+
116
+ expect(result).toBe(2);
117
+ });
118
+ });
119
+ });
@@ -1,6 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const { debugLogger, chunk, pMap } = require('@terascope/utils');
3
+ const {
4
+ debugLogger,
5
+ chunk,
6
+ pMap
7
+ } = require('@terascope/utils');
4
8
  const { ElasticsearchTestHelpers } = require('elasticsearch-store');
5
9
  const elasticsearchAPI = require('../index');
6
10
 
@@ -11,7 +15,7 @@ const {
11
15
 
12
16
  const THREE_MINUTES = 3 * 60 * 1000;
13
17
 
14
- jest.setTimeout(THREE_MINUTES + 30000);
18
+ jest.setTimeout(THREE_MINUTES + 60000);
15
19
 
16
20
  function formatUploadData(
17
21
  index, data, isES8ClientTest = false
@@ -31,33 +35,38 @@ function formatUploadData(
31
35
  return results;
32
36
  }
33
37
 
34
- describe('bulkSend can work with congested queues', () => {
35
- const logger = debugLogger('congested_test');
36
- const index = `${TEST_INDEX_PREFIX}_congested_queues_`;
37
-
38
+ describe('bulkSend', () => {
38
39
  let client;
39
40
  let api;
40
41
  let isElasticsearch8 = false;
41
42
 
42
43
  beforeAll(async () => {
43
44
  client = await makeClient();
44
- await cleanupIndex(client, index);
45
- api = elasticsearchAPI(client, logger);
46
- isElasticsearch8 = api.isElasticsearch8();
47
45
  });
48
46
 
49
- afterAll(async () => {
50
- await cleanupIndex(client, index);
51
- });
47
+ describe('can work with congested queues', () => {
48
+ const logger = debugLogger('congested_test');
49
+ const index = `${TEST_INDEX_PREFIX}_congested_queues_`;
50
+
51
+ beforeAll(async () => {
52
+ await cleanupIndex(client, index);
53
+ api = elasticsearchAPI(client, logger);
54
+ isElasticsearch8 = api.isElasticsearch8();
55
+ });
56
+
57
+ afterAll(async () => {
58
+ await cleanupIndex(client, index);
59
+ });
52
60
 
53
- it('can get correct data even with congested queues', async () => {
54
- const chunkedData = chunk(EvenDateData.data, 50);
61
+ it('can get correct data even with congested queues', async () => {
62
+ const chunkedData = chunk(EvenDateData.data, 50);
55
63
 
56
- await pMap(chunkedData, async (cData) => {
57
- const formattedData = formatUploadData(index, cData, isElasticsearch8);
58
- return api.bulkSend(formattedData);
59
- }, { concurrency: 9 });
64
+ await pMap(chunkedData, async (cData) => {
65
+ const formattedData = formatUploadData(index, cData, isElasticsearch8);
66
+ return api.bulkSend(formattedData);
67
+ }, { concurrency: 9 });
60
68
 
61
- await waitForData(client, index, EvenDateData.data.length, logger, THREE_MINUTES);
69
+ await waitForData(client, index, EvenDateData.data.length, logger, THREE_MINUTES);
70
+ });
62
71
  });
63
72
  });
package/types/index.d.ts CHANGED
@@ -62,7 +62,7 @@ declare namespace elasticsearchAPI {
62
62
  /**
63
63
  * This is used for improved bulk sending function
64
64
  */
65
- export interface AnyBulkAction {
65
+ export interface AnyBulkAction {
66
66
  update?: Partial<BulkActionMetadata>;
67
67
  index?: Partial<BulkActionMetadata>;
68
68
  create?: Partial<BulkActionMetadata>;