wingbot-mongodb 3.2.0 → 3.2.2

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.
@@ -1 +1 @@
1
- {"parent":null,"pid":24990,"argv":["/usr/local/bin/node","/Users/david/Development/wingbot-mongodb/node_modules/.bin/mocha","./test"],"execArgv":[],"cwd":"/Users/david/Development/wingbot-mongodb","time":1676028268542,"ppid":24989,"coverageFilename":"/Users/david/Development/wingbot-mongodb/.nyc_output/cb79cd4d-6c45-40e4-b29a-e38af10c7c7c.json","externalId":"","uuid":"cb79cd4d-6c45-40e4-b29a-e38af10c7c7c","files":["/Users/david/Development/wingbot-mongodb/src/BaseStorage.js","/Users/david/Development/wingbot-mongodb/src/defaultLogger.js","/Users/david/Development/wingbot-mongodb/src/AttachmentCache.js","/Users/david/Development/wingbot-mongodb/src/AuditLogStorage.js","/Users/david/Development/wingbot-mongodb/src/BotConfigStorage.js","/Users/david/Development/wingbot-mongodb/src/BotTokenStorage.js","/Users/david/Development/wingbot-mongodb/src/tokenFactory.js","/Users/david/Development/wingbot-mongodb/src/ChatLogStorage.js","/Users/david/Development/wingbot-mongodb/src/NotificationsStorage.js","/Users/david/Development/wingbot-mongodb/src/StateStorage.js"]}
1
+ {"parent":null,"pid":81134,"argv":["/usr/local/bin/node","/Users/david/Development/wingbot-mongodb/node_modules/.bin/mocha","./test"],"execArgv":[],"cwd":"/Users/david/Development/wingbot-mongodb","time":1676533760202,"ppid":81133,"coverageFilename":"/Users/david/Development/wingbot-mongodb/.nyc_output/98a862df-b2a1-4d14-9176-6b0bada108f7.json","externalId":"","uuid":"98a862df-b2a1-4d14-9176-6b0bada108f7","files":["/Users/david/Development/wingbot-mongodb/src/BaseStorage.js","/Users/david/Development/wingbot-mongodb/src/defaultLogger.js","/Users/david/Development/wingbot-mongodb/src/AttachmentCache.js","/Users/david/Development/wingbot-mongodb/src/AuditLogStorage.js","/Users/david/Development/wingbot-mongodb/src/BotConfigStorage.js","/Users/david/Development/wingbot-mongodb/src/BotTokenStorage.js","/Users/david/Development/wingbot-mongodb/src/tokenFactory.js","/Users/david/Development/wingbot-mongodb/src/ChatLogStorage.js","/Users/david/Development/wingbot-mongodb/src/NotificationsStorage.js","/Users/david/Development/wingbot-mongodb/src/StateStorage.js"]}
@@ -1 +1 @@
1
- {"processes":{"cb79cd4d-6c45-40e4-b29a-e38af10c7c7c":{"parent":null,"children":[]}},"files":{"/Users/david/Development/wingbot-mongodb/src/BaseStorage.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"],"/Users/david/Development/wingbot-mongodb/src/defaultLogger.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"],"/Users/david/Development/wingbot-mongodb/src/AttachmentCache.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"],"/Users/david/Development/wingbot-mongodb/src/AuditLogStorage.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"],"/Users/david/Development/wingbot-mongodb/src/BotConfigStorage.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"],"/Users/david/Development/wingbot-mongodb/src/BotTokenStorage.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"],"/Users/david/Development/wingbot-mongodb/src/tokenFactory.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"],"/Users/david/Development/wingbot-mongodb/src/ChatLogStorage.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"],"/Users/david/Development/wingbot-mongodb/src/NotificationsStorage.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"],"/Users/david/Development/wingbot-mongodb/src/StateStorage.js":["cb79cd4d-6c45-40e4-b29a-e38af10c7c7c"]},"externalIds":{}}
1
+ {"processes":{"98a862df-b2a1-4d14-9176-6b0bada108f7":{"parent":null,"children":[]}},"files":{"/Users/david/Development/wingbot-mongodb/src/BaseStorage.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"],"/Users/david/Development/wingbot-mongodb/src/defaultLogger.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"],"/Users/david/Development/wingbot-mongodb/src/AttachmentCache.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"],"/Users/david/Development/wingbot-mongodb/src/AuditLogStorage.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"],"/Users/david/Development/wingbot-mongodb/src/BotConfigStorage.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"],"/Users/david/Development/wingbot-mongodb/src/BotTokenStorage.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"],"/Users/david/Development/wingbot-mongodb/src/tokenFactory.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"],"/Users/david/Development/wingbot-mongodb/src/ChatLogStorage.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"],"/Users/david/Development/wingbot-mongodb/src/NotificationsStorage.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"],"/Users/david/Development/wingbot-mongodb/src/StateStorage.js":["98a862df-b2a1-4d14-9176-6b0bada108f7"]},"externalIds":{}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot-mongodb",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "description": "MongoDB storage for wingbot.ai",
5
5
  "main": "src/main.js",
6
6
  "scripts": {
@@ -33,12 +33,31 @@ function getNestedObjects (obj, nested, attr = null, ret = {}) {
33
33
 
34
34
  class BaseStorage {
35
35
 
36
+ static netFailuresIntervalMs = 600000; // 10 minutes
37
+
38
+ static netFailuresCount = 0;
39
+
40
+ static _killing = null;
41
+
42
+ static _failStack = [];
43
+
44
+ static killer = () => setTimeout(() => {
45
+ process.exit(1);
46
+ }, 10000);
47
+
48
+ static networkFailureErrorNames = [
49
+ 'MongoServerSelectionError',
50
+ 'MongoNetworkError',
51
+ 'MongoNetworkTimeoutError',
52
+ 'MongoTopologyClosedError'
53
+ ];
54
+
36
55
  /**
37
56
  *
38
57
  * @param {Db|{():Promise<Db>}} mongoDb
39
58
  * @param {string} collectionName
40
59
  * @param {{error:Function,log:Function}} [log] - console like logger
41
- * @param {boolean} [isCosmo]
60
+ * @param {boolean|number} [isCosmo]
42
61
  * @example
43
62
  *
44
63
  * const { BaseStorage } = require('winbot-mongodb');
@@ -67,7 +86,7 @@ class BaseStorage {
67
86
  constructor (mongoDb, collectionName, log = defaultLogger, isCosmo = false) {
68
87
  this._mongoDb = mongoDb;
69
88
  this._collectionName = collectionName;
70
- this._isCosmo = isCosmo;
89
+ this._isCosmo = typeof isCosmo === 'number' || isCosmo;
71
90
  this._log = log;
72
91
 
73
92
  /**
@@ -84,7 +103,11 @@ class BaseStorage {
84
103
 
85
104
  this._fixtures = [];
86
105
 
87
- if (isCosmo && !process.argv.some((a) => a.endsWith('mocha'))) {
106
+ if (typeof isCosmo === 'number') {
107
+ BaseStorage.netFailuresCount = isCosmo;
108
+ }
109
+
110
+ if (this._isCosmo && !process.argv.some((a) => a.endsWith('mocha'))) {
88
111
  process.nextTick(() => {
89
112
  const hasUniqueIndex = this._indexes.some((i) => i.options.unique);
90
113
 
@@ -96,6 +119,45 @@ class BaseStorage {
96
119
  }
97
120
  }
98
121
 
122
+ /**
123
+ *
124
+ * @template T
125
+ * @param {{():T}} fn
126
+ * @returns {Promise<T>}
127
+ */
128
+ _catchNetworkIssues (fn) {
129
+ return Promise.resolve(fn())
130
+ .catch((e) => {
131
+ this._detectNetworkIssueException(e);
132
+ throw e;
133
+ });
134
+ }
135
+
136
+ /**
137
+ * @template {Error} T
138
+ * @param {T} e
139
+ * @returns {T}
140
+ */
141
+ _detectNetworkIssueException (e) {
142
+ if (BaseStorage.netFailuresCount !== 0
143
+ && BaseStorage.networkFailureErrorNames.includes(e.name)) {
144
+
145
+ const now = Date.now();
146
+ BaseStorage._failStack.push(now);
147
+
148
+ if (BaseStorage._failStack.length >= BaseStorage.netFailuresCount
149
+ && BaseStorage._failStack.shift() > (now - BaseStorage.netFailuresIntervalMs)
150
+ && BaseStorage._killing === null) {
151
+
152
+ this._log.error(`${this._collectionName}: KILLING APP DUE FREQUENT NETWORK ERRORS ${BaseStorage._failStack.length}`, e);
153
+
154
+ // let it alive for following ten seconds to process all logs
155
+ BaseStorage._killing = BaseStorage.killer() || null;
156
+ }
157
+ }
158
+ return e;
159
+ }
160
+
99
161
  preHeat () {
100
162
  return this._getCollection();
101
163
  }
@@ -201,6 +263,7 @@ class BaseStorage {
201
263
  this._collection = c;
202
264
  } catch (e) {
203
265
  this._collection = null;
266
+ this._detectNetworkIssueException(e);
204
267
  throw e;
205
268
  }
206
269
  }
@@ -303,13 +303,14 @@ class NotificationsStorage {
303
303
 
304
304
  const findMissingIds = tasks
305
305
  .reduce((arr, {
306
- campaignId, senderId, pageId, sent
306
+ campaignId, senderId, pageId, sent, enqueue
307
307
  }, i) => {
308
308
  if (typeof res.upsertedIds[i] !== 'undefined') {
309
309
  return arr;
310
310
  }
311
311
  arr.push({
312
312
  i,
313
+ enqueue,
313
314
  filter: {
314
315
  campaignId, senderId, pageId, sent
315
316
  }
@@ -321,23 +322,31 @@ class NotificationsStorage {
321
322
 
322
323
  if (findMissingIds.length > 0) {
323
324
  await Promise.all(findMissingIds
324
- .map(({ filter, i }) => c.findOne(filter, {
325
+ .map(({ filter, i, enqueue }) => c.findOne(filter, {
325
326
  projection: {
326
327
  _id: 1, insEnqueue: 1, enqueue: 1, ups: 1
327
328
  }
328
329
  })
329
330
  .then((found) => {
330
- const id = typeof found._id === 'string'
331
- ? found._id
332
- : found._id.toHexString();
333
- missingIds.set(i, {
334
- id,
335
- insEnqueue: found.insEnqueue,
336
- enqueue: found.insEnqueue === found.enqueue
337
- && found.enqueue !== MAX_TS && found.ups !== 1
338
- ? found.enqueue + 1
339
- : found.enqueue
340
- });
331
+ if (!found) { // race condition occurred
332
+ missingIds.set(i, {
333
+ id: null,
334
+ insEnqueue: -1,
335
+ enqueue
336
+ });
337
+ } else {
338
+ const id = typeof found._id === 'string'
339
+ ? found._id
340
+ : found._id.toHexString();
341
+ missingIds.set(i, {
342
+ id,
343
+ insEnqueue: found.insEnqueue,
344
+ enqueue: found.insEnqueue === found.enqueue
345
+ && found.enqueue !== MAX_TS && found.ups !== 1
346
+ ? found.enqueue + 1
347
+ : found.enqueue
348
+ });
349
+ }
341
350
  })));
342
351
  }
343
352
 
@@ -40,7 +40,7 @@ class StateStorage extends BaseStorage {
40
40
  * @param {boolean|number} isCosmo - boolean or number of failures in last 10min to kill app
41
41
  */
42
42
  constructor (mongoDb, collectionName = 'states', log = defaultLogger, isCosmo = false) {
43
- super(mongoDb, collectionName, log, typeof isCosmo === 'number' || isCosmo);
43
+ super(mongoDb, collectionName, log, isCosmo);
44
44
 
45
45
  this.addIndex(
46
46
  { senderId: 1, pageId: 1 },
@@ -51,10 +51,6 @@ class StateStorage extends BaseStorage {
51
51
  { name: LAST_INTERACTION_INDEX }
52
52
  );
53
53
 
54
- this._failStack = [];
55
-
56
- this._failures = typeof isCosmo === 'number' ? isCosmo : 0;
57
-
58
54
  if (isCosmo) {
59
55
  this.addIndex(
60
56
  { name: 1 },
@@ -66,21 +62,6 @@ class StateStorage extends BaseStorage {
66
62
  { name: SEARCH }
67
63
  );
68
64
  }
69
-
70
- this.failuresIntervalMs = 600000; // 10 minutes
71
-
72
- this._killing = null;
73
-
74
- this.networkFailureErrorNames = [
75
- 'MongoServerSelectionError',
76
- 'MongoNetworkError',
77
- 'MongoNetworkTimeoutError',
78
- 'MongoTopologyClosedError'
79
- ];
80
-
81
- this.killer = () => setTimeout(() => {
82
- process.exit(1);
83
- }, 10000);
84
65
  }
85
66
 
86
67
  /**
@@ -118,59 +99,37 @@ class StateStorage extends BaseStorage {
118
99
  * @returns {Promise<object>} - conversation state
119
100
  */
120
101
  async getOrCreateAndLock (senderId, pageId, defaultState = {}, timeout = 300) {
121
- let now = Date.now();
122
- try {
123
- const c = await this._getCollection();
124
-
125
- const $setOnInsert = {
126
- state: defaultState,
127
- lastSendError: null,
128
- off: false
129
- };
130
-
131
- const $set = {
132
- lock: now
133
- };
134
-
135
- const $lt = now - timeout;
136
-
137
- const res = await c.findOneAndUpdate({
138
- senderId,
139
- pageId,
140
- lock: { $lt }
141
- }, {
142
- $setOnInsert,
143
- $set
144
- }, {
145
- upsert: true,
146
- returnDocument: 'after',
147
- projection: {
148
- _id: 0
149
- }
150
- });
151
-
152
- return res.value;
153
- } catch (e) {
154
- if (this._failures !== 0
155
- && this.networkFailureErrorNames.includes(e.name)) {
156
-
157
- now = Date.now();
158
- this._failStack.push(now);
159
-
160
- if (this._failStack.length >= this._failures
161
- && this._failStack.shift() > (now - this.failuresIntervalMs)
162
- && this._killing === null) {
163
-
164
- this._log.error(`StateStorage: KILLING APP DUE FREQUENT NETWORK ERRORS ${this._failStack.length}`, e);
165
-
166
- // let it alive for following ten seconds to process all logs
167
- this._killing = this.killer() || null;
168
- }
169
- }
102
+ const now = Date.now();
103
+ const c = await this._getCollection();
170
104
 
171
- throw e;
172
- }
105
+ const $setOnInsert = {
106
+ state: defaultState,
107
+ lastSendError: null,
108
+ off: false
109
+ };
110
+
111
+ const $set = {
112
+ lock: now
113
+ };
114
+
115
+ const $lt = now - timeout;
116
+
117
+ const res = await this._catchNetworkIssues(() => c.findOneAndUpdate({
118
+ senderId,
119
+ pageId,
120
+ lock: { $lt }
121
+ }, {
122
+ $setOnInsert,
123
+ $set
124
+ }, {
125
+ upsert: true,
126
+ returnDocument: 'after',
127
+ projection: {
128
+ _id: 0
129
+ }
130
+ }));
173
131
 
132
+ return res.value;
174
133
  }
175
134
 
176
135
  /**