scripts.js 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229
  1. /**
  2. * Includes all the scripts needed by the queue and jobs.
  3. */
  4. 'use strict';
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.Scripts = void 0;
  7. exports.raw2NextJobData = raw2NextJobData;
  8. const msgpackr_1 = require("msgpackr");
  9. const packer = new msgpackr_1.Packr({
  10. useRecords: false,
  11. encodeUndefinedAsNil: true,
  12. });
  13. const pack = packer.pack;
  14. const enums_1 = require("../enums");
  15. const utils_1 = require("../utils");
  16. const version_1 = require("../version");
  17. const errors_1 = require("./errors");
  18. class Scripts {
  19. constructor(queue) {
  20. this.queue = queue;
  21. this.version = version_1.version;
  22. const queueKeys = this.queue.keys;
  23. this.moveToFinishedKeys = [
  24. queueKeys.wait,
  25. queueKeys.active,
  26. queueKeys.prioritized,
  27. queueKeys.events,
  28. queueKeys.stalled,
  29. queueKeys.limiter,
  30. queueKeys.delayed,
  31. queueKeys.paused,
  32. queueKeys.meta,
  33. queueKeys.pc,
  34. undefined,
  35. undefined,
  36. undefined,
  37. undefined,
  38. ];
  39. }
  40. execCommand(client, commandName, args) {
  41. const commandNameWithVersion = `${commandName}:${this.version}`;
  42. return client[commandNameWithVersion](args);
  43. }
  44. async isJobInList(listKey, jobId) {
  45. const client = await this.queue.client;
  46. let result;
  47. if ((0, utils_1.isRedisVersionLowerThan)(this.queue.redisVersion, '6.0.6', this.queue.databaseType)) {
  48. result = await this.execCommand(client, 'isJobInList', [listKey, jobId]);
  49. }
  50. else {
  51. result = await client.lpos(listKey, jobId);
  52. }
  53. return Number.isInteger(result);
  54. }
  55. addDelayedJobArgs(job, encodedOpts, args) {
  56. const queueKeys = this.queue.keys;
  57. const keys = [
  58. queueKeys.marker,
  59. queueKeys.meta,
  60. queueKeys.id,
  61. queueKeys.delayed,
  62. queueKeys.completed,
  63. queueKeys.events,
  64. ];
  65. keys.push(pack(args), job.data, encodedOpts);
  66. return keys;
  67. }
  68. addDelayedJob(client, job, encodedOpts, args) {
  69. const argsList = this.addDelayedJobArgs(job, encodedOpts, args);
  70. return this.execCommand(client, 'addDelayedJob', argsList);
  71. }
  72. addPrioritizedJobArgs(job, encodedOpts, args) {
  73. const queueKeys = this.queue.keys;
  74. const keys = [
  75. queueKeys.marker,
  76. queueKeys.meta,
  77. queueKeys.id,
  78. queueKeys.prioritized,
  79. queueKeys.delayed,
  80. queueKeys.completed,
  81. queueKeys.active,
  82. queueKeys.events,
  83. queueKeys.pc,
  84. ];
  85. keys.push(pack(args), job.data, encodedOpts);
  86. return keys;
  87. }
  88. addPrioritizedJob(client, job, encodedOpts, args) {
  89. const argsList = this.addPrioritizedJobArgs(job, encodedOpts, args);
  90. return this.execCommand(client, 'addPrioritizedJob', argsList);
  91. }
  92. addParentJobArgs(job, encodedOpts, args) {
  93. const queueKeys = this.queue.keys;
  94. const keys = [
  95. queueKeys.meta,
  96. queueKeys.id,
  97. queueKeys.delayed,
  98. queueKeys['waiting-children'],
  99. queueKeys.completed,
  100. queueKeys.events,
  101. ];
  102. keys.push(pack(args), job.data, encodedOpts);
  103. return keys;
  104. }
  105. addParentJob(client, job, encodedOpts, args) {
  106. const argsList = this.addParentJobArgs(job, encodedOpts, args);
  107. return this.execCommand(client, 'addParentJob', argsList);
  108. }
  109. addStandardJobArgs(job, encodedOpts, args) {
  110. const queueKeys = this.queue.keys;
  111. const keys = [
  112. queueKeys.wait,
  113. queueKeys.paused,
  114. queueKeys.meta,
  115. queueKeys.id,
  116. queueKeys.completed,
  117. queueKeys.delayed,
  118. queueKeys.active,
  119. queueKeys.events,
  120. queueKeys.marker,
  121. ];
  122. keys.push(pack(args), job.data, encodedOpts);
  123. return keys;
  124. }
  125. addStandardJob(client, job, encodedOpts, args) {
  126. const argsList = this.addStandardJobArgs(job, encodedOpts, args);
  127. return this.execCommand(client, 'addStandardJob', argsList);
  128. }
  129. async addJob(client, job, opts, jobId, parentKeyOpts = {}) {
  130. const queueKeys = this.queue.keys;
  131. const parent = job.parent;
  132. const args = [
  133. queueKeys[''],
  134. typeof jobId !== 'undefined' ? jobId : '',
  135. job.name,
  136. job.timestamp,
  137. job.parentKey || null,
  138. parentKeyOpts.parentDependenciesKey || null,
  139. parent,
  140. job.repeatJobKey,
  141. job.deduplicationId ? `${queueKeys.de}:${job.deduplicationId}` : null,
  142. ];
  143. let encodedOpts;
  144. if (opts.repeat) {
  145. const repeat = Object.assign({}, opts.repeat);
  146. if (repeat.startDate) {
  147. repeat.startDate = +new Date(repeat.startDate);
  148. }
  149. if (repeat.endDate) {
  150. repeat.endDate = +new Date(repeat.endDate);
  151. }
  152. encodedOpts = pack(Object.assign(Object.assign({}, opts), { repeat }));
  153. }
  154. else {
  155. encodedOpts = pack(opts);
  156. }
  157. let result;
  158. if (parentKeyOpts.addToWaitingChildren) {
  159. result = await this.addParentJob(client, job, encodedOpts, args);
  160. }
  161. else if (typeof opts.delay == 'number' && opts.delay > 0) {
  162. result = await this.addDelayedJob(client, job, encodedOpts, args);
  163. }
  164. else if (opts.priority) {
  165. result = await this.addPrioritizedJob(client, job, encodedOpts, args);
  166. }
  167. else {
  168. result = await this.addStandardJob(client, job, encodedOpts, args);
  169. }
  170. if (result < 0) {
  171. throw this.finishedErrors({
  172. code: result,
  173. parentKey: parentKeyOpts.parentKey,
  174. command: 'addJob',
  175. });
  176. }
  177. return result;
  178. }
  179. pauseArgs(pause) {
  180. let src = 'wait', dst = 'paused';
  181. if (!pause) {
  182. src = 'paused';
  183. dst = 'wait';
  184. }
  185. const keys = [src, dst, 'meta', 'prioritized'].map((name) => this.queue.toKey(name));
  186. keys.push(this.queue.keys.events, this.queue.keys.delayed, this.queue.keys.marker);
  187. const args = [pause ? 'paused' : 'resumed'];
  188. return keys.concat(args);
  189. }
  190. async pause(pause) {
  191. const client = await this.queue.client;
  192. const args = this.pauseArgs(pause);
  193. return this.execCommand(client, 'pause', args);
  194. }
  195. addRepeatableJobArgs(customKey, nextMillis, opts, legacyCustomKey) {
  196. const queueKeys = this.queue.keys;
  197. const keys = [
  198. queueKeys.repeat,
  199. queueKeys.delayed,
  200. ];
  201. const args = [
  202. nextMillis,
  203. pack(opts),
  204. legacyCustomKey,
  205. customKey,
  206. queueKeys[''],
  207. ];
  208. return keys.concat(args);
  209. }
  210. async addRepeatableJob(customKey, nextMillis, opts, legacyCustomKey) {
  211. const client = await this.queue.client;
  212. const args = this.addRepeatableJobArgs(customKey, nextMillis, opts, legacyCustomKey);
  213. return this.execCommand(client, 'addRepeatableJob', args);
  214. }
  215. async removeDeduplicationKey(deduplicationId, jobId) {
  216. const client = await this.queue.client;
  217. const queueKeys = this.queue.keys;
  218. const keys = [`${queueKeys.de}:${deduplicationId}`];
  219. const args = [jobId];
  220. return this.execCommand(client, 'removeDeduplicationKey', keys.concat(args));
  221. }
  222. async addJobScheduler(jobSchedulerId, nextMillis, templateData, templateOpts, opts, delayedJobOpts,
  223. // The job id of the job that produced this next iteration
  224. producerId) {
  225. const client = await this.queue.client;
  226. const queueKeys = this.queue.keys;
  227. const keys = [
  228. queueKeys.repeat,
  229. queueKeys.delayed,
  230. queueKeys.wait,
  231. queueKeys.paused,
  232. queueKeys.meta,
  233. queueKeys.prioritized,
  234. queueKeys.marker,
  235. queueKeys.id,
  236. queueKeys.events,
  237. queueKeys.pc,
  238. queueKeys.active,
  239. ];
  240. const args = [
  241. nextMillis,
  242. pack(opts),
  243. jobSchedulerId,
  244. templateData,
  245. pack(templateOpts),
  246. pack(delayedJobOpts),
  247. Date.now(),
  248. queueKeys[''],
  249. producerId ? this.queue.toKey(producerId) : '',
  250. ];
  251. const result = await this.execCommand(client, 'addJobScheduler', keys.concat(args));
  252. if (typeof result === 'number' && result < 0) {
  253. throw this.finishedErrors({
  254. code: result,
  255. command: 'addJobScheduler',
  256. });
  257. }
  258. return result;
  259. }
  260. async updateRepeatableJobMillis(client, customKey, nextMillis, legacyCustomKey) {
  261. const args = [
  262. this.queue.keys.repeat,
  263. nextMillis,
  264. customKey,
  265. legacyCustomKey,
  266. ];
  267. return this.execCommand(client, 'updateRepeatableJobMillis', args);
  268. }
  269. async updateJobSchedulerNextMillis(jobSchedulerId, nextMillis, templateData, delayedJobOpts,
  270. // The job id of the job that produced this next iteration - TODO: remove in next breaking change
  271. producerId) {
  272. const client = await this.queue.client;
  273. const queueKeys = this.queue.keys;
  274. const keys = [
  275. queueKeys.repeat,
  276. queueKeys.delayed,
  277. queueKeys.wait,
  278. queueKeys.paused,
  279. queueKeys.meta,
  280. queueKeys.prioritized,
  281. queueKeys.marker,
  282. queueKeys.id,
  283. queueKeys.events,
  284. queueKeys.pc,
  285. producerId ? this.queue.toKey(producerId) : '',
  286. queueKeys.active,
  287. ];
  288. const args = [
  289. nextMillis,
  290. jobSchedulerId,
  291. templateData,
  292. pack(delayedJobOpts),
  293. Date.now(),
  294. queueKeys[''],
  295. producerId,
  296. ];
  297. return this.execCommand(client, 'updateJobScheduler', keys.concat(args));
  298. }
  299. removeRepeatableArgs(legacyRepeatJobId, repeatConcatOptions, repeatJobKey) {
  300. const queueKeys = this.queue.keys;
  301. const keys = [queueKeys.repeat, queueKeys.delayed, queueKeys.events];
  302. const args = [
  303. legacyRepeatJobId,
  304. this.getRepeatConcatOptions(repeatConcatOptions, repeatJobKey),
  305. repeatJobKey,
  306. queueKeys[''],
  307. ];
  308. return keys.concat(args);
  309. }
  310. // TODO: remove this check in next breaking change
  311. getRepeatConcatOptions(repeatConcatOptions, repeatJobKey) {
  312. if (repeatJobKey && repeatJobKey.split(':').length > 2) {
  313. return repeatJobKey;
  314. }
  315. return repeatConcatOptions;
  316. }
  317. async removeRepeatable(legacyRepeatJobId, repeatConcatOptions, repeatJobKey) {
  318. const client = await this.queue.client;
  319. const args = this.removeRepeatableArgs(legacyRepeatJobId, repeatConcatOptions, repeatJobKey);
  320. return this.execCommand(client, 'removeRepeatable', args);
  321. }
  322. async removeJobScheduler(jobSchedulerId) {
  323. const client = await this.queue.client;
  324. const queueKeys = this.queue.keys;
  325. const keys = [queueKeys.repeat, queueKeys.delayed, queueKeys.events];
  326. const args = [jobSchedulerId, queueKeys['']];
  327. return this.execCommand(client, 'removeJobScheduler', keys.concat(args));
  328. }
  329. removeArgs(jobId, removeChildren) {
  330. const keys = [jobId, 'repeat'].map(name => this.queue.toKey(name));
  331. const args = [jobId, removeChildren ? 1 : 0, this.queue.toKey('')];
  332. return keys.concat(args);
  333. }
  334. async remove(jobId, removeChildren) {
  335. const client = await this.queue.client;
  336. const args = this.removeArgs(jobId, removeChildren);
  337. const result = await this.execCommand(client, 'removeJob', args);
  338. if (result < 0) {
  339. throw this.finishedErrors({
  340. code: result,
  341. jobId,
  342. command: 'removeJob',
  343. });
  344. }
  345. return result;
  346. }
  347. async removeUnprocessedChildren(jobId) {
  348. const client = await this.queue.client;
  349. const args = [
  350. this.queue.toKey(jobId),
  351. this.queue.keys.meta,
  352. this.queue.toKey(''),
  353. jobId,
  354. ];
  355. await this.execCommand(client, 'removeUnprocessedChildren', args);
  356. }
  357. async extendLock(jobId, token, duration, client) {
  358. client = client || (await this.queue.client);
  359. const args = [
  360. this.queue.toKey(jobId) + ':lock',
  361. this.queue.keys.stalled,
  362. token,
  363. duration,
  364. jobId,
  365. ];
  366. return this.execCommand(client, 'extendLock', args);
  367. }
  368. async extendLocks(jobIds, tokens, duration) {
  369. const client = await this.queue.client;
  370. const args = [
  371. this.queue.keys.stalled,
  372. this.queue.toKey(''),
  373. pack(tokens),
  374. pack(jobIds),
  375. duration,
  376. ];
  377. return this.execCommand(client, 'extendLocks', args);
  378. }
  379. async updateData(job, data) {
  380. const client = await this.queue.client;
  381. const keys = [this.queue.toKey(job.id)];
  382. const dataJson = JSON.stringify(data);
  383. const result = await this.execCommand(client, 'updateData', keys.concat([dataJson]));
  384. if (result < 0) {
  385. throw this.finishedErrors({
  386. code: result,
  387. jobId: job.id,
  388. command: 'updateData',
  389. });
  390. }
  391. }
  392. async updateProgress(jobId, progress) {
  393. const client = await this.queue.client;
  394. const keys = [
  395. this.queue.toKey(jobId),
  396. this.queue.keys.events,
  397. this.queue.keys.meta,
  398. ];
  399. const progressJson = JSON.stringify(progress);
  400. const result = await this.execCommand(client, 'updateProgress', keys.concat([jobId, progressJson]));
  401. if (result < 0) {
  402. throw this.finishedErrors({
  403. code: result,
  404. jobId,
  405. command: 'updateProgress',
  406. });
  407. }
  408. }
  409. async addLog(jobId, logRow, keepLogs) {
  410. const client = await this.queue.client;
  411. const keys = [
  412. this.queue.toKey(jobId),
  413. this.queue.toKey(jobId) + ':logs',
  414. ];
  415. const result = await this.execCommand(client, 'addLog', keys.concat([jobId, logRow, keepLogs ? keepLogs : '']));
  416. if (result < 0) {
  417. throw this.finishedErrors({
  418. code: result,
  419. jobId,
  420. command: 'addLog',
  421. });
  422. }
  423. return result;
  424. }
  425. moveToFinishedArgs(job, val, propVal, shouldRemove, target, token, timestamp, fetchNext = true, fieldsToUpdate) {
  426. var _a, _b, _c, _d, _e, _f, _g;
  427. const queueKeys = this.queue.keys;
  428. const opts = this.queue.opts;
  429. const workerKeepJobs = target === 'completed' ? opts.removeOnComplete : opts.removeOnFail;
  430. const metricsKey = this.queue.toKey(`metrics:${target}`);
  431. const keys = this.moveToFinishedKeys;
  432. keys[10] = queueKeys[target];
  433. keys[11] = this.queue.toKey((_a = job.id) !== null && _a !== void 0 ? _a : '');
  434. keys[12] = metricsKey;
  435. keys[13] = this.queue.keys.marker;
  436. const keepJobs = this.getKeepJobs(shouldRemove, workerKeepJobs);
  437. const args = [
  438. job.id,
  439. timestamp,
  440. propVal,
  441. typeof val === 'undefined' ? 'null' : val,
  442. target,
  443. !fetchNext || this.queue.closing ? 0 : 1,
  444. queueKeys[''],
  445. pack({
  446. token,
  447. name: opts.name,
  448. keepJobs,
  449. limiter: opts.limiter,
  450. lockDuration: opts.lockDuration,
  451. attempts: job.opts.attempts,
  452. maxMetricsSize: ((_b = opts.metrics) === null || _b === void 0 ? void 0 : _b.maxDataPoints)
  453. ? (_c = opts.metrics) === null || _c === void 0 ? void 0 : _c.maxDataPoints
  454. : '',
  455. fpof: !!((_d = job.opts) === null || _d === void 0 ? void 0 : _d.failParentOnFailure),
  456. cpof: !!((_e = job.opts) === null || _e === void 0 ? void 0 : _e.continueParentOnFailure),
  457. idof: !!((_f = job.opts) === null || _f === void 0 ? void 0 : _f.ignoreDependencyOnFailure),
  458. rdof: !!((_g = job.opts) === null || _g === void 0 ? void 0 : _g.removeDependencyOnFailure),
  459. }),
  460. fieldsToUpdate ? pack((0, utils_1.objectToFlatArray)(fieldsToUpdate)) : void 0,
  461. ];
  462. return keys.concat(args);
  463. }
  464. getKeepJobs(shouldRemove, workerKeepJobs) {
  465. if (typeof shouldRemove === 'undefined') {
  466. return workerKeepJobs || { count: shouldRemove ? 0 : -1 };
  467. }
  468. return typeof shouldRemove === 'object'
  469. ? shouldRemove
  470. : typeof shouldRemove === 'number'
  471. ? { count: shouldRemove }
  472. : { count: shouldRemove ? 0 : -1 };
  473. }
  474. async moveToFinished(jobId, args) {
  475. const client = await this.queue.client;
  476. const result = await this.execCommand(client, 'moveToFinished', args);
  477. if (result < 0) {
  478. throw this.finishedErrors({
  479. code: result,
  480. jobId,
  481. command: 'moveToFinished',
  482. state: 'active',
  483. });
  484. }
  485. else {
  486. if (typeof result !== 'undefined') {
  487. return raw2NextJobData(result);
  488. }
  489. }
  490. }
  491. drainArgs(delayed) {
  492. const queueKeys = this.queue.keys;
  493. const keys = [
  494. queueKeys.wait,
  495. queueKeys.paused,
  496. queueKeys.delayed,
  497. queueKeys.prioritized,
  498. queueKeys.repeat,
  499. ];
  500. const args = [queueKeys[''], delayed ? '1' : '0'];
  501. return keys.concat(args);
  502. }
  503. async drain(delayed) {
  504. const client = await this.queue.client;
  505. const args = this.drainArgs(delayed);
  506. return this.execCommand(client, 'drain', args);
  507. }
  508. removeChildDependencyArgs(jobId, parentKey) {
  509. const queueKeys = this.queue.keys;
  510. const keys = [queueKeys['']];
  511. const args = [this.queue.toKey(jobId), parentKey];
  512. return keys.concat(args);
  513. }
  514. async removeChildDependency(jobId, parentKey) {
  515. const client = await this.queue.client;
  516. const args = this.removeChildDependencyArgs(jobId, parentKey);
  517. const result = await this.execCommand(client, 'removeChildDependency', args);
  518. switch (result) {
  519. case 0:
  520. return true;
  521. case 1:
  522. return false;
  523. default:
  524. throw this.finishedErrors({
  525. code: result,
  526. jobId,
  527. parentKey,
  528. command: 'removeChildDependency',
  529. });
  530. }
  531. }
  532. getRangesArgs(types, start, end, asc) {
  533. const queueKeys = this.queue.keys;
  534. const transformedTypes = types.map(type => {
  535. return type === 'waiting' ? 'wait' : type;
  536. });
  537. const keys = [queueKeys['']];
  538. const args = [start, end, asc ? '1' : '0', ...transformedTypes];
  539. return keys.concat(args);
  540. }
  541. async getRanges(types, start = 0, end = 1, asc = false) {
  542. const client = await this.queue.client;
  543. const args = this.getRangesArgs(types, start, end, asc);
  544. return await this.execCommand(client, 'getRanges', args);
  545. }
  546. getCountsArgs(types) {
  547. const queueKeys = this.queue.keys;
  548. const transformedTypes = types.map(type => {
  549. return type === 'waiting' ? 'wait' : type;
  550. });
  551. const keys = [queueKeys['']];
  552. const args = [...transformedTypes];
  553. return keys.concat(args);
  554. }
  555. async getCounts(types) {
  556. const client = await this.queue.client;
  557. const args = this.getCountsArgs(types);
  558. return await this.execCommand(client, 'getCounts', args);
  559. }
  560. getCountsPerPriorityArgs(priorities) {
  561. const keys = [
  562. this.queue.keys.wait,
  563. this.queue.keys.paused,
  564. this.queue.keys.meta,
  565. this.queue.keys.prioritized,
  566. ];
  567. const args = priorities;
  568. return keys.concat(args);
  569. }
  570. async getCountsPerPriority(priorities) {
  571. const client = await this.queue.client;
  572. const args = this.getCountsPerPriorityArgs(priorities);
  573. return await this.execCommand(client, 'getCountsPerPriority', args);
  574. }
  575. getDependencyCountsArgs(jobId, types) {
  576. const keys = [
  577. `${jobId}:processed`,
  578. `${jobId}:dependencies`,
  579. `${jobId}:failed`,
  580. `${jobId}:unsuccessful`,
  581. ].map(name => {
  582. return this.queue.toKey(name);
  583. });
  584. const args = types;
  585. return keys.concat(args);
  586. }
  587. async getDependencyCounts(jobId, types) {
  588. const client = await this.queue.client;
  589. const args = this.getDependencyCountsArgs(jobId, types);
  590. return await this.execCommand(client, 'getDependencyCounts', args);
  591. }
  592. moveToCompletedArgs(job, returnvalue, removeOnComplete, token, fetchNext = false) {
  593. const timestamp = Date.now();
  594. return this.moveToFinishedArgs(job, returnvalue, 'returnvalue', removeOnComplete, 'completed', token, timestamp, fetchNext);
  595. }
  596. moveToFailedArgs(job, failedReason, removeOnFailed, token, fetchNext = false, fieldsToUpdate) {
  597. const timestamp = Date.now();
  598. return this.moveToFinishedArgs(job, failedReason, 'failedReason', removeOnFailed, 'failed', token, timestamp, fetchNext, fieldsToUpdate);
  599. }
  600. async isFinished(jobId, returnValue = false) {
  601. const client = await this.queue.client;
  602. const keys = ['completed', 'failed', jobId].map((key) => {
  603. return this.queue.toKey(key);
  604. });
  605. return this.execCommand(client, 'isFinished', keys.concat([jobId, returnValue ? '1' : '']));
  606. }
  607. async getState(jobId) {
  608. const client = await this.queue.client;
  609. const keys = [
  610. 'completed',
  611. 'failed',
  612. 'delayed',
  613. 'active',
  614. 'wait',
  615. 'paused',
  616. 'waiting-children',
  617. 'prioritized',
  618. ].map((key) => {
  619. return this.queue.toKey(key);
  620. });
  621. if ((0, utils_1.isRedisVersionLowerThan)(this.queue.redisVersion, '6.0.6', this.queue.databaseType)) {
  622. return this.execCommand(client, 'getState', keys.concat([jobId]));
  623. }
  624. return this.execCommand(client, 'getStateV2', keys.concat([jobId]));
  625. }
  626. /**
  627. * Change delay of a delayed job.
  628. *
  629. * Reschedules a delayed job by setting a new delay from the current time.
  630. * For example, calling changeDelay(5000) will reschedule the job to execute
  631. * 5000 milliseconds (5 seconds) from now, regardless of the original delay.
  632. *
  633. * @param jobId - the ID of the job to change the delay for.
  634. * @param delay - milliseconds from now when the job should be processed.
  635. * @returns delay in milliseconds.
  636. * @throws JobNotExist
  637. * This exception is thrown if jobId is missing.
  638. * @throws JobNotInState
  639. * This exception is thrown if job is not in delayed state.
  640. */
  641. async changeDelay(jobId, delay) {
  642. const client = await this.queue.client;
  643. const args = this.changeDelayArgs(jobId, delay);
  644. const result = await this.execCommand(client, 'changeDelay', args);
  645. if (result < 0) {
  646. throw this.finishedErrors({
  647. code: result,
  648. jobId,
  649. command: 'changeDelay',
  650. state: 'delayed',
  651. });
  652. }
  653. }
  654. changeDelayArgs(jobId, delay) {
  655. const timestamp = Date.now();
  656. const keys = [
  657. this.queue.keys.delayed,
  658. this.queue.keys.meta,
  659. this.queue.keys.marker,
  660. this.queue.keys.events,
  661. ];
  662. return keys.concat([
  663. delay,
  664. JSON.stringify(timestamp),
  665. jobId,
  666. this.queue.toKey(jobId),
  667. ]);
  668. }
  669. async changePriority(jobId, priority = 0, lifo = false) {
  670. const client = await this.queue.client;
  671. const args = this.changePriorityArgs(jobId, priority, lifo);
  672. const result = await this.execCommand(client, 'changePriority', args);
  673. if (result < 0) {
  674. throw this.finishedErrors({
  675. code: result,
  676. jobId,
  677. command: 'changePriority',
  678. });
  679. }
  680. }
  681. changePriorityArgs(jobId, priority = 0, lifo = false) {
  682. const keys = [
  683. this.queue.keys.wait,
  684. this.queue.keys.paused,
  685. this.queue.keys.meta,
  686. this.queue.keys.prioritized,
  687. this.queue.keys.active,
  688. this.queue.keys.pc,
  689. this.queue.keys.marker,
  690. ];
  691. return keys.concat([priority, this.queue.toKey(''), jobId, lifo ? 1 : 0]);
  692. }
  693. moveToDelayedArgs(jobId, timestamp, token, delay, opts = {}) {
  694. const queueKeys = this.queue.keys;
  695. const workerOpts = this.queue.opts;
  696. const keys = [
  697. queueKeys.marker,
  698. queueKeys.active,
  699. queueKeys.prioritized,
  700. queueKeys.delayed,
  701. this.queue.toKey(jobId),
  702. queueKeys.events,
  703. queueKeys.meta,
  704. queueKeys.stalled,
  705. queueKeys.wait,
  706. queueKeys.limiter,
  707. queueKeys.paused,
  708. queueKeys.pc,
  709. ];
  710. const fetchNext = opts.fetchNext && !this.queue.closing ? 1 : 0;
  711. return keys.concat([
  712. this.queue.keys[''],
  713. timestamp,
  714. jobId,
  715. token,
  716. delay,
  717. opts.skipAttempt ? '1' : '0',
  718. opts.fieldsToUpdate
  719. ? pack((0, utils_1.objectToFlatArray)(opts.fieldsToUpdate))
  720. : void 0,
  721. fetchNext,
  722. fetchNext
  723. ? pack({
  724. token,
  725. lockDuration: workerOpts.lockDuration,
  726. limiter: workerOpts.limiter,
  727. name: workerOpts.name,
  728. })
  729. : void 0,
  730. ]);
  731. }
  732. moveToWaitingChildrenArgs(jobId, token, opts) {
  733. const timestamp = Date.now();
  734. const childKey = (0, utils_1.getParentKey)(opts.child);
  735. const keys = [
  736. 'active',
  737. 'waiting-children',
  738. jobId,
  739. `${jobId}:dependencies`,
  740. `${jobId}:unsuccessful`,
  741. 'stalled',
  742. 'events',
  743. ].map(name => {
  744. return this.queue.toKey(name);
  745. });
  746. return keys.concat([
  747. token,
  748. childKey !== null && childKey !== void 0 ? childKey : '',
  749. JSON.stringify(timestamp),
  750. jobId,
  751. this.queue.toKey(''),
  752. ]);
  753. }
  754. isMaxedArgs() {
  755. const queueKeys = this.queue.keys;
  756. const keys = [queueKeys.meta, queueKeys.active];
  757. return keys;
  758. }
  759. async isMaxed() {
  760. const client = await this.queue.client;
  761. const args = this.isMaxedArgs();
  762. return !!(await this.execCommand(client, 'isMaxed', args));
  763. }
  764. async moveToDelayed(jobId, timestamp, delay, token = '0', opts = {}) {
  765. const client = await this.queue.client;
  766. const args = this.moveToDelayedArgs(jobId, timestamp, token, delay, opts);
  767. const result = await this.execCommand(client, 'moveToDelayed', args);
  768. if (result < 0) {
  769. throw this.finishedErrors({
  770. code: result,
  771. jobId,
  772. command: 'moveToDelayed',
  773. state: 'active',
  774. });
  775. }
  776. else if (typeof result !== 'undefined') {
  777. return raw2NextJobData(result);
  778. }
  779. }
  780. /**
  781. * Move parent job to waiting-children state.
  782. *
  783. * @returns true if job is successfully moved, false if there are pending dependencies.
  784. * @throws JobNotExist
  785. * This exception is thrown if jobId is missing.
  786. * @throws JobLockNotExist
  787. * This exception is thrown if job lock is missing.
  788. * @throws JobNotInState
  789. * This exception is thrown if job is not in active state.
  790. */
  791. async moveToWaitingChildren(jobId, token, opts = {}) {
  792. const client = await this.queue.client;
  793. const args = this.moveToWaitingChildrenArgs(jobId, token, opts);
  794. const result = await this.execCommand(client, 'moveToWaitingChildren', args);
  795. switch (result) {
  796. case 0:
  797. return true;
  798. case 1:
  799. return false;
  800. default:
  801. throw this.finishedErrors({
  802. code: result,
  803. jobId,
  804. command: 'moveToWaitingChildren',
  805. state: 'active',
  806. });
  807. }
  808. }
  809. getRateLimitTtlArgs(maxJobs) {
  810. const keys = [
  811. this.queue.keys.limiter,
  812. this.queue.keys.meta,
  813. ];
  814. return keys.concat([maxJobs !== null && maxJobs !== void 0 ? maxJobs : '0']);
  815. }
  816. async getRateLimitTtl(maxJobs) {
  817. const client = await this.queue.client;
  818. const args = this.getRateLimitTtlArgs(maxJobs);
  819. return this.execCommand(client, 'getRateLimitTtl', args);
  820. }
  821. /**
  822. * Remove jobs in a specific state.
  823. *
  824. * @returns Id jobs from the deleted records.
  825. */
  826. async cleanJobsInSet(set, timestamp, limit = 0) {
  827. const client = await this.queue.client;
  828. return this.execCommand(client, 'cleanJobsInSet', [
  829. this.queue.toKey(set),
  830. this.queue.toKey('events'),
  831. this.queue.toKey('repeat'),
  832. this.queue.toKey(''),
  833. timestamp,
  834. limit,
  835. set,
  836. ]);
  837. }
  838. getJobSchedulerArgs(id) {
  839. const keys = [this.queue.keys.repeat];
  840. return keys.concat([id]);
  841. }
  842. async getJobScheduler(id) {
  843. const client = await this.queue.client;
  844. const args = this.getJobSchedulerArgs(id);
  845. return this.execCommand(client, 'getJobScheduler', args);
  846. }
  847. retryJobArgs(jobId, lifo, token, opts = {}) {
  848. const keys = [
  849. this.queue.keys.active,
  850. this.queue.keys.wait,
  851. this.queue.keys.paused,
  852. this.queue.toKey(jobId),
  853. this.queue.keys.meta,
  854. this.queue.keys.events,
  855. this.queue.keys.delayed,
  856. this.queue.keys.prioritized,
  857. this.queue.keys.pc,
  858. this.queue.keys.marker,
  859. this.queue.keys.stalled,
  860. ];
  861. const pushCmd = (lifo ? 'R' : 'L') + 'PUSH';
  862. return keys.concat([
  863. this.queue.toKey(''),
  864. Date.now(),
  865. pushCmd,
  866. jobId,
  867. token,
  868. opts.fieldsToUpdate
  869. ? pack((0, utils_1.objectToFlatArray)(opts.fieldsToUpdate))
  870. : void 0,
  871. ]);
  872. }
  873. async retryJob(jobId, lifo, token = '0', opts = {}) {
  874. const client = await this.queue.client;
  875. const args = this.retryJobArgs(jobId, lifo, token, opts);
  876. const result = await this.execCommand(client, 'retryJob', args);
  877. if (result < 0) {
  878. throw this.finishedErrors({
  879. code: result,
  880. jobId,
  881. command: 'retryJob',
  882. state: 'active',
  883. });
  884. }
  885. }
  886. moveJobsToWaitArgs(state, count, timestamp) {
  887. const keys = [
  888. this.queue.toKey(''),
  889. this.queue.keys.events,
  890. this.queue.toKey(state),
  891. this.queue.toKey('wait'),
  892. this.queue.toKey('paused'),
  893. this.queue.keys.meta,
  894. this.queue.keys.active,
  895. this.queue.keys.marker,
  896. ];
  897. const args = [count, timestamp, state];
  898. return keys.concat(args);
  899. }
  900. async retryJobs(state = 'failed', count = 1000, timestamp = new Date().getTime()) {
  901. const client = await this.queue.client;
  902. const args = this.moveJobsToWaitArgs(state, count, timestamp);
  903. return this.execCommand(client, 'moveJobsToWait', args);
  904. }
  905. async promoteJobs(count = 1000) {
  906. const client = await this.queue.client;
  907. const args = this.moveJobsToWaitArgs('delayed', count, Number.MAX_VALUE);
  908. return this.execCommand(client, 'moveJobsToWait', args);
  909. }
  910. /**
  911. * Attempts to reprocess a job
  912. *
  913. * @param job - The job to reprocess
  914. * @param state - The expected job state. If the job is not found
  915. * on the provided state, then it's not reprocessed. Supported states: 'failed', 'completed'
  916. *
  917. * @returns A promise that resolves when the job has been successfully moved to the wait queue.
  918. * @throws Will throw an error with a code property indicating the failure reason:
  919. * - code 0: Job does not exist
  920. * - code -1: Job is currently locked and can't be retried
  921. * - code -2: Job was not found in the expected set
  922. */
  923. async reprocessJob(job, state, opts = {}) {
  924. const client = await this.queue.client;
  925. const keys = [
  926. this.queue.toKey(job.id),
  927. this.queue.keys.events,
  928. this.queue.toKey(state),
  929. this.queue.keys.wait,
  930. this.queue.keys.meta,
  931. this.queue.keys.paused,
  932. this.queue.keys.active,
  933. this.queue.keys.marker,
  934. ];
  935. const args = [
  936. job.id,
  937. (job.opts.lifo ? 'R' : 'L') + 'PUSH',
  938. state === 'failed' ? 'failedReason' : 'returnvalue',
  939. state,
  940. opts.resetAttemptsMade ? '1' : '0',
  941. opts.resetAttemptsStarted ? '1' : '0',
  942. ];
  943. const result = await this.execCommand(client, 'reprocessJob', keys.concat(args));
  944. switch (result) {
  945. case 1:
  946. return;
  947. default:
  948. throw this.finishedErrors({
  949. code: result,
  950. jobId: job.id,
  951. command: 'reprocessJob',
  952. state,
  953. });
  954. }
  955. }
  956. async getMetrics(type, start = 0, end = -1) {
  957. const client = await this.queue.client;
  958. const keys = [
  959. this.queue.toKey(`metrics:${type}`),
  960. this.queue.toKey(`metrics:${type}:data`),
  961. ];
  962. const args = [start, end];
  963. const result = await this.execCommand(client, 'getMetrics', keys.concat(args));
  964. return result;
  965. }
  966. async moveToActive(client, token, name) {
  967. const opts = this.queue.opts;
  968. const queueKeys = this.queue.keys;
  969. const keys = [
  970. queueKeys.wait,
  971. queueKeys.active,
  972. queueKeys.prioritized,
  973. queueKeys.events,
  974. queueKeys.stalled,
  975. queueKeys.limiter,
  976. queueKeys.delayed,
  977. queueKeys.paused,
  978. queueKeys.meta,
  979. queueKeys.pc,
  980. queueKeys.marker,
  981. ];
  982. const args = [
  983. queueKeys[''],
  984. Date.now(),
  985. pack({
  986. token,
  987. lockDuration: opts.lockDuration,
  988. limiter: opts.limiter,
  989. name,
  990. }),
  991. ];
  992. const result = await this.execCommand(client, 'moveToActive', keys.concat(args));
  993. return raw2NextJobData(result);
  994. }
  995. async promote(jobId) {
  996. const client = await this.queue.client;
  997. const keys = [
  998. this.queue.keys.delayed,
  999. this.queue.keys.wait,
  1000. this.queue.keys.paused,
  1001. this.queue.keys.meta,
  1002. this.queue.keys.prioritized,
  1003. this.queue.keys.active,
  1004. this.queue.keys.pc,
  1005. this.queue.keys.events,
  1006. this.queue.keys.marker,
  1007. ];
  1008. const args = [this.queue.toKey(''), jobId];
  1009. const code = await this.execCommand(client, 'promote', keys.concat(args));
  1010. if (code < 0) {
  1011. throw this.finishedErrors({
  1012. code,
  1013. jobId,
  1014. command: 'promote',
  1015. state: 'delayed',
  1016. });
  1017. }
  1018. }
  1019. moveStalledJobsToWaitArgs() {
  1020. const opts = this.queue.opts;
  1021. const keys = [
  1022. this.queue.keys.stalled,
  1023. this.queue.keys.wait,
  1024. this.queue.keys.active,
  1025. this.queue.keys['stalled-check'],
  1026. this.queue.keys.meta,
  1027. this.queue.keys.paused,
  1028. this.queue.keys.marker,
  1029. this.queue.keys.events,
  1030. ];
  1031. const args = [
  1032. opts.maxStalledCount,
  1033. this.queue.toKey(''),
  1034. Date.now(),
  1035. opts.stalledInterval,
  1036. ];
  1037. return keys.concat(args);
  1038. }
  1039. /**
  1040. * Looks for unlocked jobs in the active queue.
  1041. *
  1042. * The job was being worked on, but the worker process died and it failed to renew the lock.
  1043. * We call these jobs 'stalled'. This is the most common case. We resolve these by moving them
  1044. * back to wait to be re-processed. To prevent jobs from cycling endlessly between active and wait,
  1045. * (e.g. if the job handler keeps crashing),
  1046. * we limit the number stalled job recoveries to settings.maxStalledCount.
  1047. */
  1048. async moveStalledJobsToWait() {
  1049. const client = await this.queue.client;
  1050. const args = this.moveStalledJobsToWaitArgs();
  1051. return this.execCommand(client, 'moveStalledJobsToWait', args);
  1052. }
  1053. /**
  1054. * Moves a job back from Active to Wait.
  1055. * This script is used when a job has been manually rate limited and needs
  1056. * to be moved back to wait from active status.
  1057. *
  1058. * @param client - Redis client
  1059. * @param jobId - Job id
  1060. * @returns
  1061. */
  1062. async moveJobFromActiveToWait(jobId, token = '0') {
  1063. const client = await this.queue.client;
  1064. const keys = [
  1065. this.queue.keys.active,
  1066. this.queue.keys.wait,
  1067. this.queue.keys.stalled,
  1068. this.queue.keys.paused,
  1069. this.queue.keys.meta,
  1070. this.queue.keys.limiter,
  1071. this.queue.keys.prioritized,
  1072. this.queue.keys.marker,
  1073. this.queue.keys.events,
  1074. ];
  1075. const args = [jobId, token, this.queue.toKey(jobId)];
  1076. const result = await this.execCommand(client, 'moveJobFromActiveToWait', keys.concat(args));
  1077. if (result < 0) {
  1078. throw this.finishedErrors({
  1079. code: result,
  1080. jobId,
  1081. command: 'moveJobFromActiveToWait',
  1082. state: 'active',
  1083. });
  1084. }
  1085. return result;
  1086. }
  1087. async obliterate(opts) {
  1088. const client = await this.queue.client;
  1089. const keys = [
  1090. this.queue.keys.meta,
  1091. this.queue.toKey(''),
  1092. ];
  1093. const args = [opts.count, opts.force ? 'force' : null];
  1094. const result = await this.execCommand(client, 'obliterate', keys.concat(args));
  1095. if (result < 0) {
  1096. switch (result) {
  1097. case -1:
  1098. throw new Error('Cannot obliterate non-paused queue');
  1099. case -2:
  1100. throw new Error('Cannot obliterate queue with active jobs');
  1101. }
  1102. }
  1103. return result;
  1104. }
  1105. /**
  1106. * Paginate a set or hash keys.
  1107. * @param opts - options to define the pagination behaviour
  1108. *
  1109. */
  1110. async paginate(key, opts) {
  1111. const client = await this.queue.client;
  1112. const keys = [key];
  1113. const maxIterations = 5;
  1114. const pageSize = opts.end >= 0 ? opts.end - opts.start + 1 : Infinity;
  1115. let cursor = '0', offset = 0, items, total, rawJobs, page = [], jobs = [];
  1116. do {
  1117. const args = [
  1118. opts.start + page.length,
  1119. opts.end,
  1120. cursor,
  1121. offset,
  1122. maxIterations,
  1123. ];
  1124. if (opts.fetchJobs) {
  1125. args.push(1);
  1126. }
  1127. [cursor, offset, items, total, rawJobs] = await this.execCommand(client, 'paginate', keys.concat(args));
  1128. page = page.concat(items);
  1129. if (rawJobs && rawJobs.length) {
  1130. jobs = jobs.concat(rawJobs.map(utils_1.array2obj));
  1131. }
  1132. // Important to keep this coercive inequality (!=) instead of strict inequality (!==)
  1133. } while (cursor != '0' && page.length < pageSize);
  1134. // If we get an array of arrays, it means we are paginating a hash
  1135. if (page.length && Array.isArray(page[0])) {
  1136. const result = [];
  1137. for (let index = 0; index < page.length; index++) {
  1138. const [id, value] = page[index];
  1139. try {
  1140. result.push({ id, v: JSON.parse(value) });
  1141. }
  1142. catch (err) {
  1143. result.push({ id, err: err.message });
  1144. }
  1145. }
  1146. return {
  1147. cursor,
  1148. items: result,
  1149. total,
  1150. jobs,
  1151. };
  1152. }
  1153. else {
  1154. return {
  1155. cursor,
  1156. items: page.map(item => ({ id: item })),
  1157. total,
  1158. jobs,
  1159. };
  1160. }
  1161. }
  1162. finishedErrors({ code, jobId, parentKey, command, state, }) {
  1163. let error;
  1164. switch (code) {
  1165. case enums_1.ErrorCode.JobNotExist:
  1166. error = new Error(`Missing key for job ${jobId}. ${command}`);
  1167. break;
  1168. case enums_1.ErrorCode.JobLockNotExist:
  1169. error = new Error(`Missing lock for job ${jobId}. ${command}`);
  1170. break;
  1171. case enums_1.ErrorCode.JobNotInState:
  1172. error = new Error(`Job ${jobId} is not in the ${state} state. ${command}`);
  1173. break;
  1174. case enums_1.ErrorCode.JobPendingChildren:
  1175. error = new Error(`Job ${jobId} has pending dependencies. ${command}`);
  1176. break;
  1177. case enums_1.ErrorCode.ParentJobNotExist:
  1178. error = new Error(`Missing key for parent job ${parentKey}. ${command}`);
  1179. break;
  1180. case enums_1.ErrorCode.JobLockMismatch:
  1181. error = new Error(`Lock mismatch for job ${jobId}. Cmd ${command} from ${state}`);
  1182. break;
  1183. case enums_1.ErrorCode.ParentJobCannotBeReplaced:
  1184. error = new Error(`The parent job ${parentKey} cannot be replaced. ${command}`);
  1185. break;
  1186. case enums_1.ErrorCode.JobBelongsToJobScheduler:
  1187. error = new Error(`Job ${jobId} belongs to a job scheduler and cannot be removed directly. ${command}`);
  1188. break;
  1189. case enums_1.ErrorCode.JobHasFailedChildren:
  1190. error = new errors_1.UnrecoverableError(`Cannot complete job ${jobId} because it has at least one failed child. ${command}`);
  1191. break;
  1192. case enums_1.ErrorCode.SchedulerJobIdCollision:
  1193. error = new Error(`Cannot create job scheduler iteration - job ID already exists. ${command}`);
  1194. break;
  1195. case enums_1.ErrorCode.SchedulerJobSlotsBusy:
  1196. error = new Error(`Cannot create job scheduler iteration - current and next time slots already have jobs. ${command}`);
  1197. break;
  1198. default:
  1199. error = new Error(`Unknown code ${code} error for ${jobId}. ${command}`);
  1200. }
  1201. // Add the code property to the error object
  1202. error.code = code;
  1203. return error;
  1204. }
  1205. async removeOrphanedJobs(candidateJobIds, stateKeySuffixes, jobSubKeySuffixes) {
  1206. const client = await this.queue.client;
  1207. const args = [
  1208. this.queue.toKey(''),
  1209. stateKeySuffixes.length,
  1210. ...stateKeySuffixes,
  1211. jobSubKeySuffixes.length,
  1212. ...jobSubKeySuffixes,
  1213. ...candidateJobIds,
  1214. ];
  1215. return this.execCommand(client, 'removeOrphanedJobs', args);
  1216. }
  1217. }
  1218. exports.Scripts = Scripts;
  1219. function raw2NextJobData(raw) {
  1220. if (raw) {
  1221. const result = [null, raw[1], raw[2], raw[3]];
  1222. if (raw[0]) {
  1223. result[0] = (0, utils_1.array2obj)(raw[0]);
  1224. }
  1225. return result;
  1226. }
  1227. return [];
  1228. }
  1229. //# sourceMappingURL=scripts.js.map