1
1
import type { Redis } from "ioredis" ;
2
2
import type { Logger } from "pino" ;
3
+ import { ScheduleMap } from ".." ;
3
4
import { Closable } from "../Closable" ;
4
5
import { tenantToRedisPrefix } from "../encodeRedisKey" ;
5
6
import type { Producer } from "../producer/producer" ;
6
7
import { computeTimestampForNextRetry } from "../worker/retry" ;
8
+ import { getNextExecutionDate } from "../worker/worker" ;
7
9
import type { Acknowledger } from "./acknowledger" ;
8
10
import { scanTenantsForProcessing } from "./scan-tenants" ;
9
11
12
+ function valueAndScoreToObj ( arr : ( string | number ) [ ] ) {
13
+ const result : { value : string ; score : number } [ ] = [ ] ;
14
+
15
+ for ( let i = 0 ; i < arr . length ; i += 2 ) {
16
+ result . push ( {
17
+ value : String ( arr [ i ] ) ,
18
+ score : Number ( arr [ i + 1 ] ) ,
19
+ } ) ;
20
+ }
21
+
22
+ return result ;
23
+ }
24
+
10
25
const oneMinute = 60 * 1000 ;
11
26
12
27
export interface StaleCheckerConfig {
@@ -23,6 +38,7 @@ export class StaleChecker<ScheduleType extends string> implements Closable {
23
38
private readonly redis : Redis ,
24
39
private readonly acknowledger : Acknowledger < ScheduleType > ,
25
40
private readonly producer : Producer < ScheduleType > ,
41
+ private readonly scheduleMap : ScheduleMap < ScheduleType > ,
26
42
config : StaleCheckerConfig = { } ,
27
43
private readonly logger ?: Logger
28
44
) {
@@ -53,7 +69,7 @@ export class StaleChecker<ScheduleType extends string> implements Closable {
53
69
) {
54
70
const result = await this . redis
55
71
. pipeline ( )
56
- . zrangebyscore ( key , min , max )
72
+ . zrangebyscore ( key , min , max , "WITHSCORES" )
57
73
. zremrangebyscore ( key , min , max )
58
74
. exec ( ) ;
59
75
@@ -67,7 +83,7 @@ export class StaleChecker<ScheduleType extends string> implements Closable {
67
83
throw remRangeByScoreResult [ 0 ] ;
68
84
}
69
85
70
- return rangeByScoreResult [ 1 ] as string [ ] ;
86
+ return valueAndScoreToObj ( rangeByScoreResult [ 1 ] ) ;
71
87
}
72
88
73
89
private parseJobDescriptor ( descriptor : string ) {
@@ -103,14 +119,18 @@ export class StaleChecker<ScheduleType extends string> implements Closable {
103
119
104
120
const staleJobs = await this . producer . findJobs (
105
121
tenant ,
106
- staleJobDescriptors . map ( this . parseJobDescriptor )
122
+ staleJobDescriptors . map ( ( { value } ) =>
123
+ this . parseJobDescriptor ( value )
124
+ )
107
125
) ;
108
126
109
127
const pipeline = this . redis . pipeline ( ) ;
110
128
111
129
const error = "Job Timed Out" ;
112
130
113
- for ( const job of staleJobs ) {
131
+ for ( let i = 0 ; i < staleJobs . length ; i ++ ) {
132
+ const job = staleJobs [ 0 ] ;
133
+ const score = staleJobDescriptors [ 0 ] . score ;
114
134
if ( ! job ) {
115
135
this . logger ?. error (
116
136
{ tenant } ,
@@ -125,6 +145,18 @@ export class StaleChecker<ScheduleType extends string> implements Closable {
125
145
job . count
126
146
) ;
127
147
148
+ // TODO: duplicated logic in producer, please extract
149
+ let nextExecutionDate : number | undefined = undefined ;
150
+
151
+ if ( ! job . schedule ?. times || job . count < job . schedule ?. times ) {
152
+ nextExecutionDate = getNextExecutionDate (
153
+ this . scheduleMap ,
154
+ job . schedule ?. type ,
155
+ job . schedule ?. meta ?? "" ,
156
+ new Date ( score )
157
+ ) ;
158
+ }
159
+
128
160
this . logger ?. trace (
129
161
{ tenant, job } ,
130
162
"Stale-Checker: Adding Failure report to pipeline"
@@ -136,6 +168,7 @@ export class StaleChecker<ScheduleType extends string> implements Closable {
136
168
queueId : job . queue ,
137
169
jobId : job . id ,
138
170
timestampForNextRetry,
171
+ nextExecutionDate,
139
172
} ,
140
173
job ,
141
174
error ,
0 commit comments