1 | "use strict";
|
2 |
|
3 | function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
|
4 |
|
5 | function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
6 |
|
7 | function _toArray(arr) { return _arrayWithHoles(arr) || _iterableToArray(arr) || _nonIterableRest(); }
|
8 |
|
9 | function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
|
10 |
|
11 | function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
|
12 |
|
13 | function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
14 |
|
15 | function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
16 |
|
17 | function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
18 |
|
19 | var Bottleneck,
|
20 | DEFAULT_PRIORITY,
|
21 | Events,
|
22 | Job,
|
23 | LocalDatastore,
|
24 | NUM_PRIORITIES,
|
25 | Queues,
|
26 | RedisDatastore,
|
27 | States,
|
28 | Sync,
|
29 | parser,
|
30 | splice = [].splice;
|
31 | NUM_PRIORITIES = 10;
|
32 | DEFAULT_PRIORITY = 5;
|
33 | parser = require("./parser");
|
34 | Queues = require("./Queues");
|
35 | Job = require("./Job");
|
36 | LocalDatastore = require("./LocalDatastore");
|
37 | RedisDatastore = require("./RedisDatastore");
|
38 | Events = require("./Events");
|
39 | States = require("./States");
|
40 | Sync = require("./Sync");
|
41 |
|
42 | Bottleneck = function () {
|
43 | class Bottleneck {
|
44 | constructor(options = {}, ...invalid) {
|
45 | var storeInstanceOptions, storeOptions;
|
46 | this._addToQueue = this._addToQueue.bind(this);
|
47 |
|
48 | this._validateOptions(options, invalid);
|
49 |
|
50 | parser.load(options, this.instanceDefaults, this);
|
51 | this._queues = new Queues(NUM_PRIORITIES);
|
52 | this._scheduled = {};
|
53 | this._states = new States(["RECEIVED", "QUEUED", "RUNNING", "EXECUTING"].concat(this.trackDoneStatus ? ["DONE"] : []));
|
54 | this._limiter = null;
|
55 | this.Events = new Events(this);
|
56 | this._submitLock = new Sync("submit", this.Promise);
|
57 | this._registerLock = new Sync("register", this.Promise);
|
58 | storeOptions = parser.load(options, this.storeDefaults, {});
|
59 |
|
60 | this._store = function () {
|
61 | if (this.datastore === "redis" || this.datastore === "ioredis" || this.connection != null) {
|
62 | storeInstanceOptions = parser.load(options, this.redisStoreDefaults, {});
|
63 | return new RedisDatastore(this, storeOptions, storeInstanceOptions);
|
64 | } else if (this.datastore === "local") {
|
65 | storeInstanceOptions = parser.load(options, this.localStoreDefaults, {});
|
66 | return new LocalDatastore(this, storeOptions, storeInstanceOptions);
|
67 | } else {
|
68 | throw new Bottleneck.prototype.BottleneckError(`Invalid datastore type: ${this.datastore}`);
|
69 | }
|
70 | }.call(this);
|
71 |
|
72 | this._queues.on("leftzero", () => {
|
73 | var ref;
|
74 | return (ref = this._store.heartbeat) != null ? typeof ref.ref === "function" ? ref.ref() : void 0 : void 0;
|
75 | });
|
76 |
|
77 | this._queues.on("zero", () => {
|
78 | var ref;
|
79 | return (ref = this._store.heartbeat) != null ? typeof ref.unref === "function" ? ref.unref() : void 0 : void 0;
|
80 | });
|
81 | }
|
82 |
|
83 | _validateOptions(options, invalid) {
|
84 | if (!(options != null && typeof options === "object" && invalid.length === 0)) {
|
85 | throw new Bottleneck.prototype.BottleneckError("Bottleneck v2 takes a single object argument. Refer to https://github.com/SGrondin/bottleneck#upgrading-to-v2 if you're upgrading from Bottleneck v1.");
|
86 | }
|
87 | }
|
88 |
|
89 | ready() {
|
90 | return this._store.ready;
|
91 | }
|
92 |
|
93 | clients() {
|
94 | return this._store.clients;
|
95 | }
|
96 |
|
97 | channel() {
|
98 | return `b_${this.id}`;
|
99 | }
|
100 |
|
101 | channel_client() {
|
102 | return `b_${this.id}_${this._store.clientId}`;
|
103 | }
|
104 |
|
105 | publish(message) {
|
106 | return this._store.__publish__(message);
|
107 | }
|
108 |
|
109 | disconnect(flush = true) {
|
110 | return this._store.__disconnect__(flush);
|
111 | }
|
112 |
|
113 | chain(_limiter) {
|
114 | this._limiter = _limiter;
|
115 | return this;
|
116 | }
|
117 |
|
118 | queued(priority) {
|
119 | return this._queues.queued(priority);
|
120 | }
|
121 |
|
122 | clusterQueued() {
|
123 | return this._store.__queued__();
|
124 | }
|
125 |
|
126 | empty() {
|
127 | return this.queued() === 0 && this._submitLock.isEmpty();
|
128 | }
|
129 |
|
130 | running() {
|
131 | return this._store.__running__();
|
132 | }
|
133 |
|
134 | done() {
|
135 | return this._store.__done__();
|
136 | }
|
137 |
|
138 | jobStatus(id) {
|
139 | return this._states.jobStatus(id);
|
140 | }
|
141 |
|
142 | jobs(status) {
|
143 | return this._states.statusJobs(status);
|
144 | }
|
145 |
|
146 | counts() {
|
147 | return this._states.statusCounts();
|
148 | }
|
149 |
|
150 | _randomIndex() {
|
151 | return Math.random().toString(36).slice(2);
|
152 | }
|
153 |
|
154 | check(weight = 1) {
|
155 | return this._store.__check__(weight);
|
156 | }
|
157 |
|
158 | _clearGlobalState(index) {
|
159 | if (this._scheduled[index] != null) {
|
160 | clearTimeout(this._scheduled[index].expiration);
|
161 | delete this._scheduled[index];
|
162 | return true;
|
163 | } else {
|
164 | return false;
|
165 | }
|
166 | }
|
167 |
|
168 | _free(index, job, options, eventInfo) {
|
169 | var _this = this;
|
170 |
|
171 | return _asyncToGenerator(function* () {
|
172 | var e, running;
|
173 |
|
174 | try {
|
175 | var _ref = yield _this._store.__free__(index, options.weight);
|
176 |
|
177 | running = _ref.running;
|
178 |
|
179 | _this.Events.trigger("debug", `Freed ${options.id}`, eventInfo);
|
180 |
|
181 | if (running === 0 && _this.empty()) {
|
182 | return _this.Events.trigger("idle");
|
183 | }
|
184 | } catch (error1) {
|
185 | e = error1;
|
186 | return _this.Events.trigger("error", e);
|
187 | }
|
188 | })();
|
189 | }
|
190 |
|
191 | _run(index, job, wait) {
|
192 | var clearGlobalState, free, run;
|
193 | job.doRun();
|
194 | clearGlobalState = this._clearGlobalState.bind(this, index);
|
195 | run = this._run.bind(this, index, job);
|
196 | free = this._free.bind(this, index, job);
|
197 | return this._scheduled[index] = {
|
198 | timeout: setTimeout(() => {
|
199 | return job.doExecute(this._limiter, clearGlobalState, run, free);
|
200 | }, wait),
|
201 | expiration: job.options.expiration != null ? setTimeout(function () {
|
202 | return job.doExpire(clearGlobalState, run, free);
|
203 | }, wait + job.options.expiration) : void 0,
|
204 | job: job
|
205 | };
|
206 | }
|
207 |
|
208 | _drainOne(capacity) {
|
209 | return this._registerLock.schedule(() => {
|
210 | var args, index, next, options, queue;
|
211 |
|
212 | if (this.queued() === 0) {
|
213 | return this.Promise.resolve(null);
|
214 | }
|
215 |
|
216 | queue = this._queues.getFirst();
|
217 |
|
218 | var _next2 = next = queue.first();
|
219 |
|
220 | options = _next2.options;
|
221 | args = _next2.args;
|
222 |
|
223 | if (capacity != null && options.weight > capacity) {
|
224 | return this.Promise.resolve(null);
|
225 | }
|
226 |
|
227 | this.Events.trigger("debug", `Draining ${options.id}`, {
|
228 | args,
|
229 | options
|
230 | });
|
231 | index = this._randomIndex();
|
232 | return this._store.__register__(index, options.weight, options.expiration).then(({
|
233 | success,
|
234 | wait,
|
235 | reservoir
|
236 | }) => {
|
237 | var empty;
|
238 | this.Events.trigger("debug", `Drained ${options.id}`, {
|
239 | success,
|
240 | args,
|
241 | options
|
242 | });
|
243 |
|
244 | if (success) {
|
245 | queue.shift();
|
246 | empty = this.empty();
|
247 |
|
248 | if (empty) {
|
249 | this.Events.trigger("empty");
|
250 | }
|
251 |
|
252 | if (reservoir === 0) {
|
253 | this.Events.trigger("depleted", empty);
|
254 | }
|
255 |
|
256 | this._run(index, next, wait);
|
257 |
|
258 | return this.Promise.resolve(options.weight);
|
259 | } else {
|
260 | return this.Promise.resolve(null);
|
261 | }
|
262 | });
|
263 | });
|
264 | }
|
265 |
|
266 | _drainAll(capacity, total = 0) {
|
267 | return this._drainOne(capacity).then(drained => {
|
268 | var newCapacity;
|
269 |
|
270 | if (drained != null) {
|
271 | newCapacity = capacity != null ? capacity - drained : capacity;
|
272 | return this._drainAll(newCapacity, total + drained);
|
273 | } else {
|
274 | return this.Promise.resolve(total);
|
275 | }
|
276 | }).catch(e => {
|
277 | return this.Events.trigger("error", e);
|
278 | });
|
279 | }
|
280 |
|
281 | _dropAllQueued(message) {
|
282 | return this._queues.shiftAll(function (job) {
|
283 | return job.doDrop({
|
284 | message
|
285 | });
|
286 | });
|
287 | }
|
288 |
|
289 | stop(options = {}) {
|
290 | var done, waitForExecuting;
|
291 | options = parser.load(options, this.stopDefaults);
|
292 |
|
293 | waitForExecuting = at => {
|
294 | var finished;
|
295 |
|
296 | finished = () => {
|
297 | var counts;
|
298 | counts = this._states.counts;
|
299 | return counts[0] + counts[1] + counts[2] + counts[3] === at;
|
300 | };
|
301 |
|
302 | return new this.Promise((resolve, reject) => {
|
303 | if (finished()) {
|
304 | return resolve();
|
305 | } else {
|
306 | return this.on("done", () => {
|
307 | if (finished()) {
|
308 | this.removeAllListeners("done");
|
309 | return resolve();
|
310 | }
|
311 | });
|
312 | }
|
313 | });
|
314 | };
|
315 |
|
316 | done = options.dropWaitingJobs ? (this._run = function (index, next) {
|
317 | return next.doDrop({
|
318 | message: options.dropErrorMessage
|
319 | });
|
320 | }, this._drainOne = () => {
|
321 | return this.Promise.resolve(null);
|
322 | }, this._registerLock.schedule(() => {
|
323 | return this._submitLock.schedule(() => {
|
324 | var k, ref, v;
|
325 | ref = this._scheduled;
|
326 |
|
327 | for (k in ref) {
|
328 | v = ref[k];
|
329 |
|
330 | if (this.jobStatus(v.job.options.id) === "RUNNING") {
|
331 | clearTimeout(v.timeout);
|
332 | clearTimeout(v.expiration);
|
333 | v.job.doDrop({
|
334 | message: options.dropErrorMessage
|
335 | });
|
336 | }
|
337 | }
|
338 |
|
339 | this._dropAllQueued(options.dropErrorMessage);
|
340 |
|
341 | return waitForExecuting(0);
|
342 | });
|
343 | })) : this.schedule({
|
344 | priority: NUM_PRIORITIES - 1,
|
345 | weight: 0
|
346 | }, () => {
|
347 | return waitForExecuting(1);
|
348 | });
|
349 |
|
350 | this._receive = function (job) {
|
351 | return job._reject(new Bottleneck.prototype.BottleneckError(options.enqueueErrorMessage));
|
352 | };
|
353 |
|
354 | this.stop = () => {
|
355 | return this.Promise.reject(new Bottleneck.prototype.BottleneckError("stop() has already been called"));
|
356 | };
|
357 |
|
358 | return done;
|
359 | }
|
360 |
|
361 | _addToQueue(job) {
|
362 | var _this2 = this;
|
363 |
|
364 | return _asyncToGenerator(function* () {
|
365 | var args, blocked, error, options, reachedHWM, shifted, strategy;
|
366 | args = job.args;
|
367 | options = job.options;
|
368 |
|
369 | try {
|
370 | var _ref2 = yield _this2._store.__submit__(_this2.queued(), options.weight);
|
371 |
|
372 | reachedHWM = _ref2.reachedHWM;
|
373 | blocked = _ref2.blocked;
|
374 | strategy = _ref2.strategy;
|
375 | } catch (error1) {
|
376 | error = error1;
|
377 |
|
378 | _this2.Events.trigger("debug", `Could not queue ${options.id}`, {
|
379 | args,
|
380 | options,
|
381 | error
|
382 | });
|
383 |
|
384 | job.doDrop({
|
385 | error
|
386 | });
|
387 | return false;
|
388 | }
|
389 |
|
390 | if (blocked) {
|
391 | job.doDrop();
|
392 | return true;
|
393 | } else if (reachedHWM) {
|
394 | shifted = strategy === Bottleneck.prototype.strategy.LEAK ? _this2._queues.shiftLastFrom(options.priority) : strategy === Bottleneck.prototype.strategy.OVERFLOW_PRIORITY ? _this2._queues.shiftLastFrom(options.priority + 1) : strategy === Bottleneck.prototype.strategy.OVERFLOW ? job : void 0;
|
395 |
|
396 | if (shifted != null) {
|
397 | shifted.doDrop();
|
398 | }
|
399 |
|
400 | if (shifted == null || strategy === Bottleneck.prototype.strategy.OVERFLOW) {
|
401 | if (shifted == null) {
|
402 | job.doDrop();
|
403 | }
|
404 |
|
405 | return reachedHWM;
|
406 | }
|
407 | }
|
408 |
|
409 | job.doQueue(reachedHWM, blocked);
|
410 |
|
411 | _this2._queues.push(job);
|
412 |
|
413 | yield _this2._drainAll();
|
414 | return reachedHWM;
|
415 | })();
|
416 | }
|
417 |
|
418 | _receive(job) {
|
419 | if (this._states.jobStatus(job.options.id) != null) {
|
420 | job._reject(new Bottleneck.prototype.BottleneckError(`A job with the same id already exists (id=${job.options.id})`));
|
421 |
|
422 | return false;
|
423 | } else {
|
424 | job.doReceive();
|
425 | return this._submitLock.schedule(this._addToQueue, job);
|
426 | }
|
427 | }
|
428 |
|
429 | submit(...args) {
|
430 | var cb, fn, job, options, ref, ref1, task;
|
431 |
|
432 | if (typeof args[0] === "function") {
|
433 | var _ref3, _ref4, _splice$call, _splice$call2;
|
434 |
|
435 | ref = args, (_ref3 = ref, _ref4 = _toArray(_ref3), fn = _ref4[0], args = _ref4.slice(1), _ref3), (_splice$call = splice.call(args, -1), _splice$call2 = _slicedToArray(_splice$call, 1), cb = _splice$call2[0], _splice$call);
|
436 | options = parser.load({}, this.jobDefaults);
|
437 | } else {
|
438 | var _ref5, _ref6, _splice$call3, _splice$call4;
|
439 |
|
440 | ref1 = args, (_ref5 = ref1, _ref6 = _toArray(_ref5), options = _ref6[0], fn = _ref6[1], args = _ref6.slice(2), _ref5), (_splice$call3 = splice.call(args, -1), _splice$call4 = _slicedToArray(_splice$call3, 1), cb = _splice$call4[0], _splice$call3);
|
441 | options = parser.load(options, this.jobDefaults);
|
442 | }
|
443 |
|
444 | task = (...args) => {
|
445 | return new this.Promise(function (resolve, reject) {
|
446 | return fn(...args, function (...args) {
|
447 | return (args[0] != null ? reject : resolve)(args);
|
448 | });
|
449 | });
|
450 | };
|
451 |
|
452 | job = new Job(task, args, options, this.jobDefaults, this.rejectOnDrop, this.Events, this._states, this.Promise);
|
453 | job.promise.then(function (args) {
|
454 | return typeof cb === "function" ? cb(...args) : void 0;
|
455 | }).catch(function (args) {
|
456 | if (Array.isArray(args)) {
|
457 | return typeof cb === "function" ? cb(...args) : void 0;
|
458 | } else {
|
459 | return typeof cb === "function" ? cb(args) : void 0;
|
460 | }
|
461 | });
|
462 | return this._receive(job);
|
463 | }
|
464 |
|
465 | schedule(...args) {
|
466 | var job, options, task;
|
467 |
|
468 | if (typeof args[0] === "function") {
|
469 | var _args = args;
|
470 |
|
471 | var _args2 = _toArray(_args);
|
472 |
|
473 | task = _args2[0];
|
474 | args = _args2.slice(1);
|
475 | options = {};
|
476 | } else {
|
477 | var _args3 = args;
|
478 |
|
479 | var _args4 = _toArray(_args3);
|
480 |
|
481 | options = _args4[0];
|
482 | task = _args4[1];
|
483 | args = _args4.slice(2);
|
484 | }
|
485 |
|
486 | job = new Job(task, args, options, this.jobDefaults, this.rejectOnDrop, this.Events, this._states, this.Promise);
|
487 |
|
488 | this._receive(job);
|
489 |
|
490 | return job.promise;
|
491 | }
|
492 |
|
493 | wrap(fn) {
|
494 | var schedule, wrapped;
|
495 | schedule = this.schedule.bind(this);
|
496 |
|
497 | wrapped = function wrapped(...args) {
|
498 | return schedule(fn.bind(this), ...args);
|
499 | };
|
500 |
|
501 | wrapped.withOptions = function (options, ...args) {
|
502 | return schedule(options, fn, ...args);
|
503 | };
|
504 |
|
505 | return wrapped;
|
506 | }
|
507 |
|
508 | updateSettings(options = {}) {
|
509 | var _this3 = this;
|
510 |
|
511 | return _asyncToGenerator(function* () {
|
512 | yield _this3._store.__updateSettings__(parser.overwrite(options, _this3.storeDefaults));
|
513 | parser.overwrite(options, _this3.instanceDefaults, _this3);
|
514 | return _this3;
|
515 | })();
|
516 | }
|
517 |
|
518 | currentReservoir() {
|
519 | return this._store.__currentReservoir__();
|
520 | }
|
521 |
|
522 | incrementReservoir(incr = 0) {
|
523 | return this._store.__incrementReservoir__(incr);
|
524 | }
|
525 |
|
526 | }
|
527 |
|
528 | ;
|
529 | Bottleneck.default = Bottleneck;
|
530 | Bottleneck.Events = Events;
|
531 | Bottleneck.version = Bottleneck.prototype.version = require("./version.json").version;
|
532 | Bottleneck.strategy = Bottleneck.prototype.strategy = {
|
533 | LEAK: 1,
|
534 | OVERFLOW: 2,
|
535 | OVERFLOW_PRIORITY: 4,
|
536 | BLOCK: 3
|
537 | };
|
538 | Bottleneck.BottleneckError = Bottleneck.prototype.BottleneckError = require("./BottleneckError");
|
539 | Bottleneck.Group = Bottleneck.prototype.Group = require("./Group");
|
540 | Bottleneck.RedisConnection = Bottleneck.prototype.RedisConnection = require("./RedisConnection");
|
541 | Bottleneck.IORedisConnection = Bottleneck.prototype.IORedisConnection = require("./IORedisConnection");
|
542 | Bottleneck.Batcher = Bottleneck.prototype.Batcher = require("./Batcher");
|
543 | Bottleneck.prototype.jobDefaults = {
|
544 | priority: DEFAULT_PRIORITY,
|
545 | weight: 1,
|
546 | expiration: null,
|
547 | id: "<no-id>"
|
548 | };
|
549 | Bottleneck.prototype.storeDefaults = {
|
550 | maxConcurrent: null,
|
551 | minTime: 0,
|
552 | highWater: null,
|
553 | strategy: Bottleneck.prototype.strategy.LEAK,
|
554 | penalty: null,
|
555 | reservoir: null,
|
556 | reservoirRefreshInterval: null,
|
557 | reservoirRefreshAmount: null,
|
558 | reservoirIncreaseInterval: null,
|
559 | reservoirIncreaseAmount: null,
|
560 | reservoirIncreaseMaximum: null
|
561 | };
|
562 | Bottleneck.prototype.localStoreDefaults = {
|
563 | Promise: Promise,
|
564 | timeout: null,
|
565 | heartbeatInterval: 250
|
566 | };
|
567 | Bottleneck.prototype.redisStoreDefaults = {
|
568 | Promise: Promise,
|
569 | timeout: null,
|
570 | heartbeatInterval: 5000,
|
571 | clientTimeout: 10000,
|
572 | Redis: null,
|
573 | clientOptions: {},
|
574 | clusterNodes: null,
|
575 | clearDatastore: false,
|
576 | connection: null
|
577 | };
|
578 | Bottleneck.prototype.instanceDefaults = {
|
579 | datastore: "local",
|
580 | connection: null,
|
581 | id: "<no-id>",
|
582 | rejectOnDrop: true,
|
583 | trackDoneStatus: false,
|
584 | Promise: Promise
|
585 | };
|
586 | Bottleneck.prototype.stopDefaults = {
|
587 | enqueueErrorMessage: "This limiter has been stopped and cannot accept new jobs.",
|
588 | dropWaitingJobs: true,
|
589 | dropErrorMessage: "This limiter has been stopped."
|
590 | };
|
591 | return Bottleneck;
|
592 | }.call(void 0);
|
593 |
|
594 | module.exports = Bottleneck; |
\ | No newline at end of file |