@itentialopensource/adapter-utils 4.44.9
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/.eslintignore +3 -0
- package/.eslintrc.js +18 -0
- package/.jshintrc +3 -0
- package/CHANGELOG.md +1398 -0
- package/CODE_OF_CONDUCT.md +48 -0
- package/CONTRIBUTING.md +173 -0
- package/LICENSE +201 -0
- package/README.md +12 -0
- package/actionSchema.json +186 -0
- package/error.json +148 -0
- package/index.js +7 -0
- package/lib/connectorRest.js +4083 -0
- package/lib/dbUtil.js +1300 -0
- package/lib/propertyUtil.js +1012 -0
- package/lib/requestHandler.js +1175 -0
- package/lib/restHandler.js +1309 -0
- package/lib/throttle.js +1289 -0
- package/lib/translatorUtil.js +1137 -0
- package/package.json +61 -0
- package/propertiesSchema.json +840 -0
- package/utils/pre-commit.sh +26 -0
- package/utils/setup.js +32 -0
- package/utils/testRunner.js +259 -0
package/lib/throttle.js
ADDED
|
@@ -0,0 +1,1289 @@
|
|
|
1
|
+
/* @copyright Itential, LLC 2018 */
|
|
2
|
+
|
|
3
|
+
// Set globals
|
|
4
|
+
/* global pronghornProps log */
|
|
5
|
+
/* eslint class-methods-use-this:warn */
|
|
6
|
+
/* eslint comma-dangle: ["error", "never"] */
|
|
7
|
+
/* eslint consistent-return:warn */
|
|
8
|
+
/* eslint no-loop-func:warn */
|
|
9
|
+
/* eslint no-param-reassign:warn */
|
|
10
|
+
/* eslint no-underscore-dangle: [2, { "allow": ["_id"] }] */
|
|
11
|
+
|
|
12
|
+
/* NodeJS internal API utilities */
|
|
13
|
+
const uuid = require('uuid');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
|
|
17
|
+
/* Set up event emitter and other required references */
|
|
18
|
+
const AsyncLockCl = require('async-lock');
|
|
19
|
+
|
|
20
|
+
let transUtilInst = null;
|
|
21
|
+
let dbUtilInst = null;
|
|
22
|
+
|
|
23
|
+
// Global Variables - From Properties
|
|
24
|
+
let props = {};
|
|
25
|
+
let numberPhs = 1;
|
|
26
|
+
let syncAsync = 'sync';
|
|
27
|
+
let MaxInQueue = 1000;
|
|
28
|
+
let concurrentMax = 1;
|
|
29
|
+
let expireTimeout = 0;
|
|
30
|
+
let avgRuntime = 200;
|
|
31
|
+
const priorityTable = [];
|
|
32
|
+
|
|
33
|
+
// Other global variables
|
|
34
|
+
let id = null;
|
|
35
|
+
let phInstance = null;
|
|
36
|
+
let queueColl = '_queue';
|
|
37
|
+
const memQueue = [];
|
|
38
|
+
const memQlock = 0;
|
|
39
|
+
let avgQueue = [];
|
|
40
|
+
const avgQlock = 0;
|
|
41
|
+
let avgPtr = 1;
|
|
42
|
+
const avgSize = 25;
|
|
43
|
+
let avgTotal = 0;
|
|
44
|
+
let qlock = null;
|
|
45
|
+
// let MONGOQ;
|
|
46
|
+
|
|
47
|
+
/* THROTTLE ENGINE INTERNAL FUNCTIONS */
|
|
48
|
+
/*
|
|
49
|
+
* INTERNAL FUNCTION: getQueueItems is used to retrieve queue items from
|
|
50
|
+
* the database or memory based on the filter provided
|
|
51
|
+
*/
|
|
52
|
+
function getQueueItems(dbUI, collectionName, filter, callback) {
|
|
53
|
+
const origin = `${id}-throttle-getQueueItems`;
|
|
54
|
+
log.spam(origin);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
let myFilter = filter;
|
|
58
|
+
|
|
59
|
+
if (myFilter === null || myFilter === undefined) {
|
|
60
|
+
myFilter = {};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// If the number of Pronghorns is greater than 1, the queue is in the database
|
|
64
|
+
if (numberPhs > 1) {
|
|
65
|
+
// actual call to retrieve the queue items from the database
|
|
66
|
+
// return MONGOQ.find(myFilter).toArray((qerror, queueItems) => {
|
|
67
|
+
return dbUI.find(collectionName, filter, null, false, (err, res) => {
|
|
68
|
+
if (err) {
|
|
69
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Find Error', [myFilter, err], null, null, null);
|
|
70
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
71
|
+
return callback(null, errorObj);
|
|
72
|
+
}
|
|
73
|
+
return res.toArray((qerror, queueItems) => {
|
|
74
|
+
if (qerror) {
|
|
75
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'No Queue Item', [myFilter, qerror], null, null, null);
|
|
76
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
77
|
+
return callback(null, errorObj);
|
|
78
|
+
}
|
|
79
|
+
return callback(queueItems);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (myFilter.transNum !== undefined && myFilter.transNum.$lte !== undefined) {
|
|
85
|
+
// If the number of Pronghorns is 1, the queue is in memory
|
|
86
|
+
// need to add capabilities to searches as needed
|
|
87
|
+
// currently - before me, active and all
|
|
88
|
+
// Lock the memQueue while getting the ones before me
|
|
89
|
+
return qlock.acquire(memQlock, (done) => {
|
|
90
|
+
const tempQueue = [];
|
|
91
|
+
|
|
92
|
+
// push all of the transactions before me into the return queue
|
|
93
|
+
for (let i = 0; i < memQueue.length; i += 1) {
|
|
94
|
+
if (memQueue[i].transNum <= myFilter.transNum.$lte) {
|
|
95
|
+
tempQueue.push(memQueue[i]);
|
|
96
|
+
} else {
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// return the temp queue
|
|
102
|
+
done(tempQueue);
|
|
103
|
+
}, (ret, error) => {
|
|
104
|
+
if (error) {
|
|
105
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'No Queue Item', [myFilter, error], null, null, null);
|
|
106
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
107
|
+
return callback(null, errorObj);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return callback(ret);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (myFilter.active !== undefined && myFilter.active === true) {
|
|
115
|
+
// currently - active and all
|
|
116
|
+
// Lock the memQueue while finding the active items
|
|
117
|
+
return qlock.acquire(memQlock, (done) => {
|
|
118
|
+
const activeOnes = [];
|
|
119
|
+
|
|
120
|
+
for (let i = 0; i < memQueue.length; i += 1) {
|
|
121
|
+
if (memQueue[i].active === true) {
|
|
122
|
+
activeOnes.push(memQueue[i]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
done(activeOnes);
|
|
127
|
+
}, (ret, error) => {
|
|
128
|
+
if (error) {
|
|
129
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'No Queue Item', [myFilter, error], null, null, null);
|
|
130
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
131
|
+
return callback(null, errorObj);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return callback(ret);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// unsupported filter - just return entire queue
|
|
139
|
+
// Lock the memQueue while cloning it
|
|
140
|
+
return qlock.acquire(memQlock, (done) => {
|
|
141
|
+
// return a clone so it is current state and not modified as we are looking
|
|
142
|
+
const tempQueue = memQueue.slice();
|
|
143
|
+
done(tempQueue);
|
|
144
|
+
}, (ret, error) => {
|
|
145
|
+
if (error) {
|
|
146
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'No Queue Item', [myFilter, error], null, null, null);
|
|
147
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
148
|
+
return callback(null, errorObj);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return callback(ret);
|
|
152
|
+
});
|
|
153
|
+
} catch (e) {
|
|
154
|
+
// handle any exception
|
|
155
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue getting queue items');
|
|
156
|
+
return callback(null, errorObj);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/*
|
|
161
|
+
* INTERNAL FUNCTION: claimTurn is used to start the request and
|
|
162
|
+
* claim the turn. This means setting the start time and changing
|
|
163
|
+
* the running and active flags to true.
|
|
164
|
+
*/
|
|
165
|
+
function claimTurn(dbUI, collectionName, queueItem, callback) {
|
|
166
|
+
const origin = `${id}-throttle-claimTurn`;
|
|
167
|
+
log.spam(origin);
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const cur = new Date();
|
|
171
|
+
|
|
172
|
+
// set up the update object to mark the queue item that the execution is starting
|
|
173
|
+
// set start, change active to true, change running to true
|
|
174
|
+
const data = {
|
|
175
|
+
_id: queueItem._id,
|
|
176
|
+
ph_instance: queueItem.ph_instance,
|
|
177
|
+
request_id: queueItem.request_id,
|
|
178
|
+
transNum: queueItem.transNum,
|
|
179
|
+
priority: queueItem.priority,
|
|
180
|
+
start: cur.getTime(),
|
|
181
|
+
end: queueItem.end,
|
|
182
|
+
active: true,
|
|
183
|
+
running: true,
|
|
184
|
+
event: queueItem.event
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// If the number of Pronghorns is greater than 1, the queue is in the database
|
|
188
|
+
if (numberPhs > 1) {
|
|
189
|
+
// actual call to claim the turn from the queue in the database
|
|
190
|
+
// return MONGOQ.replaceOne({ _id: data._id }, data, (error, result) => {
|
|
191
|
+
return dbUI.replaceOne(collectionName, { _id: data._id }, data, {}, null, false, (error, result) => {
|
|
192
|
+
if (error) {
|
|
193
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Claim Turn', [queueItem.request_id, queueItem.transNum, error], null, null, null);
|
|
194
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
195
|
+
return callback(null, errorObj);
|
|
196
|
+
}
|
|
197
|
+
log.debug(`${origin}: Mongo replace one returned ${result}`);
|
|
198
|
+
return callback(data);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// If the number of Pronghorns is 1, the queue is in memory
|
|
203
|
+
// Lock the memQueue while finding and updating the item
|
|
204
|
+
return qlock.acquire(memQlock, (done) => {
|
|
205
|
+
const index = memQueue.indexOf(queueItem);
|
|
206
|
+
|
|
207
|
+
if (index >= 0) {
|
|
208
|
+
memQueue[index] = data;
|
|
209
|
+
done(memQueue[index]);
|
|
210
|
+
} else {
|
|
211
|
+
done(null, 'Queue Item Not Found');
|
|
212
|
+
}
|
|
213
|
+
}, (ret, error) => {
|
|
214
|
+
if (error) {
|
|
215
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Claim Turn', [queueItem.request_id, queueItem.transNum, error], null, null, null);
|
|
216
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
217
|
+
return callback(null, errorObj);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return callback(ret);
|
|
221
|
+
});
|
|
222
|
+
} catch (e) {
|
|
223
|
+
// handle any exception
|
|
224
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue claiming turn');
|
|
225
|
+
return callback(null, errorObj);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/*
|
|
230
|
+
* INTERNAL FUNCTION: freeQueueItem is used to free the turn from the given request.
|
|
231
|
+
* This is done by deleting the item from the database or removing it from the
|
|
232
|
+
* queue.
|
|
233
|
+
*/
|
|
234
|
+
function freeQueueItem(dbUI, collectionName, queueItem, callback) {
|
|
235
|
+
const origin = `${id}-throttle-freeQueueItem`;
|
|
236
|
+
log.spam(origin);
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
// If the number of Pronghorns is greater than 1, the queue is in the database
|
|
240
|
+
if (numberPhs > 1) {
|
|
241
|
+
// actual call to remove the request from the queue in the database
|
|
242
|
+
// return MONGOQ.deleteOne({ _id: queueItem._id }, (error, result) => {
|
|
243
|
+
return dbUI.delete(collectionName, { _id: queueItem._id }, {}, false, null, false, (error, result) => {
|
|
244
|
+
if (error) {
|
|
245
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Free Turn', [queueItem.request_id, queueItem.transNum, error], null, null, null);
|
|
246
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
247
|
+
return callback(null, errorObj);
|
|
248
|
+
}
|
|
249
|
+
return callback(result);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// If the number of Pronghorns is 1, the queue is in memory
|
|
254
|
+
// Lock the memQueue while finding and removing the item
|
|
255
|
+
return qlock.acquire(memQlock, (done) => {
|
|
256
|
+
const index = memQueue.indexOf(queueItem);
|
|
257
|
+
|
|
258
|
+
if (index >= 0) {
|
|
259
|
+
memQueue.splice(index, 1);
|
|
260
|
+
done('successfully removed from queue');
|
|
261
|
+
} else {
|
|
262
|
+
done(null, 'Queue Item Not Found');
|
|
263
|
+
}
|
|
264
|
+
}, (ret, error) => {
|
|
265
|
+
if (error) {
|
|
266
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Free Turn', [queueItem.request_id, queueItem.transNum, error], null, null, null);
|
|
267
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
268
|
+
return callback(null, errorObj);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return callback(ret);
|
|
272
|
+
});
|
|
273
|
+
} catch (e) {
|
|
274
|
+
// handle any exception
|
|
275
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue freeing queue item');
|
|
276
|
+
return callback(null, errorObj);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/*
|
|
281
|
+
* INTERNAL FUNCTION: freeExpiredQueueItems goes through the current active usage to
|
|
282
|
+
* determine if any have finished running long enough ago that the turn has expired.
|
|
283
|
+
* If it has, this method calls freeQueueItem to have the turn freed in Pronghorn so
|
|
284
|
+
* another request can get the turn. This is only used when there is some graceful
|
|
285
|
+
* expiration to a concurrent run. If runs expire immediately, expireTimeout will
|
|
286
|
+
* be 0 and this does nothing.
|
|
287
|
+
*/
|
|
288
|
+
function freeExpiredQueueItems(callback) {
|
|
289
|
+
const origin = `${id}-throttle-freeExpiredQueueItems`;
|
|
290
|
+
log.spam(origin);
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const status = 'success';
|
|
294
|
+
|
|
295
|
+
// if timeout is 0, this is handled in finish queue item as that is more efficient
|
|
296
|
+
if (expireTimeout === 0) {
|
|
297
|
+
return callback(status);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const cur = new Date();
|
|
301
|
+
|
|
302
|
+
// actual call to retrieve the in use turn from the database
|
|
303
|
+
return getQueueItems(dbUtilInst, queueColl, { active: true }, (currentUse, gerror) => {
|
|
304
|
+
if (gerror) {
|
|
305
|
+
return callback(null, gerror);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// if there is nothing to free just return
|
|
309
|
+
if (currentUse.length === 0) {
|
|
310
|
+
return callback(status);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let handled = 0;
|
|
314
|
+
|
|
315
|
+
// go through the queue items to see if any have expired
|
|
316
|
+
for (let i = 0; i < currentUse.length; i += 1) {
|
|
317
|
+
// if the transaction finished and the end + timeout is less than the current time
|
|
318
|
+
if (currentUse[i].end !== null
|
|
319
|
+
&& (Number(currentUse[i].end) + Number(expireTimeout) < cur.getTime())) {
|
|
320
|
+
freeQueueItem(dbUtilInst, queueColl, currentUse[i], (flic, ferror) => {
|
|
321
|
+
if (ferror) {
|
|
322
|
+
return callback(null, ferror);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
handled += 1;
|
|
326
|
+
// when we have handled everything - return
|
|
327
|
+
if (handled === currentUse.length) {
|
|
328
|
+
log.spam(`${origin}: ${flic}`);
|
|
329
|
+
return callback(status);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
} else {
|
|
333
|
+
handled += 1;
|
|
334
|
+
}
|
|
335
|
+
// when we have handled everything - return
|
|
336
|
+
if (handled === currentUse.length) {
|
|
337
|
+
return callback(status);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return callback(status);
|
|
342
|
+
});
|
|
343
|
+
} catch (e) {
|
|
344
|
+
// handle any exception
|
|
345
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue freeing queue items');
|
|
346
|
+
return callback(null, errorObj);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/*
|
|
351
|
+
* INTERNAL FUNCTION: checkTurnAvailable is used to check what is available
|
|
352
|
+
* and what is ahead of me to determine if there is availability for my
|
|
353
|
+
* use.
|
|
354
|
+
*/
|
|
355
|
+
function checkTurnAvailable(myRequest, myTransNum, callback) {
|
|
356
|
+
const origin = `${id}-throttle-checkTurnAvailable`;
|
|
357
|
+
log.spam(origin);
|
|
358
|
+
|
|
359
|
+
try {
|
|
360
|
+
return freeExpiredQueueItems((freeRes, ferror) => {
|
|
361
|
+
if (ferror) {
|
|
362
|
+
return callback(null, ferror);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
log.spam(`${origin}: ${freeRes}`);
|
|
366
|
+
|
|
367
|
+
// only want to set filter if using DB - if memory want the entire queue so we get all priorities
|
|
368
|
+
let useFilter = null;
|
|
369
|
+
if (numberPhs > 1) {
|
|
370
|
+
useFilter = { transNum: { $lte: myTransNum } };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// actual call to retrieve the queue items from the database
|
|
374
|
+
return getQueueItems(dbUtilInst, queueColl, useFilter, (queueItems, gerror) => {
|
|
375
|
+
if (gerror) {
|
|
376
|
+
return callback(null, gerror);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
let numActive = 0;
|
|
380
|
+
let beforeMe = 0;
|
|
381
|
+
|
|
382
|
+
// go through the queue items - need to determine if available and my place in queue
|
|
383
|
+
// Can not assume that I am last in the returned items or that they are in order as data
|
|
384
|
+
// from Mongo is not sorted - YET!
|
|
385
|
+
for (let i = 0; i < queueItems.length; i += 1) {
|
|
386
|
+
if (JSON.stringify(queueItems[i]) !== '{}') {
|
|
387
|
+
// if this item is active
|
|
388
|
+
if (queueItems[i].active) {
|
|
389
|
+
numActive += 1;
|
|
390
|
+
} else if (numberPhs > 1) {
|
|
391
|
+
// if database, need to be able to reorder since db doesn't maintain order
|
|
392
|
+
// if the item is not active but before me in the queue
|
|
393
|
+
if (Number(queueItems[i].transNum) < Number(myTransNum)) {
|
|
394
|
+
beforeMe += 1;
|
|
395
|
+
} else if (Number(queueItems[i].transNum) === Number(myTransNum)
|
|
396
|
+
&& queueItems[i].ph_instance < phInstance) {
|
|
397
|
+
// if the item is not active but before me in the queue (same trans - < pronghorn)
|
|
398
|
+
beforeMe += 1;
|
|
399
|
+
} else if (Number(queueItems[i].transNum) === Number(myTransNum)
|
|
400
|
+
&& queueItems[i].ph_instance === phInstance
|
|
401
|
+
&& Number(queueItems[i].request_id) < Number(myRequest)) {
|
|
402
|
+
// if the item is not active but before me in the queue (same trans but request id)
|
|
403
|
+
beforeMe += 1;
|
|
404
|
+
}
|
|
405
|
+
} else if (queueItems[i].transNum === Number(myTransNum)) {
|
|
406
|
+
// if this is me --- I know my index and can finish searching
|
|
407
|
+
beforeMe = i - numActive;
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// clear the memory from the queue items so it can be reclaimed
|
|
414
|
+
queueItems = undefined;
|
|
415
|
+
|
|
416
|
+
// number of spaces available (max - active)
|
|
417
|
+
const spaceAvail = Number(concurrentMax) - numActive;
|
|
418
|
+
|
|
419
|
+
// can I run? if turn available are greater then before me - yes
|
|
420
|
+
if (beforeMe < spaceAvail) {
|
|
421
|
+
return callback(0);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// otherwise the ones before can be reduced by the ones that are about to start running
|
|
425
|
+
return callback((beforeMe - spaceAvail) + 1);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
} catch (e) {
|
|
429
|
+
// handle any exception
|
|
430
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue checking turn');
|
|
431
|
+
return callback(null, errorObj);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/*
|
|
436
|
+
* INTERNAL FUNCTION: gettingCloseInterval checks if it is my turn under faster since
|
|
437
|
+
* my turn is getting near.
|
|
438
|
+
*/
|
|
439
|
+
function gettingCloseInterval(myRequest, transNum, callback) {
|
|
440
|
+
const origin = `${id}-throttle-gettingCloseInterval`;
|
|
441
|
+
log.spam(origin);
|
|
442
|
+
|
|
443
|
+
try {
|
|
444
|
+
let intRun = false;
|
|
445
|
+
const fastInt = (avgTotal / avgSize) * 0.5;
|
|
446
|
+
|
|
447
|
+
// rapid inner interval - should be done when it is almost my time to run.
|
|
448
|
+
const intervalObject = setInterval(() => {
|
|
449
|
+
// Prevents running the interval ontop of itself in case interval time is less than time
|
|
450
|
+
// it takes to run
|
|
451
|
+
if (!intRun) {
|
|
452
|
+
intRun = true;
|
|
453
|
+
|
|
454
|
+
// check if there is an actual turn available
|
|
455
|
+
checkTurnAvailable(myRequest, transNum, (toRun, cerror) => {
|
|
456
|
+
if (cerror) {
|
|
457
|
+
return callback(null, cerror);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// is it my turn to run
|
|
461
|
+
if (toRun === 0) {
|
|
462
|
+
clearInterval(intervalObject);
|
|
463
|
+
return callback(true);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
intRun = false;
|
|
467
|
+
log.debug(`${origin}: Request ${myRequest} Transaction ${transNum} waiting for turn to become free`);
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}, fastInt);
|
|
471
|
+
} catch (e) {
|
|
472
|
+
// handle any exception
|
|
473
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue during getting close');
|
|
474
|
+
return callback(null, errorObj);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
class SystemXThrottle {
|
|
479
|
+
/**
|
|
480
|
+
* Throttle
|
|
481
|
+
* @constructor
|
|
482
|
+
*/
|
|
483
|
+
constructor(prongid, properties, transUtilCl, dbUtilCl) {
|
|
484
|
+
this.myid = prongid;
|
|
485
|
+
id = prongid;
|
|
486
|
+
this.transUtil = transUtilCl;
|
|
487
|
+
this.dbUtil = dbUtilCl;
|
|
488
|
+
|
|
489
|
+
// set globals (available to private functions)
|
|
490
|
+
transUtilInst = this.transUtil;
|
|
491
|
+
dbUtilInst = this.dbUtil;
|
|
492
|
+
|
|
493
|
+
// this uniquely identifies this adapter on this pronghorn system
|
|
494
|
+
phInstance = `${id} - ${os.hostname()}`;
|
|
495
|
+
queueColl = id + queueColl;
|
|
496
|
+
this.qlockInst = new AsyncLockCl();
|
|
497
|
+
this.alock = new AsyncLockCl();
|
|
498
|
+
qlock = this.qlockInst;
|
|
499
|
+
|
|
500
|
+
// set up the properties I care about
|
|
501
|
+
this.refreshProperties(properties);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* @callback verifyCallback
|
|
506
|
+
* @param {Boolean} result - the result of the verify ready
|
|
507
|
+
* @param {String} error - any error that occured
|
|
508
|
+
*/
|
|
509
|
+
/**
|
|
510
|
+
* @callback queueCallback
|
|
511
|
+
* @param {Object} queueItem - the updated queue item
|
|
512
|
+
* @param {String} error - any error that occured
|
|
513
|
+
*/
|
|
514
|
+
|
|
515
|
+
/* THROTTLE ENGINE EXTERNAL FUNCTIONS */
|
|
516
|
+
/* refreshProperties - take in new properties without having to restart */
|
|
517
|
+
/* verifyReady - verify that we are ready */
|
|
518
|
+
/* requestQueueItem - put self in queue */
|
|
519
|
+
/* waitingMyTurn - interval waiting turn to run */
|
|
520
|
+
/* finishTurn - done using my turn to run */
|
|
521
|
+
/**
|
|
522
|
+
* refreshProperties is used to set up all of the properties for the throttle engine.
|
|
523
|
+
* It allows properties to be changed later by simply calling refreshProperties rather
|
|
524
|
+
* than having to restart the throttle engine.
|
|
525
|
+
*
|
|
526
|
+
* @function refreshProperties
|
|
527
|
+
* @param {Object} properties - an object containing all of the properties
|
|
528
|
+
*/
|
|
529
|
+
refreshProperties(properties) {
|
|
530
|
+
const origin = `${this.myid}-throttle-refreshProperties`;
|
|
531
|
+
log.trace(origin);
|
|
532
|
+
props = properties;
|
|
533
|
+
|
|
534
|
+
if (!props) {
|
|
535
|
+
log.error(`${origin}: Throttle received no properties!`);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (props.throttle) {
|
|
540
|
+
// set the throttle number of pronghorns (optional - default is 1)
|
|
541
|
+
if (props.throttle.number_pronghorns && Number(props.throttle.number_pronghorns) >= 1) {
|
|
542
|
+
numberPhs = Number(props.throttle.number_pronghorns);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// set the throttle synchronous or asynchronous (optional - default is synchronous)
|
|
546
|
+
if (props.throttle.sync_async && (props.throttle.sync_async === 'async'
|
|
547
|
+
|| props.throttle.sync_async === 'asynchronous')) {
|
|
548
|
+
syncAsync = 'async';
|
|
549
|
+
log.error(`${origin}: Throttle Engine does not currently support async ${syncAsync}`);
|
|
550
|
+
syncAsync = 'sync';
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// set the throttle maximum queue size (optional - default is 1000)
|
|
554
|
+
if (props.throttle.max_in_queue && Number(props.throttle.max_in_queue) > 0) {
|
|
555
|
+
MaxInQueue = Number(props.throttle.max_in_queue);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// set the throttle max (optional - default is 1)
|
|
559
|
+
if (props.throttle.concurrent_max !== null && Number(props.throttle.concurrent_max) > -1) {
|
|
560
|
+
if (props.throttle.concurrent_max === 0) {
|
|
561
|
+
log.warn(`${origin}: concurrent_max is set to 0. All requests will be blocked until concurrent_max is not 0`);
|
|
562
|
+
}
|
|
563
|
+
concurrentMax = Number(props.throttle.concurrent_max);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// set the expire timeout (optional - default is 0 milliseconds)
|
|
567
|
+
if (props.throttle.expire_timeout && Number(props.throttle.expire_timeout) >= 0) {
|
|
568
|
+
expireTimeout = Number(props.throttle.expire_timeout);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// set the queue interval (optional - default is 200) can not be less than 50ms
|
|
572
|
+
if (props.throttle.avg_runtime && Number(props.throttle.avg_runtime) >= 50) {
|
|
573
|
+
avgRuntime = Number(props.throttle.avg_runtime);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// set the priority table (default is empty)
|
|
577
|
+
if (props.throttle.priorities && props.throttle.priorities.length > 0) {
|
|
578
|
+
for (let p = 0; p < props.throttle.priorities.length; p += 1) {
|
|
579
|
+
if (props.throttle.priorities.value !== undefined && props.throttle.priorities.value !== null
|
|
580
|
+
&& props.throttle.priorities.percent !== undefined && props.throttle.priorities.percent !== null) {
|
|
581
|
+
const prior = {
|
|
582
|
+
value: props.throttle.priorities.value,
|
|
583
|
+
percent: props.throttle.priorities.percent
|
|
584
|
+
};
|
|
585
|
+
priorityTable.push(prior);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// reset the average runtime queue
|
|
591
|
+
this.alock.acquire(avgQlock, (done) => {
|
|
592
|
+
avgQueue = [];
|
|
593
|
+
avgTotal = 0;
|
|
594
|
+
|
|
595
|
+
// load up the tools for calculating the average run time
|
|
596
|
+
for (let i = 0; i < avgSize; i += 1) {
|
|
597
|
+
avgQueue.push(avgRuntime);
|
|
598
|
+
avgTotal += avgRuntime;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
done(avgTotal / avgSize);
|
|
602
|
+
}, (ret, error) => {
|
|
603
|
+
if (error) {
|
|
604
|
+
log.error(`${origin}: Error from updating average queue: ${error}`);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
log.debug(`${origin}: Average run time now reset to: ${ret}`);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* verifyReady is used to verify everything needed for throttling is set up. This
|
|
614
|
+
* generally means that it there is more than one Pronghorn, the database collection
|
|
615
|
+
* exists.
|
|
616
|
+
*
|
|
617
|
+
* @function verifyReady
|
|
618
|
+
* @param {Function} callback - a callback function to return whether throttle engine is ready
|
|
619
|
+
*/
|
|
620
|
+
verifyReady(callback) {
|
|
621
|
+
const origin = `${this.myid}-throttle-verifyReady`;
|
|
622
|
+
log.trace(origin);
|
|
623
|
+
|
|
624
|
+
try {
|
|
625
|
+
// if we are using Mongo - make sure it is set up properly
|
|
626
|
+
if (numberPhs > 1) {
|
|
627
|
+
const adapterProps = pronghornProps.adapterProps.adapters;
|
|
628
|
+
let prongo = null;
|
|
629
|
+
|
|
630
|
+
// Find the 'pronghorn' db
|
|
631
|
+
for (let i = 0; i < adapterProps.length; i += 1) {
|
|
632
|
+
if (adapterProps[i].type === 'MongoDriver') {
|
|
633
|
+
prongo = adapterProps[i];
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (prongo === null) {
|
|
639
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['Database Properties'], null, null, null);
|
|
640
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
641
|
+
return callback(null, errorObj);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Connection URL
|
|
645
|
+
let murl = prongo.properties.url;
|
|
646
|
+
log.spam(`${origin}: ${murl}`);
|
|
647
|
+
const dbName = prongo.properties.db;
|
|
648
|
+
|
|
649
|
+
// define some local variables to help in validating the properties.json file
|
|
650
|
+
let sslEnabled = false;
|
|
651
|
+
let sslValidate = false;
|
|
652
|
+
let sslCheckServerIdentity = false;
|
|
653
|
+
let sslCA = null;
|
|
654
|
+
let replSetEnabled = false;
|
|
655
|
+
let dbAuthEnabled = false;
|
|
656
|
+
let dbUsername = 'pronghorn';
|
|
657
|
+
let dbPassword = 'pronghorn';
|
|
658
|
+
|
|
659
|
+
/*
|
|
660
|
+
* this first section is configuration mapping
|
|
661
|
+
* it can be replaced with the config object when available
|
|
662
|
+
*/
|
|
663
|
+
if (prongo.properties.ssl) {
|
|
664
|
+
// enable ssl encryption?
|
|
665
|
+
if (prongo.properties.ssl.enabled === true) {
|
|
666
|
+
log.info(`${origin}: Connecting to MongoDB with SSL.`);
|
|
667
|
+
sslEnabled = true;
|
|
668
|
+
// validate the server's certificate against a known certificate authority?
|
|
669
|
+
if (prongo.properties.ssl.acceptInvalidCerts === false) {
|
|
670
|
+
sslValidate = true;
|
|
671
|
+
log.info(`${origin}: Certificate based SSL MongoDB connections will be used.`);
|
|
672
|
+
// if validation is enabled, we need to read the CA file
|
|
673
|
+
if (prongo.properties.ssl.sslCA) {
|
|
674
|
+
try {
|
|
675
|
+
sslCA = [fs.readFileSync(prongo.properties.ssl.sslCA)];
|
|
676
|
+
} catch (err) {
|
|
677
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', ['CA FIle'], null, null, null);
|
|
678
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
679
|
+
return callback(null, errorObj);
|
|
680
|
+
}
|
|
681
|
+
} else {
|
|
682
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', ['CA FIle'], null, null, null);
|
|
683
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
684
|
+
return callback(null, errorObj);
|
|
685
|
+
}
|
|
686
|
+
} else {
|
|
687
|
+
log.info(`${origin}: SSL MongoDB connection without CA certificate validation.`);
|
|
688
|
+
}
|
|
689
|
+
// validate the server certificate against the configured url?
|
|
690
|
+
if (prongo.properties.ssl.checkServerIdentity === true) {
|
|
691
|
+
sslCheckServerIdentity = true;
|
|
692
|
+
} else {
|
|
693
|
+
log.warn(`${origin}: WARNING: Skipping server identity validation`);
|
|
694
|
+
}
|
|
695
|
+
} else {
|
|
696
|
+
log.warn(`${origin}: WARNING: Connecting to MongoDB without SSL.`);
|
|
697
|
+
}
|
|
698
|
+
} else {
|
|
699
|
+
log.warn(`${origin}: WARNING: Connecting to MongoDB without SSL.`);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// are we using a replication set?
|
|
703
|
+
if (prongo.properties.replSet && prongo.properties.replSet.enabled === true) {
|
|
704
|
+
replSetEnabled = true;
|
|
705
|
+
murl = prongo.properties.url;
|
|
706
|
+
}
|
|
707
|
+
// are we using a username and password to authenticate?
|
|
708
|
+
if (prongo.properties.credentials) {
|
|
709
|
+
if (prongo.properties.credentials.dbAuth === true) {
|
|
710
|
+
dbAuthEnabled = true;
|
|
711
|
+
} else {
|
|
712
|
+
log.warn(`${origin}: WARNING: Connecting to MongoDB without user authentication.`);
|
|
713
|
+
}
|
|
714
|
+
if (prongo.properties.credentials.user) {
|
|
715
|
+
dbUsername = prongo.properties.credentials.user;
|
|
716
|
+
} else {
|
|
717
|
+
log.info(`${origin}: Using default mongo username`);
|
|
718
|
+
}
|
|
719
|
+
if (prongo.properties.credentials.passwd) {
|
|
720
|
+
dbPassword = prongo.properties.credentials.passwd;
|
|
721
|
+
} else {
|
|
722
|
+
log.info(`${origin}: Using default mongo password`);
|
|
723
|
+
}
|
|
724
|
+
if (dbAuthEnabled && (dbUsername === null || dbPassword === null)) {
|
|
725
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Database Credentials', [], null, null, null);
|
|
726
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
727
|
+
return callback(null, errorObj);
|
|
728
|
+
}
|
|
729
|
+
} else {
|
|
730
|
+
log.warn(`${origin}: WARNING: Connecting to MongoDB without user authentication.`);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/*
|
|
734
|
+
* This second section is to construct the mongo options object
|
|
735
|
+
*/
|
|
736
|
+
const opts = {
|
|
737
|
+
reconnectTries: 2000,
|
|
738
|
+
reconnectInterval: 1000,
|
|
739
|
+
ssl: sslEnabled,
|
|
740
|
+
sslValidate,
|
|
741
|
+
checkServerIdentity: sslCheckServerIdentity
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
const options = (replSetEnabled === true) ? { replSet: opts } : { server: opts };
|
|
745
|
+
log.debug(`${origin}: Connecting to MongoDB with options ${JSON.stringify(options)}`);
|
|
746
|
+
|
|
747
|
+
if (sslValidate === true) {
|
|
748
|
+
opts.sslCA = sslCA;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Connect to the DB
|
|
752
|
+
log.info(`${origin}: Workflow Engine: Establishing connection to Pronghorn DB...`);
|
|
753
|
+
|
|
754
|
+
this.dbUtil.connect((alive, client) => {
|
|
755
|
+
if (!alive) {
|
|
756
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Database Nonexistent', [], null, null, null);
|
|
757
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
758
|
+
return callback(null, errorObj);
|
|
759
|
+
}
|
|
760
|
+
log.info(`${origin}: Workflow Engine: Connection to Pronghorn DB Established`);
|
|
761
|
+
// Use db specified by properties file
|
|
762
|
+
const db = client.db(dbName);
|
|
763
|
+
/*
|
|
764
|
+
* once we are connected to mongo, we need to authenticate the connection
|
|
765
|
+
*/
|
|
766
|
+
if (dbAuthEnabled) {
|
|
767
|
+
db.authenticate(dbUsername, dbPassword, (autherr, result) => {
|
|
768
|
+
if (autherr) {
|
|
769
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Database Credentials', [], null, null, null);
|
|
770
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
771
|
+
return callback(null, errorObj);
|
|
772
|
+
}
|
|
773
|
+
if (result === false) {
|
|
774
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Database Credentials', [], null, null, null);
|
|
775
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
776
|
+
return callback(null, errorObj);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/*
|
|
780
|
+
* finally, we need to perform a health check to ensure we can read from the database
|
|
781
|
+
*/
|
|
782
|
+
log.info(`${origin}: Successfully authenticated with the Mongo DB server as user : ${dbUsername}`);
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// See if the queue collection exists in the database
|
|
787
|
+
db.listCollections({ name: queueColl }).toArray((dberr, dbres) => {
|
|
788
|
+
// if it does not exist, create it and index it
|
|
789
|
+
if (dberr || dbres === null || dbres === undefined || dbres.length <= 0) {
|
|
790
|
+
log.info(`${origin}: Queue collection does not exist, creating new collection`);
|
|
791
|
+
|
|
792
|
+
// add the queue to the database
|
|
793
|
+
this.dbUtil.createCollection(queueColl, null, false, (error, result) => {
|
|
794
|
+
if (error) {
|
|
795
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Unable To Create Queue', [error], null, null, null);
|
|
796
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
797
|
+
return callback(null, errorObj);
|
|
798
|
+
}
|
|
799
|
+
log.info(`${origin}: ${result}`);
|
|
800
|
+
// Get the queue collection
|
|
801
|
+
this.dbUtil.createIndex(queueColl, { transNum: 1 }, {}, null, (inerr, inres) => {
|
|
802
|
+
if (inerr) {
|
|
803
|
+
log.error(`${origin}: Received an error on indexing ${inerr}`);
|
|
804
|
+
} else {
|
|
805
|
+
log.debug(`${origin}: Mongo ensure index returned ${inres}`);
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
return callback(true);
|
|
809
|
+
// ensureIndex is deprecated, using Node.js MongoDB's createIndex method now.
|
|
810
|
+
});
|
|
811
|
+
// db.createCollection(queueColl, null, false, (data, error) => {
|
|
812
|
+
// if (error) {
|
|
813
|
+
// const errorObj = this.transUtil.formatErrorObject(origin, 'Unable To Create Queue', [error], null, null, null);
|
|
814
|
+
// log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
815
|
+
// return callback(null, errorObj);
|
|
816
|
+
// }
|
|
817
|
+
|
|
818
|
+
// // Get the queue collection
|
|
819
|
+
// MONGOQ = db.collection(queueColl);
|
|
820
|
+
// MONGOQ.ensureIndex({ transNum: 1 }, (inerr, inres) => {
|
|
821
|
+
// if (inerr) {
|
|
822
|
+
// log.error(`${origin}: Received an error on indexing ${inerr}`);
|
|
823
|
+
// } else {
|
|
824
|
+
// log.debug(`${origin}: Mongo ensure index returned ${inres}`);
|
|
825
|
+
// }
|
|
826
|
+
// });
|
|
827
|
+
|
|
828
|
+
// return callback(true);
|
|
829
|
+
// });
|
|
830
|
+
} else {
|
|
831
|
+
// if the collection already exists in the database
|
|
832
|
+
log.info(`${origin}: Queue collection check passed`);
|
|
833
|
+
|
|
834
|
+
// Get the queue collection
|
|
835
|
+
// MONGOQ = db.collection(queueColl);
|
|
836
|
+
this.dbUtil.createCollection(queueColl, null, false, (error, res) => {
|
|
837
|
+
if (error) {
|
|
838
|
+
log.error(`${origin}: ${error}`);
|
|
839
|
+
return callback(null, error);
|
|
840
|
+
}
|
|
841
|
+
log.info(`${origin}: ${res}`);
|
|
842
|
+
this.dbUtil.createIndex(queueColl, { transNum: 1 }, {}, null, (inerr, inres) => {
|
|
843
|
+
if (inerr) {
|
|
844
|
+
log.error(`${origin}: Received an error on indexing ${inerr}`);
|
|
845
|
+
} else {
|
|
846
|
+
log.debug(`${origin}: Mongo ensure index returned ${inres}`);
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
// ensureIndex is deprecated, using Node.js MongoDB's createIndex method now.
|
|
850
|
+
});
|
|
851
|
+
// MONGOQ.ensureIndex({ transNum: 1 }, (inerr, inres) => {
|
|
852
|
+
// if (inerr) {
|
|
853
|
+
// log.error(`Received an error on indexing ${inerr}`);
|
|
854
|
+
// }
|
|
855
|
+
|
|
856
|
+
// log.debug(`Mongo ensure index returned ${inres}`);
|
|
857
|
+
// });
|
|
858
|
+
|
|
859
|
+
// Delete any queue belonging to this adapter
|
|
860
|
+
// These requests are no longer waiting/processing since the adapter went down
|
|
861
|
+
const deleteFilter = {
|
|
862
|
+
ph_instance: phInstance
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
this.dbUtil.delete(queueColl, deleteFilter, {}, true, null, false, (delerr, res) => {
|
|
866
|
+
if (delerr) {
|
|
867
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Unable To Clear Queue', [delerr], null, null, null);
|
|
868
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
869
|
+
return callback(null, errorObj);
|
|
870
|
+
}
|
|
871
|
+
log.debug(`${origin}: Mongo delete many returned ${res}`);
|
|
872
|
+
return callback(true);
|
|
873
|
+
});
|
|
874
|
+
// MONGOQ.deleteMany(deleteFilter, (delerr, res) => {
|
|
875
|
+
// if (delerr) {
|
|
876
|
+
// const errorObj = this.transUtil.formatErrorObject(origin, 'Unable To Clear Queue', [delerr], null, null, null);
|
|
877
|
+
// log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
878
|
+
// return callback(null, errorObj);
|
|
879
|
+
// }
|
|
880
|
+
|
|
881
|
+
// log.debug(`Mongo delete many returned ${res}`);
|
|
882
|
+
// return callback(true);
|
|
883
|
+
// });
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
} else {
|
|
888
|
+
// if in memory queue nothing to verify
|
|
889
|
+
return callback(true);
|
|
890
|
+
}
|
|
891
|
+
} catch (e) {
|
|
892
|
+
// handle any exception
|
|
893
|
+
const errorObj = this.transUtil.checkAndReturn(e, origin, 'Issue during verify ready');
|
|
894
|
+
return callback(null, errorObj);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* requestQueueItem is used to request a queue item putting the request on the queue
|
|
900
|
+
* so that it can acquire the turn when it is available.
|
|
901
|
+
*
|
|
902
|
+
* @function requestQueueItem
|
|
903
|
+
* @param {String} myRequest - identifies the request. unique to this Pronghorn (required)
|
|
904
|
+
* @param {String} transNum - something to denote approximate order
|
|
905
|
+
* (e.g. time of the request) (required)
|
|
906
|
+
* @param {Number} priority - identifies the priorityfor the request (optional)
|
|
907
|
+
* @param {Function} callback - a callback function to return the resulting Queue Object
|
|
908
|
+
*/
|
|
909
|
+
requestQueueItem(myRequest, transNum, priority, event, callback) {
|
|
910
|
+
const origin = `${this.myid}-throttle-requestQueueItem`;
|
|
911
|
+
log.trace(origin);
|
|
912
|
+
|
|
913
|
+
try {
|
|
914
|
+
if (myRequest === null || myRequest === undefined) {
|
|
915
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['request id'], null, null, null);
|
|
916
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
917
|
+
return callback(null, errorObj);
|
|
918
|
+
}
|
|
919
|
+
if (transNum === null || transNum === undefined) {
|
|
920
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['transaction number'], null, null, null);
|
|
921
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
922
|
+
return callback(null, errorObj);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// if priority
|
|
926
|
+
let myPercent = 100;
|
|
927
|
+
if (priority !== undefined && priority !== null && priority !== -1) {
|
|
928
|
+
// find the priority
|
|
929
|
+
for (let p = 0; p < priorityTable.length; p += 1) {
|
|
930
|
+
if (priority === priorityTable[p].value) {
|
|
931
|
+
try {
|
|
932
|
+
myPercent = Number(priorityTable[p].percent);
|
|
933
|
+
} catch (exp) {
|
|
934
|
+
myPercent = 100;
|
|
935
|
+
}
|
|
936
|
+
break;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
// must make percent legitimate
|
|
941
|
+
if (myPercent < 0 || myPercent > 100) {
|
|
942
|
+
myPercent = 100;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// set up the queue item request - set instance, request, transNum,
|
|
946
|
+
// and set active and running flags to false
|
|
947
|
+
const data = {
|
|
948
|
+
_id: uuid.v4(),
|
|
949
|
+
ph_instance: phInstance,
|
|
950
|
+
request_id: myRequest,
|
|
951
|
+
transNum: Number(transNum),
|
|
952
|
+
priority: myPercent,
|
|
953
|
+
start: null,
|
|
954
|
+
end: null,
|
|
955
|
+
active: false,
|
|
956
|
+
running: false,
|
|
957
|
+
event
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
// If the number of Pronghorns is greater than 1, the queue is in the database
|
|
961
|
+
if (numberPhs > 1) {
|
|
962
|
+
// determine if there is space in the queue
|
|
963
|
+
|
|
964
|
+
// count is now deprecated, use countDocuments instead.
|
|
965
|
+
// operator replacements (count => countDocuments): $where => $expr, $near => $geoWithin with $center, $nearSphere => $geoWithin with $centerSphere
|
|
966
|
+
// return MONGOQ.count({}, (gerror, qsize) => {
|
|
967
|
+
return this.dbUtil.countDocuments(queueColl, {}, {}, null, false, (gerror, qsize) => {
|
|
968
|
+
if (gerror) {
|
|
969
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Database Error', [gerror], null, null, null);
|
|
970
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
971
|
+
return callback(null, errorObj);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// check to see if queue maxed out
|
|
975
|
+
if (qsize >= Number(MaxInQueue)) {
|
|
976
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Queue Full', [myRequest, transNum, qsize], null, null, null);
|
|
977
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
978
|
+
return callback(null, errorObj);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// write my queue item into the database queue
|
|
982
|
+
// return MONGOQ.insertOne(data, (error, result) => {
|
|
983
|
+
return this.dbUtil.create(queueColl, data, null, false, (error, result) => {
|
|
984
|
+
if (error) {
|
|
985
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Database Error', [error], null, null, null);
|
|
986
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
987
|
+
return callback(null, errorObj);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
log.debug(`${origin}: Mongo insert one returned ${result}`);
|
|
991
|
+
return callback(data);
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// If the number of Pronghorns is 1, the queue is in memory
|
|
997
|
+
// Lock the memQueue while adding the item
|
|
998
|
+
return qlock.acquire(memQlock, (done) => {
|
|
999
|
+
// if the queue is less than concurrentMax length -
|
|
1000
|
+
// just add it to queue at end as it will run right away
|
|
1001
|
+
if (memQueue.length < concurrentMax) {
|
|
1002
|
+
memQueue.push(data);
|
|
1003
|
+
done(memQueue[memQueue.length - 1]);
|
|
1004
|
+
} else if (myPercent >= 0 && myPercent < 100) {
|
|
1005
|
+
// if prioritized - put it at the right place in the queue
|
|
1006
|
+
let myIndex = Math.round(memQueue.length * (myPercent / 100));
|
|
1007
|
+
|
|
1008
|
+
// if myIndex is less than what is running, insert after running ones
|
|
1009
|
+
if (myIndex < concurrentMax) {
|
|
1010
|
+
myIndex = concurrentMax + 1;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// if at the end push
|
|
1014
|
+
if (myIndex >= memQueue.length) {
|
|
1015
|
+
myIndex = memQueue.length;
|
|
1016
|
+
memQueue.push(data);
|
|
1017
|
+
} else {
|
|
1018
|
+
// need to put behind any other equal or higher priority requests already in the queue
|
|
1019
|
+
for (let pos = myIndex; pos < memQueue.length; pos += 1) {
|
|
1020
|
+
// when we find a lower priority request, we will put this one in front of it so break the loop
|
|
1021
|
+
if (memQueue[pos].priority === undefined || memQueue[pos].priority === null || memQueue[pos].priority > myPercent) {
|
|
1022
|
+
myIndex = pos;
|
|
1023
|
+
break;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// if not at the end, insert after current index
|
|
1028
|
+
memQueue.splice(myIndex, 0, data);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// return my item
|
|
1032
|
+
done(memQueue[myIndex]);
|
|
1033
|
+
} else if (memQueue.length >= Number(MaxInQueue)) {
|
|
1034
|
+
// check to see if queue maxed out
|
|
1035
|
+
done(null, 'Queue Full');
|
|
1036
|
+
} else {
|
|
1037
|
+
// put at the end of the queue
|
|
1038
|
+
memQueue.push(data);
|
|
1039
|
+
|
|
1040
|
+
// return my item
|
|
1041
|
+
done(memQueue[memQueue.length - 1]);
|
|
1042
|
+
}
|
|
1043
|
+
}, (ret, error) => {
|
|
1044
|
+
if (error) {
|
|
1045
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Queue Full', [myRequest, transNum, memQueue.length], null, null, null);
|
|
1046
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1047
|
+
return callback(null, errorObj);
|
|
1048
|
+
}
|
|
1049
|
+
return callback(ret);
|
|
1050
|
+
});
|
|
1051
|
+
} catch (e) {
|
|
1052
|
+
// handle any exception
|
|
1053
|
+
const errorObj = this.transUtil.checkAndReturn(e, origin, 'Issue while requesting a queue item');
|
|
1054
|
+
return callback(null, errorObj);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* waitingMyTurn is used to determine when is it safe for the item to run. It attempts to see
|
|
1060
|
+
* if there is availability first, if it can not (because all spaces are in use) it will put the
|
|
1061
|
+
* attempts into an interval that re attempts. The interval is based on where in the queue and
|
|
1062
|
+
* approximate time before there will be availability.
|
|
1063
|
+
*
|
|
1064
|
+
* @function waitingMyTurn
|
|
1065
|
+
* @param {Object} queueItem - the queue request which would have been returned from
|
|
1066
|
+
* requestQueueItem (required)
|
|
1067
|
+
* @param {Function} callback - a callback function to return the resulting Queue Object
|
|
1068
|
+
*/
|
|
1069
|
+
waitingMyTurn(queueItem, callback) {
|
|
1070
|
+
const origin = `${this.myid}-throttle-waitingMyTurn`;
|
|
1071
|
+
log.trace(origin);
|
|
1072
|
+
|
|
1073
|
+
try {
|
|
1074
|
+
if (queueItem === null || queueItem === undefined) {
|
|
1075
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['queue item'], null, null, null);
|
|
1076
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1077
|
+
return callback(null, errorObj);
|
|
1078
|
+
}
|
|
1079
|
+
if (queueItem.request_id === undefined || Number(queueItem.request_id) < 0) {
|
|
1080
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['queue item -> request id'], null, null, null);
|
|
1081
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1082
|
+
return callback(null, errorObj);
|
|
1083
|
+
}
|
|
1084
|
+
if (queueItem.transNum === undefined || Number(queueItem.transNum) < 0) {
|
|
1085
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['queue item -> transaction number'], null, null, null);
|
|
1086
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1087
|
+
return callback(null, errorObj);
|
|
1088
|
+
}
|
|
1089
|
+
if ((queueItem.active !== undefined && queueItem.active)
|
|
1090
|
+
|| (queueItem.end !== undefined && queueItem.end !== null)) {
|
|
1091
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Item In Wrong State', ['already started'], null, null, null);
|
|
1092
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1093
|
+
return callback(null, errorObj);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// check if there is an actual turn available
|
|
1097
|
+
return checkTurnAvailable(queueItem.request_id, queueItem.transNum, (toRun, cerror) => {
|
|
1098
|
+
if (cerror) {
|
|
1099
|
+
return callback(null, cerror);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// is it my turn to run
|
|
1103
|
+
if (toRun === 0) {
|
|
1104
|
+
return claimTurn(this.dbUtil, queueColl, queueItem, callback);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
if (toRun <= (3 * concurrentMax)) {
|
|
1108
|
+
// if I have less than 3 wait cycles, OK to start running checks faster
|
|
1109
|
+
return gettingCloseInterval(queueItem.request_id, queueItem.transNum, (waitEnd, gerror) => {
|
|
1110
|
+
if (gerror) {
|
|
1111
|
+
return callback(null, gerror);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
log.spam(`${origin}: ${waitEnd}`);
|
|
1115
|
+
return claimTurn(this.dbUtil, queueColl, queueItem, callback);
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
// set divisor to be 1 if concurrentMax is 0
|
|
1119
|
+
const concurrentMaxDivisor = concurrentMax !== 0 ? concurrentMax : 1;
|
|
1120
|
+
// calculate the wait for the outer timeout (this one needs to get us close to our run time)
|
|
1121
|
+
let outerInterval = (toRun / concurrentMaxDivisor) * (expireTimeout + (avgTotal / avgSize));
|
|
1122
|
+
|
|
1123
|
+
// Use the 90% of the outer intverval or the outer Interval -2 seconds which ever is greater
|
|
1124
|
+
if ((outerInterval * 0.1) > 2000) {
|
|
1125
|
+
outerInterval -= 2000;
|
|
1126
|
+
} else {
|
|
1127
|
+
outerInterval *= 0.95;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
log.debug(`${origin}: Request ${queueItem.request_id} Transaction ${queueItem.transNum} Outer Interval set to: ${outerInterval}`);
|
|
1131
|
+
|
|
1132
|
+
// outer interval to get a turn request
|
|
1133
|
+
// The outer interval is really used to get us close to when we should run
|
|
1134
|
+
const intervalObject = setTimeout(() => {
|
|
1135
|
+
clearTimeout(intervalObject);
|
|
1136
|
+
|
|
1137
|
+
// recurrsive call in case re-estimate is needed
|
|
1138
|
+
// (abort, things taking longer than approximation, etc)
|
|
1139
|
+
return this.waitingMyTurn(queueItem, callback);
|
|
1140
|
+
}, outerInterval);
|
|
1141
|
+
});
|
|
1142
|
+
} catch (e) {
|
|
1143
|
+
// handle any exception
|
|
1144
|
+
const errorObj = this.transUtil.checkAndReturn(e, origin, 'Issue while waiting turn');
|
|
1145
|
+
return callback(null, errorObj);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/**
|
|
1150
|
+
* finishTurn is used to mark the request completed. This means setting the end time
|
|
1151
|
+
* and changing the running flag to false. If there is no graceful expiration of the
|
|
1152
|
+
* concurrent request, then this makes the call to free the turn.
|
|
1153
|
+
*
|
|
1154
|
+
* @function finishTurn
|
|
1155
|
+
* @param {Object} queueItem - the queue request which would have been returned from
|
|
1156
|
+
* waitingMyTurn (required)
|
|
1157
|
+
* @param {Number} reqEnd - the time it took to execute the call (optional)
|
|
1158
|
+
* @param {Function} callback - a callback function to return the finished Queue Object
|
|
1159
|
+
*/
|
|
1160
|
+
finishTurn(queueItem, reqEnd, callback) {
|
|
1161
|
+
const origin = `${this.myid}-throttle-finishTurn`;
|
|
1162
|
+
log.trace(origin);
|
|
1163
|
+
|
|
1164
|
+
try {
|
|
1165
|
+
if (queueItem === null || queueItem === undefined) {
|
|
1166
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing Data', ['queue item'], null, null, null);
|
|
1167
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1168
|
+
return callback(null, errorObj);
|
|
1169
|
+
}
|
|
1170
|
+
if (queueItem.start === undefined || queueItem.start === null
|
|
1171
|
+
|| queueItem.active === undefined || !queueItem.active) {
|
|
1172
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Item In Wrong State', ['not started'], null, null, null);
|
|
1173
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1174
|
+
return callback(null, errorObj);
|
|
1175
|
+
}
|
|
1176
|
+
if (queueItem.end !== undefined && queueItem.end !== null) {
|
|
1177
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Item In Wrong State', ['already ended'], null, null, null);
|
|
1178
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1179
|
+
return callback(null, errorObj);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const cur = new Date();
|
|
1183
|
+
|
|
1184
|
+
// set up the update object to mark the queue item that the execution finished
|
|
1185
|
+
// set end and change running to false
|
|
1186
|
+
const data = {
|
|
1187
|
+
_id: queueItem._id,
|
|
1188
|
+
ph_instance: queueItem.ph_instance,
|
|
1189
|
+
request_id: queueItem.request_id,
|
|
1190
|
+
transNum: queueItem.transNum,
|
|
1191
|
+
priority: queueItem.priority,
|
|
1192
|
+
start: queueItem.start,
|
|
1193
|
+
end: cur.getTime(),
|
|
1194
|
+
active: queueItem.active,
|
|
1195
|
+
running: false,
|
|
1196
|
+
event: queueItem.event
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
// Lock the avgQlock while update the average time
|
|
1200
|
+
if (reqEnd !== null && reqEnd !== '') {
|
|
1201
|
+
this.alock.acquire(avgQlock, (done) => {
|
|
1202
|
+
const reqTimeMS = reqEnd / 100000;
|
|
1203
|
+
avgTotal = (avgTotal - avgQueue[avgPtr]) + reqTimeMS;
|
|
1204
|
+
avgQueue[avgPtr] = reqTimeMS;
|
|
1205
|
+
avgPtr += 1;
|
|
1206
|
+
|
|
1207
|
+
if (avgPtr === avgSize) {
|
|
1208
|
+
avgPtr = 0;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
done(avgTotal / avgSize);
|
|
1212
|
+
}, (ret, error) => {
|
|
1213
|
+
if (error) {
|
|
1214
|
+
log.error(`${origin}: Error from updating average queue: ${error}`);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
log.debug(`${origin}: Average run time now set to: ${ret}`);
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// if there is no timeout, delete the request and free the turn
|
|
1222
|
+
if (expireTimeout === 0) {
|
|
1223
|
+
return freeQueueItem(this.dbUtil, queueColl, queueItem, callback);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
if (numberPhs > 1) {
|
|
1227
|
+
// otherwise mark the request done, can not free it yet
|
|
1228
|
+
// If the number of Pronghorns is greater than 1, the queue is in the database
|
|
1229
|
+
// return MONGOQ.replaceOne({ _id: data._id }, data, (error, result) => {
|
|
1230
|
+
return this.dbUtil.replaceOne(queueColl, { _id: data._id }, data, {}, null, false, (error, result) => {
|
|
1231
|
+
if (error) {
|
|
1232
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Database Error', ['could not update item'], null, null, null);
|
|
1233
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1234
|
+
return callback(null, errorObj);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
log.debug(`${origin}: Mongo replace one returned ${result}`);
|
|
1238
|
+
return callback(data);
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// If the number of Pronghorns is 1, the queue is in memory
|
|
1243
|
+
// Lock the memQueue while finding and updating the item
|
|
1244
|
+
return qlock.acquire(memQlock, (done) => {
|
|
1245
|
+
const index = memQueue.indexOf(queueItem);
|
|
1246
|
+
|
|
1247
|
+
if (index >= 0) {
|
|
1248
|
+
memQueue[index] = data;
|
|
1249
|
+
done(memQueue[index]);
|
|
1250
|
+
} else {
|
|
1251
|
+
done(null, 'item not found in queue');
|
|
1252
|
+
}
|
|
1253
|
+
}, (ret, error) => {
|
|
1254
|
+
if (error) {
|
|
1255
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'No Queue Item', ['data'], null, null, null);
|
|
1256
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1257
|
+
return callback(null, errorObj);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
return callback(ret);
|
|
1261
|
+
});
|
|
1262
|
+
} catch (e) {
|
|
1263
|
+
// handle any exception
|
|
1264
|
+
const errorObj = this.transUtil.checkAndReturn(e, origin, 'Issue during finish turn');
|
|
1265
|
+
return callback(null, errorObj);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* getQueue is used to get information for all of the requests currently in the queue.
|
|
1271
|
+
*
|
|
1272
|
+
* @function getQueue
|
|
1273
|
+
* @param {Function} callback - a callback function to return the Queue
|
|
1274
|
+
*/
|
|
1275
|
+
getQueue(callback) {
|
|
1276
|
+
const origin = `${this.myid}-throttle-getQueue`;
|
|
1277
|
+
log.trace(origin);
|
|
1278
|
+
|
|
1279
|
+
try {
|
|
1280
|
+
return getQueueItems(this.dbUtil, queueColl, null, callback);
|
|
1281
|
+
} catch (e) {
|
|
1282
|
+
// handle any exception
|
|
1283
|
+
const errorObj = this.transUtil.checkAndReturn(e, origin, 'Issue during get queue');
|
|
1284
|
+
return callback(null, errorObj);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
module.exports = SystemXThrottle;
|