@boringnode/queue 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/build/{chunk-VHN3XZDC.js → chunk-VRXHCWNK.js} +60 -16
- package/build/chunk-VRXHCWNK.js.map +1 -0
- package/build/index.d.ts +26 -8
- package/build/index.js +1 -1
- package/build/{job-DImdhRFO.d.ts → job-Z5fBSzRX.d.ts} +144 -5
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/fake_adapter.d.ts +6 -1
- package/build/src/drivers/fake_adapter.js +1 -1
- package/build/src/drivers/knex_adapter.d.ts +1 -1
- package/build/src/drivers/redis_adapter.d.ts +1 -1
- package/build/src/drivers/redis_adapter.js +90 -92
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +1 -1
- package/build/src/drivers/sync_adapter.js +1 -1
- package/build/src/otel.d.ts +1 -1
- package/build/src/otel.js +3 -0
- package/build/src/otel.js.map +1 -1
- package/build/src/types/index.d.ts +1 -1
- package/build/src/types/main.d.ts +1 -1
- package/build/src/types/tracing_channels.d.ts +1 -1
- package/package.json +2 -3
- package/build/chunk-VHN3XZDC.js.map +0 -1
|
@@ -206,7 +206,13 @@ type Duration = number | string;
|
|
|
206
206
|
* - `{ age?, count? }`: Keep with pruning by age and/or count
|
|
207
207
|
*/
|
|
208
208
|
type JobRetention = boolean | {
|
|
209
|
+
/**
|
|
210
|
+
* Keep jobs newer than this duration.
|
|
211
|
+
*/
|
|
209
212
|
age?: Duration;
|
|
213
|
+
/**
|
|
214
|
+
* Keep at most this many jobs.
|
|
215
|
+
*/
|
|
210
216
|
count?: number;
|
|
211
217
|
};
|
|
212
218
|
/**
|
|
@@ -265,7 +271,7 @@ interface JobData {
|
|
|
265
271
|
/**
|
|
266
272
|
* Job priority (lower = higher priority).
|
|
267
273
|
*
|
|
268
|
-
* @default
|
|
274
|
+
* @default 5
|
|
269
275
|
*/
|
|
270
276
|
priority?: number;
|
|
271
277
|
/**
|
|
@@ -291,6 +297,11 @@ interface JobData {
|
|
|
291
297
|
* ```
|
|
292
298
|
*/
|
|
293
299
|
groupId?: string;
|
|
300
|
+
/**
|
|
301
|
+
* Timestamp (ms) when the job was dispatched.
|
|
302
|
+
* Used to compute queue wait time in OTel instrumentation.
|
|
303
|
+
*/
|
|
304
|
+
createdAt?: number;
|
|
294
305
|
/**
|
|
295
306
|
* Serialized trace context for distributed tracing.
|
|
296
307
|
* Injected by OTel plugin at dispatch time.
|
|
@@ -345,22 +356,28 @@ interface JobOptions {
|
|
|
345
356
|
queue?: string;
|
|
346
357
|
/**
|
|
347
358
|
* Adapter name or factory to use for this job.
|
|
359
|
+
*
|
|
360
|
+
* Defaults to the queue manager's configured default adapter.
|
|
348
361
|
*/
|
|
349
362
|
adapter?: string | (() => Adapter);
|
|
350
363
|
/**
|
|
351
364
|
* Maximum retry attempts before permanent failure.
|
|
352
365
|
*
|
|
353
|
-
*
|
|
366
|
+
* This is a convenience alias for `retry.maxRetries`.
|
|
367
|
+
*
|
|
368
|
+
* @default 0
|
|
354
369
|
*/
|
|
355
370
|
maxRetries?: number;
|
|
356
371
|
/**
|
|
357
372
|
* Job priority (lower = higher priority).
|
|
358
373
|
*
|
|
359
|
-
* @default
|
|
374
|
+
* @default 5
|
|
360
375
|
*/
|
|
361
376
|
priority?: number;
|
|
362
377
|
/**
|
|
363
|
-
* Retry configuration
|
|
378
|
+
* Retry configuration for this job.
|
|
379
|
+
*
|
|
380
|
+
* Overrides queue-level and global retry settings.
|
|
364
381
|
*/
|
|
365
382
|
retry?: RetryConfig;
|
|
366
383
|
/**
|
|
@@ -372,10 +389,22 @@ interface JobOptions {
|
|
|
372
389
|
/**
|
|
373
390
|
* Whether to mark job as failed on timeout.
|
|
374
391
|
*
|
|
375
|
-
*
|
|
392
|
+
* When disabled, timed out jobs follow the normal retry policy.
|
|
393
|
+
*
|
|
394
|
+
* @default false
|
|
376
395
|
*/
|
|
377
396
|
failOnTimeout?: boolean;
|
|
397
|
+
/**
|
|
398
|
+
* Retention policy for completed jobs.
|
|
399
|
+
*
|
|
400
|
+
* By default, completed jobs are removed immediately.
|
|
401
|
+
*/
|
|
378
402
|
removeOnComplete?: JobRetention;
|
|
403
|
+
/**
|
|
404
|
+
* Retention policy for failed jobs.
|
|
405
|
+
*
|
|
406
|
+
* By default, failed jobs are removed immediately after the failure hooks run.
|
|
407
|
+
*/
|
|
379
408
|
removeOnFail?: JobRetention;
|
|
380
409
|
}
|
|
381
410
|
/**
|
|
@@ -443,23 +472,77 @@ type JobClass<T extends Job = Job> = (new (...args: unknown[]) => T) & {
|
|
|
443
472
|
* ```
|
|
444
473
|
*/
|
|
445
474
|
type JobFactory = (JobClass: JobClass) => Job | Promise<Job>;
|
|
475
|
+
/**
|
|
476
|
+
* Retry policy used by jobs, queues, or the queue manager.
|
|
477
|
+
*/
|
|
446
478
|
interface RetryConfig {
|
|
479
|
+
/**
|
|
480
|
+
* Number of retry attempts after the first failed execution.
|
|
481
|
+
*
|
|
482
|
+
* Set to `0` to disable retries.
|
|
483
|
+
*
|
|
484
|
+
* @default 0
|
|
485
|
+
*/
|
|
447
486
|
maxRetries?: number;
|
|
487
|
+
/**
|
|
488
|
+
* Factory that creates the backoff strategy used between retry attempts.
|
|
489
|
+
*
|
|
490
|
+
* If omitted, failed jobs are retried as soon as the adapter makes them
|
|
491
|
+
* available again.
|
|
492
|
+
*/
|
|
448
493
|
backoff?: () => BackoffStrategy$1;
|
|
449
494
|
}
|
|
495
|
+
/**
|
|
496
|
+
* Built-in retry delay algorithms.
|
|
497
|
+
*/
|
|
450
498
|
type BackoffStrategy = 'exponential' | 'linear' | 'fixed';
|
|
499
|
+
/**
|
|
500
|
+
* Configuration for built-in and custom retry backoff strategies.
|
|
501
|
+
*/
|
|
451
502
|
interface BackoffConfig {
|
|
503
|
+
/**
|
|
504
|
+
* Strategy used to compute the delay before the next retry.
|
|
505
|
+
*/
|
|
452
506
|
strategy: BackoffStrategy;
|
|
507
|
+
/**
|
|
508
|
+
* Initial delay used by the strategy.
|
|
509
|
+
*/
|
|
453
510
|
baseDelay: Duration;
|
|
511
|
+
/**
|
|
512
|
+
* Upper bound for computed retry delays.
|
|
513
|
+
*/
|
|
454
514
|
maxDelay?: Duration;
|
|
515
|
+
/**
|
|
516
|
+
* Growth factor for exponential backoff.
|
|
517
|
+
*/
|
|
455
518
|
multiplier?: number;
|
|
519
|
+
/**
|
|
520
|
+
* Whether to randomize retry delays to avoid retry bursts.
|
|
521
|
+
*/
|
|
456
522
|
jitter?: boolean;
|
|
457
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* Runtime configuration for a named queue.
|
|
526
|
+
*/
|
|
458
527
|
interface QueueConfig {
|
|
528
|
+
/**
|
|
529
|
+
* Adapter name used by jobs dispatched to this queue.
|
|
530
|
+
*
|
|
531
|
+
* Falls back to the queue manager's default adapter.
|
|
532
|
+
*/
|
|
459
533
|
adapter?: string;
|
|
534
|
+
/**
|
|
535
|
+
* Retry policy applied to jobs in this queue unless overridden by job options.
|
|
536
|
+
*/
|
|
460
537
|
retry?: RetryConfig;
|
|
538
|
+
/**
|
|
539
|
+
* Default job options applied to jobs in this queue unless overridden by the job.
|
|
540
|
+
*/
|
|
461
541
|
defaultJobOptions?: JobOptions;
|
|
462
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Runtime options for workers that poll queues and execute jobs.
|
|
545
|
+
*/
|
|
463
546
|
interface WorkerConfig {
|
|
464
547
|
/**
|
|
465
548
|
* Maximum number of jobs to process concurrently.
|
|
@@ -508,22 +591,32 @@ interface WorkerConfig {
|
|
|
508
591
|
*/
|
|
509
592
|
onShutdownSignal?: () => void | Promise<void>;
|
|
510
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* Event yielded by the low-level worker processing generator.
|
|
596
|
+
*/
|
|
511
597
|
type WorkerCycle = {
|
|
598
|
+
/** A job was acquired and execution started. */
|
|
512
599
|
type: 'started';
|
|
513
600
|
queue: string;
|
|
514
601
|
job: JobData;
|
|
515
602
|
} | {
|
|
603
|
+
/** A running job finished, either successfully or after failure handling. */
|
|
516
604
|
type: 'completed';
|
|
517
605
|
queue: string;
|
|
518
606
|
job: JobData;
|
|
519
607
|
} | {
|
|
608
|
+
/** No work was available. Consumers should wait before polling again. */
|
|
520
609
|
type: 'idle';
|
|
521
610
|
suggestedDelay: Duration;
|
|
522
611
|
} | {
|
|
612
|
+
/** An unexpected worker loop error occurred. */
|
|
523
613
|
type: 'error';
|
|
524
614
|
error: Error;
|
|
525
615
|
suggestedDelay: Duration;
|
|
526
616
|
};
|
|
617
|
+
/**
|
|
618
|
+
* Factory used to lazily create adapter instances.
|
|
619
|
+
*/
|
|
527
620
|
type AdapterFactory<T extends Adapter = Adapter> = () => T;
|
|
528
621
|
/**
|
|
529
622
|
* Status of a schedule.
|
|
@@ -602,13 +695,59 @@ interface ScheduleListOptions {
|
|
|
602
695
|
status?: ScheduleStatus;
|
|
603
696
|
}
|
|
604
697
|
interface QueueManagerConfig {
|
|
698
|
+
/**
|
|
699
|
+
* Name of the adapter used when a job does not select one explicitly.
|
|
700
|
+
*
|
|
701
|
+
* Must match one of the keys from `adapters`.
|
|
702
|
+
*/
|
|
605
703
|
default: string;
|
|
704
|
+
/**
|
|
705
|
+
* Available queue adapters keyed by name.
|
|
706
|
+
*
|
|
707
|
+
* Adapters are lazy-instantiated the first time they are used.
|
|
708
|
+
*/
|
|
606
709
|
adapters: Record<string, AdapterFactory>;
|
|
710
|
+
/**
|
|
711
|
+
* Global retry configuration applied to all jobs unless overridden by
|
|
712
|
+
* queue-level or job-level options.
|
|
713
|
+
*/
|
|
607
714
|
retry?: RetryConfig;
|
|
715
|
+
/**
|
|
716
|
+
* Global job options applied to all jobs unless overridden by queue-level
|
|
717
|
+
* or job-level options.
|
|
718
|
+
*/
|
|
608
719
|
defaultJobOptions?: JobOptions;
|
|
720
|
+
/**
|
|
721
|
+
* Per-queue configuration keyed by queue name.
|
|
722
|
+
*
|
|
723
|
+
* Use this to select adapters or defaults for specific queues.
|
|
724
|
+
*/
|
|
609
725
|
queues?: Record<string, QueueConfig>;
|
|
726
|
+
/**
|
|
727
|
+
* Worker runtime options used by `Worker` instances.
|
|
728
|
+
*/
|
|
610
729
|
worker?: WorkerConfig;
|
|
730
|
+
/**
|
|
731
|
+
* Glob patterns used to discover and register job classes.
|
|
732
|
+
*
|
|
733
|
+
* These locations are used by `init()` when `autoLoadJobs` is enabled,
|
|
734
|
+
* and by `QueueManager.loadJobs()` when called without arguments.
|
|
735
|
+
*/
|
|
611
736
|
locations?: string[];
|
|
737
|
+
/**
|
|
738
|
+
* Whether `init()` should immediately register jobs from configured locations.
|
|
739
|
+
*
|
|
740
|
+
* Framework integrations may disable this to defer job loading until a
|
|
741
|
+
* command lifecycle is ready, then call `QueueManager.loadJobs()`.
|
|
742
|
+
*
|
|
743
|
+
* @default true
|
|
744
|
+
*/
|
|
745
|
+
autoLoadJobs?: boolean;
|
|
746
|
+
/**
|
|
747
|
+
* Logger used by the queue runtime.
|
|
748
|
+
*
|
|
749
|
+
* Defaults to the console logger.
|
|
750
|
+
*/
|
|
612
751
|
logger?: Logger;
|
|
613
752
|
/**
|
|
614
753
|
* Custom factory function for job instantiation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { b as AcquiredJob, A as Adapter } from '../../job-
|
|
1
|
+
export { b as AcquiredJob, A as Adapter } from '../../job-Z5fBSzRX.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as Adapter, J as JobData, a as JobClass, b as AcquiredJob, c as JobRetention, d as JobRecord, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-
|
|
1
|
+
import { A as Adapter, J as JobData, a as JobClass, b as AcquiredJob, c as JobRetention, d as JobRecord, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-Z5fBSzRX.js';
|
|
2
2
|
|
|
3
3
|
interface FakeJobRecord {
|
|
4
4
|
queue: string;
|
|
@@ -23,6 +23,11 @@ declare function fake(): () => FakeAdapter;
|
|
|
23
23
|
*/
|
|
24
24
|
declare class FakeAdapter implements Adapter {
|
|
25
25
|
#private;
|
|
26
|
+
/**
|
|
27
|
+
* Set the function to call when the fake is disposed
|
|
28
|
+
*/
|
|
29
|
+
onDispose(fn: () => void): this;
|
|
30
|
+
[Symbol.dispose](): void;
|
|
26
31
|
setWorkerId(_workerId: string): void;
|
|
27
32
|
getPushedJobs(): FakeJobRecord[];
|
|
28
33
|
getPushedJobsOn(queue: string): FakeJobRecord[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
|
-
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-
|
|
2
|
+
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-Z5fBSzRX.js';
|
|
3
3
|
|
|
4
4
|
interface KnexAdapterOptions {
|
|
5
5
|
connection: Knex;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Redis, RedisOptions } from 'ioredis';
|
|
2
|
-
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-
|
|
2
|
+
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-Z5fBSzRX.js';
|
|
3
3
|
|
|
4
4
|
type RedisConfig = Redis | RedisOptions;
|
|
5
5
|
/**
|
|
@@ -298,73 +298,71 @@ var GET_JOB_SCRIPT = `
|
|
|
298
298
|
})
|
|
299
299
|
`;
|
|
300
300
|
var CLAIM_SCHEDULE_SCRIPT = `
|
|
301
|
-
local
|
|
301
|
+
local schedules_index_key = KEYS[1]
|
|
302
|
+
local schedule_key_prefix = KEYS[2]
|
|
302
303
|
local now = tonumber(ARGV[1])
|
|
303
304
|
|
|
304
|
-
|
|
305
|
-
local data = redis.call('HGETALL', schedule_key)
|
|
306
|
-
if #data == 0 then
|
|
307
|
-
return nil
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
-- Convert HGETALL result to table
|
|
311
|
-
local schedule = {}
|
|
312
|
-
for j = 1, #data, 2 do
|
|
313
|
-
schedule[data[j]] = data[j + 1]
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
-- Check if schedule is due
|
|
317
|
-
if schedule.status ~= 'active' then
|
|
318
|
-
return nil
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
local next_run_at = tonumber(schedule.next_run_at)
|
|
322
|
-
if not next_run_at or next_run_at > now then
|
|
323
|
-
return nil
|
|
324
|
-
end
|
|
305
|
+
local ids = redis.call('SMEMBERS', schedules_index_key)
|
|
325
306
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
local to_date = schedule.to_date and tonumber(schedule.to_date) or nil
|
|
307
|
+
for i = 1, #ids do
|
|
308
|
+
local schedule_key = schedule_key_prefix .. ids[i]
|
|
329
309
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
-- This schedule is claimable - atomically update it
|
|
340
|
-
local new_run_count = run_count + 1
|
|
341
|
-
|
|
342
|
-
-- Calculate new next_run_at (simple interval-based for now)
|
|
343
|
-
-- Complex cron calculation happens in the caller
|
|
344
|
-
local new_next_run_at = ''
|
|
345
|
-
local every_ms = schedule.every_ms and tonumber(schedule.every_ms) or nil
|
|
346
|
-
if every_ms then
|
|
347
|
-
new_next_run_at = tostring(now + every_ms)
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
-- Check if we've hit the limit after this run
|
|
351
|
-
if run_limit and new_run_count >= run_limit then
|
|
352
|
-
new_next_run_at = ''
|
|
353
|
-
end
|
|
310
|
+
-- Get schedule data
|
|
311
|
+
local data = redis.call('HGETALL', schedule_key)
|
|
312
|
+
if #data > 0 then
|
|
313
|
+
-- Convert HGETALL result to table
|
|
314
|
+
local schedule = {}
|
|
315
|
+
for j = 1, #data, 2 do
|
|
316
|
+
schedule[data[j]] = data[j + 1]
|
|
317
|
+
end
|
|
354
318
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
319
|
+
-- Check if schedule is due
|
|
320
|
+
if schedule.status == 'active' then
|
|
321
|
+
local next_run_at = tonumber(schedule.next_run_at)
|
|
322
|
+
|
|
323
|
+
if next_run_at and next_run_at <= now then
|
|
324
|
+
local run_count = tonumber(schedule.run_count or '0')
|
|
325
|
+
local run_limit = schedule.run_limit and tonumber(schedule.run_limit) or nil
|
|
326
|
+
local to_date = schedule.to_date and tonumber(schedule.to_date) or nil
|
|
327
|
+
|
|
328
|
+
-- Check limits
|
|
329
|
+
if not (run_limit and run_count >= run_limit) and not (to_date and now > to_date) then
|
|
330
|
+
-- This schedule is claimable - atomically update it
|
|
331
|
+
local new_run_count = run_count + 1
|
|
332
|
+
|
|
333
|
+
-- Calculate new next_run_at (simple interval-based for now)
|
|
334
|
+
-- Complex cron calculation happens in the caller
|
|
335
|
+
local new_next_run_at = ''
|
|
336
|
+
local every_ms = schedule.every_ms and tonumber(schedule.every_ms) or nil
|
|
337
|
+
if every_ms then
|
|
338
|
+
new_next_run_at = tostring(now + every_ms)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
-- Check if we've hit the limit after this run
|
|
342
|
+
if run_limit and new_run_count >= run_limit then
|
|
343
|
+
new_next_run_at = ''
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
-- Check if past end date
|
|
347
|
+
if to_date and new_next_run_at ~= '' and tonumber(new_next_run_at) > to_date then
|
|
348
|
+
new_next_run_at = ''
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
-- Update the schedule atomically
|
|
352
|
+
redis.call('HSET', schedule_key,
|
|
353
|
+
'next_run_at', new_next_run_at,
|
|
354
|
+
'last_run_at', tostring(now),
|
|
355
|
+
'run_count', tostring(new_run_count))
|
|
356
|
+
|
|
357
|
+
-- Return the schedule data (before update) as JSON
|
|
358
|
+
return cjson.encode(schedule)
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|
|
358
363
|
end
|
|
359
364
|
|
|
360
|
-
|
|
361
|
-
redis.call('HSET', schedule_key,
|
|
362
|
-
'next_run_at', new_next_run_at,
|
|
363
|
-
'last_run_at', tostring(now),
|
|
364
|
-
'run_count', tostring(new_run_count))
|
|
365
|
-
|
|
366
|
-
-- Return the schedule data (before update) as JSON
|
|
367
|
-
return cjson.encode(schedule)
|
|
365
|
+
return nil
|
|
368
366
|
`;
|
|
369
367
|
function redis(config) {
|
|
370
368
|
return () => {
|
|
@@ -665,40 +663,40 @@ var RedisAdapter = class {
|
|
|
665
663
|
}
|
|
666
664
|
async claimDueSchedule() {
|
|
667
665
|
const now = Date.now();
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
} else if (toDate && nextRun > toDate) {
|
|
695
|
-
newNextRunAt = "";
|
|
696
|
-
}
|
|
697
|
-
await this.#connection.hset(scheduleKey, "next_run_at", newNextRunAt.toString());
|
|
666
|
+
const result = await this.#connection.eval(
|
|
667
|
+
CLAIM_SCHEDULE_SCRIPT,
|
|
668
|
+
2,
|
|
669
|
+
schedulesIndexKey,
|
|
670
|
+
`${schedulesKey}::`,
|
|
671
|
+
now.toString()
|
|
672
|
+
);
|
|
673
|
+
if (!result) {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
const data = JSON.parse(result);
|
|
677
|
+
if (data.cron_expression) {
|
|
678
|
+
const { CronExpressionParser } = await import("cron-parser");
|
|
679
|
+
const cron = CronExpressionParser.parse(data.cron_expression, {
|
|
680
|
+
currentDate: new Date(now),
|
|
681
|
+
tz: data.timezone || "UTC"
|
|
682
|
+
});
|
|
683
|
+
const nextRun = cron.next().toDate().getTime();
|
|
684
|
+
const runCount = Number.parseInt(data.run_count || "0", 10) + 1;
|
|
685
|
+
const runLimit = data.run_limit ? Number.parseInt(data.run_limit, 10) : null;
|
|
686
|
+
const toDate = data.to_date ? Number.parseInt(data.to_date, 10) : null;
|
|
687
|
+
let newNextRunAt = nextRun;
|
|
688
|
+
if (runLimit !== null && runCount >= runLimit) {
|
|
689
|
+
newNextRunAt = "";
|
|
690
|
+
} else if (toDate && nextRun > toDate) {
|
|
691
|
+
newNextRunAt = "";
|
|
698
692
|
}
|
|
699
|
-
|
|
693
|
+
await this.#connection.hset(
|
|
694
|
+
`${schedulesKey}::${data.id}`,
|
|
695
|
+
"next_run_at",
|
|
696
|
+
newNextRunAt.toString()
|
|
697
|
+
);
|
|
700
698
|
}
|
|
701
|
-
return
|
|
699
|
+
return this.#hashToScheduleData(data);
|
|
702
700
|
}
|
|
703
701
|
#hashToScheduleData(data) {
|
|
704
702
|
return {
|