@gravito/zenith 1.1.0 → 1.1.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/bin.js +14873 -4481
  3. package/dist/client/assets/index-BSMp8oq_.js +436 -0
  4. package/dist/client/assets/index-BwxlHx-_.css +1 -0
  5. package/dist/client/index.html +2 -2
  6. package/dist/server/index.js +14873 -4481
  7. package/package.json +1 -1
  8. package/scripts/verify-throttle.ts +6 -2
  9. package/scripts/worker.ts +2 -1
  10. package/src/client/Layout.tsx +10 -0
  11. package/src/client/Sidebar.tsx +9 -0
  12. package/src/client/ThroughputChart.tsx +9 -0
  13. package/src/client/WorkerStatus.tsx +14 -3
  14. package/src/client/components/BrandIcons.tsx +30 -0
  15. package/src/client/components/ConfirmDialog.tsx +24 -0
  16. package/src/client/components/JobInspector.tsx +18 -0
  17. package/src/client/components/LogArchiveModal.tsx +51 -2
  18. package/src/client/components/NotificationBell.tsx +9 -0
  19. package/src/client/components/PageHeader.tsx +9 -0
  20. package/src/client/components/Toaster.tsx +10 -0
  21. package/src/client/components/UserProfileDropdown.tsx +9 -0
  22. package/src/client/contexts/AuthContext.tsx +12 -0
  23. package/src/client/contexts/NotificationContext.tsx +25 -0
  24. package/src/client/pages/LoginPage.tsx +9 -0
  25. package/src/client/pages/MetricsPage.tsx +9 -0
  26. package/src/client/pages/OverviewPage.tsx +9 -0
  27. package/src/client/pages/PulsePage.tsx +10 -0
  28. package/src/client/pages/QueuesPage.tsx +9 -0
  29. package/src/client/pages/SchedulesPage.tsx +9 -0
  30. package/src/client/pages/SettingsPage.tsx +9 -0
  31. package/src/client/pages/WorkersPage.tsx +10 -0
  32. package/src/client/utils.ts +9 -0
  33. package/src/server/config/ServerConfigManager.ts +90 -0
  34. package/src/server/index.ts +23 -19
  35. package/src/server/services/AlertService.ts +16 -3
  36. package/src/server/services/LogStreamProcessor.ts +93 -0
  37. package/src/server/services/MaintenanceScheduler.ts +78 -0
  38. package/src/server/services/PulseService.ts +12 -1
  39. package/src/server/services/QueueMetricsCollector.ts +138 -0
  40. package/src/server/services/QueueService.ts +29 -283
  41. package/src/shared/types.ts +126 -27
  42. package/vite.config.ts +1 -1
  43. package/dist/client/assets/index-C332gZ-J.css +0 -1
  44. package/dist/client/assets/index-D4HibwTK.js +0 -436
@@ -2,6 +2,9 @@ import { EventEmitter } from 'node:events'
2
2
  import { type MySQLPersistence, QueueManager } from '@gravito/stream'
3
3
  import { Redis } from 'ioredis'
4
4
  import { AlertService } from './AlertService'
5
+ import { LogStreamProcessor } from './LogStreamProcessor'
6
+ import { MaintenanceScheduler } from './MaintenanceScheduler'
7
+ import { QueueMetricsCollector } from './QueueMetricsCollector'
5
8
 
6
9
  export interface QueueStats {
7
10
  name: string
@@ -47,11 +50,11 @@ export class QueueService {
47
50
  private subRedis: Redis
48
51
  private prefix: string
49
52
  private logEmitter = new EventEmitter()
50
- private logThrottleCount = 0
51
- private logThrottleReset = Date.now()
52
- private readonly MAX_LOGS_PER_SEC = 50
53
53
  private manager: QueueManager
54
54
  public alerts: AlertService
55
+ private logProcessor: LogStreamProcessor
56
+ private metricsCollector: QueueMetricsCollector
57
+ private maintenanceScheduler: MaintenanceScheduler
55
58
 
56
59
  constructor(
57
60
  redisUrl: string,
@@ -72,7 +75,12 @@ export class QueueService {
72
75
  this.prefix = prefix
73
76
  this.logEmitter.setMaxListeners(1000)
74
77
 
75
- // Initialized for potential use
78
+ this.logProcessor = new LogStreamProcessor(this.redis, this.subRedis)
79
+ this.metricsCollector = new QueueMetricsCollector(this.redis, prefix)
80
+ this.maintenanceScheduler = new MaintenanceScheduler(this.redis, (days) =>
81
+ this.cleanupArchive(days)
82
+ )
83
+
76
84
  this.manager = new QueueManager({
77
85
  default: 'redis',
78
86
  connections: {
@@ -88,179 +96,41 @@ export class QueueService {
88
96
  }
89
97
 
90
98
  async connect() {
91
- await Promise.all([this.redis.connect(), this.subRedis.connect(), this.alerts.connect()])
92
-
93
- // Setup single Redis subscription
94
- await this.subRedis.subscribe('flux_console:logs')
95
- this.subRedis.on('message', (channel, message) => {
96
- if (channel === 'flux_console:logs') {
97
- try {
98
- // Throttling: Reset counter every second
99
- const now = Date.now()
100
- if (now - this.logThrottleReset > 1000) {
101
- this.logThrottleReset = now
102
- this.logThrottleCount = 0
103
- }
104
-
105
- // Emit only if under limit
106
- if (this.logThrottleCount < this.MAX_LOGS_PER_SEC) {
107
- this.logThrottleCount++
108
- const log = JSON.parse(message)
109
- this.logEmitter.emit('log', log)
110
-
111
- // Increment throughput counter if it's a job final status
112
- if (log.level === 'success' || log.level === 'error') {
113
- const minute = Math.floor(Date.now() / 60000)
114
- this.redis
115
- .incr(`flux_console:throughput:${minute}`)
116
- .then(() => {
117
- this.redis.expire(`flux_console:throughput:${minute}`, 3600)
118
- })
119
- .catch(() => {})
120
- }
121
- }
122
- } catch (_e) {
123
- // Ignore
124
- }
125
- }
126
- })
127
-
128
- // Start Maintenance Loop
129
- this.runMaintenanceLoop()
130
- }
131
-
132
- private async runMaintenanceLoop() {
133
- // Initial delay to avoid startup congestion
134
- setTimeout(() => {
135
- const loop = async () => {
136
- try {
137
- await this.checkMaintenance()
138
- } catch (err) {
139
- console.error('[Maintenance] Task Error:', err)
140
- }
141
- // Check every hour (3600000 ms)
142
- setTimeout(loop, 3600000)
143
- }
144
- loop()
145
- }, 1000 * 30) // 30 seconds after boot
146
- }
147
-
148
- private async checkMaintenance() {
149
- const config = await this.getMaintenanceConfig()
150
- if (!config.autoCleanup) return
151
-
152
- const now = Date.now()
153
- const lastRun = config.lastRun || 0
154
- const ONE_DAY = 24 * 60 * 60 * 1000
155
-
156
- if (now - lastRun >= ONE_DAY) {
157
- console.log(
158
- `[Maintenance] Starting Auto-Cleanup (Retention: ${config.retentionDays} days)...`
159
- )
160
- const deleted = await this.cleanupArchive(config.retentionDays)
161
- console.log(`[Maintenance] Cleanup Complete. Removed ${deleted} records.`)
162
-
163
- // Update Last Run
164
- await this.saveMaintenanceConfig({
165
- ...config,
166
- lastRun: now,
167
- })
168
- }
169
- }
170
-
171
- async getMaintenanceConfig(): Promise<any> {
172
- const data = await this.redis.get('gravito:zenith:maintenance:config')
173
- if (data) return JSON.parse(data)
174
- return { autoCleanup: false, retentionDays: 30 }
175
- }
99
+ await Promise.all([
100
+ this.redis.connect(),
101
+ this.subRedis.connect(),
102
+ this.alerts.connect(),
103
+ this.logProcessor.subscribe(),
104
+ ])
176
105
 
177
- async saveMaintenanceConfig(config: any): Promise<void> {
178
- await this.redis.set('gravito:zenith:maintenance:config', JSON.stringify(config))
106
+ this.maintenanceScheduler.start(30000)
179
107
  }
180
108
 
181
- /**
182
- * Subscribes to the live log stream.
183
- * Returns a cleanup function.
184
- */
185
109
  onLog(callback: (msg: SystemLog) => void): () => void {
186
- this.logEmitter.on('log', callback)
187
- return () => {
110
+ const unsub = this.logProcessor.onLog(callback)
111
+ const emitterUnsub = () => {
188
112
  this.logEmitter.off('log', callback)
189
113
  }
114
+ return () => {
115
+ unsub()
116
+ emitterUnsub()
117
+ }
190
118
  }
191
119
 
192
- /**
193
- * Discovers queues using SCAN to avoid blocking Redis.
194
- */
195
120
  async listQueues(): Promise<QueueStats[]> {
196
- const queues = new Set<string>()
197
- let cursor = '0'
198
- let limit = 1000
199
-
200
- do {
201
- const result = await this.redis.scan(cursor, 'MATCH', `${this.prefix}*`, 'COUNT', 100)
202
- cursor = result[0]
203
- const keys = result[1]
204
-
205
- for (const key of keys) {
206
- const relative = key.slice(this.prefix.length)
207
- const parts = relative.split(':')
208
- const candidateName = parts[0]
209
- if (
210
- candidateName &&
211
- candidateName !== 'active' &&
212
- candidateName !== 'schedules' &&
213
- candidateName !== 'schedule' &&
214
- candidateName !== 'lock'
215
- ) {
216
- queues.add(candidateName)
217
- }
218
- }
219
- limit--
220
- } while (cursor !== '0' && limit > 0)
221
-
222
- const stats: QueueStats[] = []
223
- const queueNames = Array.from(queues).sort()
224
-
225
- const BATCH_SIZE = 10
226
-
227
- for (let i = 0; i < queueNames.length; i += BATCH_SIZE) {
228
- const batch = queueNames.slice(i, i + BATCH_SIZE)
229
- const batchResults = await Promise.all(
230
- batch.map(async (name) => {
231
- const waiting = await this.redis.llen(`${this.prefix}${name}`)
232
- const delayed = await this.redis.zcard(`${this.prefix}${name}:delayed`)
233
- const failed = await this.redis.llen(`${this.prefix}${name}:failed`)
234
- const active = await this.redis.scard(`${this.prefix}${name}:active`)
235
- const paused = await this.redis.get(`${this.prefix}${name}:paused`)
236
- return { name, waiting, delayed, failed, active, paused: paused === '1' }
237
- })
238
- )
239
- stats.push(...batchResults)
240
- }
241
-
242
- return stats
121
+ return this.metricsCollector.listQueues()
243
122
  }
244
123
 
245
- /**
246
- * Pause a queue (workers will stop processing new jobs)
247
- */
248
124
  async pauseQueue(queueName: string): Promise<boolean> {
249
125
  await this.redis.set(`${this.prefix}${queueName}:paused`, '1')
250
126
  return true
251
127
  }
252
128
 
253
- /**
254
- * Resume a paused queue
255
- */
256
129
  async resumeQueue(queueName: string): Promise<boolean> {
257
130
  await this.redis.del(`${this.prefix}${queueName}:paused`)
258
131
  return true
259
132
  }
260
133
 
261
- /**
262
- * Check if a queue is paused
263
- */
264
134
  async isQueuePaused(queueName: string): Promise<boolean> {
265
135
  const paused = await this.redis.get(`${this.prefix}${queueName}:paused`)
266
136
  return paused === '1'
@@ -327,7 +197,6 @@ export class QueueService {
327
197
  }
328
198
  })
329
199
 
330
- // If we got few results and have persistence, merge with archive
331
200
  const persistence = this.manager.getPersistence()
332
201
  if (jobs.length < stop - start + 1 && persistence && type === 'failed') {
333
202
  const archived = await persistence.list(queueName, {
@@ -341,9 +210,6 @@ export class QueueService {
341
210
  }
342
211
  }
343
212
 
344
- /**
345
- * Records a snapshot of current global statistics for sparklines.
346
- */
347
213
  async recordStatusMetrics(
348
214
  nodes: Record<string, any> = {},
349
215
  injectedWorkers?: any[]
@@ -362,25 +228,21 @@ export class QueueService {
362
228
  const now = Math.floor(Date.now() / 60000)
363
229
  const pipe = this.redis.pipeline()
364
230
 
365
- // Store snapshots for last 60 minutes
366
231
  pipe.set(`flux_console:metrics:waiting:${now}`, totals.waiting, 'EX', 3600)
367
232
  pipe.set(`flux_console:metrics:delayed:${now}`, totals.delayed, 'EX', 3600)
368
233
  pipe.set(`flux_console:metrics:failed:${now}`, totals.failed, 'EX', 3600)
369
234
 
370
- // Also record worker count
371
235
  const workers = injectedWorkers || (await this.listWorkers())
372
236
  pipe.set(`flux_console:metrics:workers:${now}`, workers.length, 'EX', 3600)
373
237
 
374
238
  await pipe.exec()
375
239
 
376
- // Real-time Broadcast
377
240
  this.logEmitter.emit('stats', {
378
241
  queues: stats,
379
242
  throughput: await this.getThroughputData(),
380
243
  workers,
381
244
  })
382
245
 
383
- // Evaluate Alert Rules (Near Zero Overhead)
384
246
  this.alerts
385
247
  .check({
386
248
  queues: stats,
@@ -391,9 +253,6 @@ export class QueueService {
391
253
  .catch((err) => console.error('[AlertService] Rule Evaluation Error:', err))
392
254
  }
393
255
 
394
- /**
395
- * Subscribes to real-time stats updates.
396
- */
397
256
  onStats(callback: (stats: GlobalStats) => void): () => void {
398
257
  this.logEmitter.on('stats', callback)
399
258
  return () => {
@@ -401,9 +260,6 @@ export class QueueService {
401
260
  }
402
261
  }
403
262
 
404
- /**
405
- * Gets historical data for a specific metric.
406
- */
407
263
  async getMetricHistory(metric: string, limit = 15): Promise<number[]> {
408
264
  const now = Math.floor(Date.now() / 60000)
409
265
  const keys = []
@@ -415,9 +271,6 @@ export class QueueService {
415
271
  return values.map((v) => parseInt(v || '0', 10))
416
272
  }
417
273
 
418
- /**
419
- * Retrieves throughput data for the last 15 minutes.
420
- */
421
274
  async getThroughputData(): Promise<{ timestamp: string; count: number }[]> {
422
275
  const now = Math.floor(Date.now() / 60000)
423
276
  const results = []
@@ -435,37 +288,10 @@ export class QueueService {
435
288
  return results
436
289
  }
437
290
 
438
- /**
439
- * Lists all active workers by scanning heartbeat keys.
440
- */
441
291
  async listWorkers(): Promise<WorkerReport[]> {
442
- const workers: WorkerReport[] = []
443
- let cursor = '0'
444
-
445
- do {
446
- const [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', 'flux_console:worker:*')
447
- cursor = nextCursor
448
-
449
- if (keys.length > 0) {
450
- const values = await this.redis.mget(...keys)
451
- values.forEach((v) => {
452
- if (v) {
453
- try {
454
- workers.push(JSON.parse(v))
455
- } catch (_e) {
456
- // Ignore malformed
457
- }
458
- }
459
- })
460
- }
461
- } while (cursor !== '0')
462
-
463
- return workers.sort((a, b) => a.id.localeCompare(b.id))
292
+ return this.metricsCollector.listWorkers()
464
293
  }
465
294
 
466
- /**
467
- * Deletes a specific job from a queue or delayed pool.
468
- */
469
295
  async deleteJob(
470
296
  queueName: string,
471
297
  type: 'waiting' | 'delayed' | 'failed',
@@ -484,14 +310,10 @@ export class QueueService {
484
310
  return result > 0
485
311
  }
486
312
 
487
- /**
488
- * Retries a specific delayed job by moving it back to the waiting queue.
489
- */
490
313
  async retryJob(queueName: string, jobRaw: string): Promise<boolean> {
491
314
  const key = `${this.prefix}${queueName}`
492
315
  const delayKey = `${key}:delayed`
493
316
 
494
- // Atomically move from ZSET to LIST
495
317
  const script = `
496
318
  local delayKey = KEYS[1]
497
319
  local queueKey = KEYS[2]
@@ -508,9 +330,6 @@ export class QueueService {
508
330
  return result === 1
509
331
  }
510
332
 
511
- /**
512
- * Purges all jobs from a queue.
513
- */
514
333
  async purgeQueue(queueName: string): Promise<void> {
515
334
  const pipe = this.redis.pipeline()
516
335
  pipe.del(`${this.prefix}${queueName}`)
@@ -520,25 +339,14 @@ export class QueueService {
520
339
  await pipe.exec()
521
340
  }
522
341
 
523
- /**
524
- * Retries all failed jobs in a queue.
525
- */
526
342
  async retryAllFailedJobs(queueName: string): Promise<number> {
527
- // Navigate via QueueManager -> Driver to use safe RPOPLPUSH (avoids Lua stack overflow)
528
- // We pass a large number to retry "all" (effectively batch processing)
529
343
  return await this.manager.retryFailed(queueName, 10000)
530
344
  }
531
345
 
532
- /**
533
- * Clears all failed jobs (DLQ).
534
- */
535
346
  async clearFailedJobs(queueName: string): Promise<void> {
536
347
  await this.manager.clearFailed(queueName)
537
348
  }
538
349
 
539
- /**
540
- * Get total count of jobs in a queue by type.
541
- */
542
350
  async getJobCount(queueName: string, type: 'waiting' | 'delayed' | 'failed'): Promise<number> {
543
351
  const key =
544
352
  type === 'delayed'
@@ -550,9 +358,6 @@ export class QueueService {
550
358
  return type === 'delayed' ? await this.redis.zcard(key) : await this.redis.llen(key)
551
359
  }
552
360
 
553
- /**
554
- * Delete ALL jobs of a specific type from a queue.
555
- */
556
361
  async deleteAllJobs(queueName: string, type: 'waiting' | 'delayed' | 'failed'): Promise<number> {
557
362
  const key =
558
363
  type === 'delayed'
@@ -566,9 +371,6 @@ export class QueueService {
566
371
  return count
567
372
  }
568
373
 
569
- /**
570
- * Retry ALL jobs of a specific type (delayed or failed).
571
- */
572
374
  async retryAllJobs(queueName: string, type: 'delayed' | 'failed'): Promise<number> {
573
375
  if (type === 'delayed') {
574
376
  return await this.retryDelayedJob(queueName)
@@ -577,9 +379,6 @@ export class QueueService {
577
379
  }
578
380
  }
579
381
 
580
- /**
581
- * Bulk deletes jobs (works for waiting, delayed, failed).
582
- */
583
382
  async deleteJobs(
584
383
  queueName: string,
585
384
  type: 'waiting' | 'delayed' | 'failed',
@@ -604,9 +403,6 @@ export class QueueService {
604
403
  return results?.reduce((acc, [_, res]) => acc + ((res as number) || 0), 0) || 0
605
404
  }
606
405
 
607
- /**
608
- * Bulk retries jobs (moves from failed/delayed to waiting).
609
- */
610
406
  async retryJobs(
611
407
  queueName: string,
612
408
  type: 'delayed' | 'failed',
@@ -626,8 +422,6 @@ export class QueueService {
626
422
  }
627
423
  }
628
424
  const results = await pipe.exec()
629
- // Each successful retry is 2 operations in pipeline (remove + push),
630
- // but we count the successfully removed jobs.
631
425
  let count = 0
632
426
  if (results) {
633
427
  for (let i = 0; i < results.length; i += 2) {
@@ -640,9 +434,6 @@ export class QueueService {
640
434
  return count
641
435
  }
642
436
 
643
- /**
644
- * Publishes a log message (used by workers).
645
- */
646
437
  async publishLog(log: { level: string; message: string; workerId: string; queue?: string }) {
647
438
  const payload = {
648
439
  ...log,
@@ -650,19 +441,16 @@ export class QueueService {
650
441
  }
651
442
  await this.redis.publish('flux_console:logs', JSON.stringify(payload))
652
443
 
653
- // Also store in a capped list for history (last 100 logs)
654
444
  const pipe = this.redis.pipeline()
655
445
  pipe.lpush('flux_console:logs:history', JSON.stringify(payload))
656
446
  pipe.ltrim('flux_console:logs:history', 0, 99)
657
447
 
658
- // Increment throughput counter for this minute
659
448
  const now = Math.floor(Date.now() / 60000)
660
449
  pipe.incr(`flux_console:throughput:${now}`)
661
- pipe.expire(`flux_console:throughput:${now}`, 3600) // Keep for 1 hour
450
+ pipe.expire(`flux_console:throughput:${now}`, 3600)
662
451
 
663
452
  await pipe.exec()
664
453
 
665
- // NEW: Archive to persistence if enabled
666
454
  const persistence = this.manager.getPersistence()
667
455
  if (persistence) {
668
456
  persistence
@@ -674,17 +462,11 @@ export class QueueService {
674
462
  }
675
463
  }
676
464
 
677
- /**
678
- * Gets recent log history.
679
- */
680
465
  async getLogHistory(): Promise<any[]> {
681
466
  const logs = await this.redis.lrange('flux_console:logs:history', 0, -1)
682
467
  return logs.map((l) => JSON.parse(l)).reverse()
683
468
  }
684
469
 
685
- /**
686
- * Search jobs across all queues by ID or data content.
687
- */
688
470
  async searchJobs(
689
471
  query: string,
690
472
  options: { limit?: number; type?: 'all' | 'waiting' | 'delayed' | 'failed' } = {}
@@ -693,7 +475,6 @@ export class QueueService {
693
475
  const results: any[] = []
694
476
  const queryLower = query.toLowerCase()
695
477
 
696
- // Get all queues
697
478
  const queues = await this.listQueues()
698
479
 
699
480
  for (const queue of queues) {
@@ -715,20 +496,14 @@ export class QueueService {
715
496
  break
716
497
  }
717
498
 
718
- // Search in job ID
719
499
  const idMatch = job.id && String(job.id).toLowerCase().includes(queryLower)
720
-
721
- // Search in job name
722
500
  const nameMatch = job.name && String(job.name).toLowerCase().includes(queryLower)
723
501
 
724
- // Search in job data (stringify and search)
725
502
  let dataMatch = false
726
503
  try {
727
504
  const dataStr = JSON.stringify(job.data || job).toLowerCase()
728
505
  dataMatch = dataStr.includes(queryLower)
729
- } catch (_e) {
730
- // Ignore stringify errors
731
- }
506
+ } catch (_e) {}
732
507
 
733
508
  if (idMatch || nameMatch || dataMatch) {
734
509
  results.push({
@@ -745,9 +520,6 @@ export class QueueService {
745
520
  return results
746
521
  }
747
522
 
748
- /**
749
- * List jobs from the SQL archive.
750
- */
751
523
  async getArchiveJobs(
752
524
  queue: string,
753
525
  page = 1,
@@ -772,9 +544,6 @@ export class QueueService {
772
544
  }
773
545
  }
774
546
 
775
- /**
776
- * Search jobs from the SQL archive.
777
- */
778
547
  async searchArchive(
779
548
  query: string,
780
549
  options: { limit?: number; page?: number; queue?: string } = {}
@@ -788,17 +557,12 @@ export class QueueService {
788
557
  const offset = (page - 1) * limit
789
558
 
790
559
  const jobs = await persistence.search(query, { limit, offset, queue })
791
- // For search, precise total count is harder without a dedicated search count method,
792
- // so we'll return the results length or a hypothetical high number if results match the limit.
793
560
  return {
794
561
  jobs: jobs.map((j: any) => ({ ...j, _archived: true })),
795
562
  total: jobs.length === limit ? limit * page + 1 : (page - 1) * limit + jobs.length,
796
563
  }
797
564
  }
798
565
 
799
- /**
800
- * List logs from the SQL archive.
801
- */
802
566
  async getArchivedLogs(
803
567
  options: {
804
568
  page?: number
@@ -827,9 +591,6 @@ export class QueueService {
827
591
  return { logs, total }
828
592
  }
829
593
 
830
- /**
831
- * Cleans up old archived jobs from SQL.
832
- */
833
594
  async cleanupArchive(days: number): Promise<number> {
834
595
  const persistence = this.manager.getPersistence()
835
596
  if (!persistence) {
@@ -838,17 +599,11 @@ export class QueueService {
838
599
  return await persistence.cleanup(days)
839
600
  }
840
601
 
841
- /**
842
- * List all recurring schedules.
843
- */
844
602
  async listSchedules(): Promise<any[]> {
845
603
  const scheduler = this.manager.getScheduler()
846
604
  return await scheduler.list()
847
605
  }
848
606
 
849
- /**
850
- * Register a new recurring schedule.
851
- */
852
607
  async registerSchedule(config: {
853
608
  id: string
854
609
  cron: string
@@ -859,25 +614,16 @@ export class QueueService {
859
614
  await scheduler.register(config)
860
615
  }
861
616
 
862
- /**
863
- * Remove a recurring schedule.
864
- */
865
617
  async removeSchedule(id: string): Promise<void> {
866
618
  const scheduler = this.manager.getScheduler()
867
619
  await scheduler.remove(id)
868
620
  }
869
621
 
870
- /**
871
- * Run a scheduled job immediately.
872
- */
873
622
  async runScheduleNow(id: string): Promise<void> {
874
623
  const scheduler = this.manager.getScheduler()
875
624
  await scheduler.runNow(id)
876
625
  }
877
626
 
878
- /**
879
- * Tick the scheduler to process due jobs.
880
- */
881
627
  async tickScheduler(): Promise<void> {
882
628
  const scheduler = this.manager.getScheduler()
883
629
  await scheduler.tick()