@nest-batch/core 0.2.0 → 0.2.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 +7 -5
- package/dist/src/adapters/in-process.adapter.d.ts +16 -13
- package/dist/src/adapters/in-process.adapter.d.ts.map +1 -1
- package/dist/src/adapters/in-process.adapter.js +2 -0
- package/dist/src/adapters/in-process.adapter.js.map +1 -1
- package/dist/src/compiler/definition-compiler.d.ts.map +1 -1
- package/dist/src/compiler/definition-compiler.js +3 -0
- package/dist/src/compiler/definition-compiler.js.map +1 -1
- package/dist/src/core/ir/listener-definition.d.ts +2 -0
- package/dist/src/core/ir/listener-definition.d.ts.map +1 -1
- package/dist/src/core/item/interfaces.d.ts +14 -4
- package/dist/src/core/item/interfaces.d.ts.map +1 -1
- package/dist/src/decorators/listener.decorators.d.ts +4 -3
- package/dist/src/decorators/listener.decorators.d.ts.map +1 -1
- package/dist/src/decorators/listener.decorators.js +6 -3
- package/dist/src/decorators/listener.decorators.js.map +1 -1
- package/dist/src/execution/chunk-step-executor.d.ts +7 -1
- package/dist/src/execution/chunk-step-executor.d.ts.map +1 -1
- package/dist/src/execution/chunk-step-executor.js +104 -13
- package/dist/src/execution/chunk-step-executor.js.map +1 -1
- package/dist/src/execution/in-process-schedule.d.ts +25 -0
- package/dist/src/execution/in-process-schedule.d.ts.map +1 -0
- package/dist/src/execution/in-process-schedule.js +129 -0
- package/dist/src/execution/in-process-schedule.js.map +1 -0
- package/dist/src/execution/index.d.ts +1 -0
- package/dist/src/execution/index.d.ts.map +1 -1
- package/dist/src/execution/index.js +1 -0
- package/dist/src/execution/index.js.map +1 -1
- package/dist/src/execution/job-executor.d.ts.map +1 -1
- package/dist/src/execution/job-executor.js +14 -8
- package/dist/src/execution/job-executor.js.map +1 -1
- package/dist/src/execution/listener-invoker.d.ts +25 -9
- package/dist/src/execution/listener-invoker.d.ts.map +1 -1
- package/dist/src/execution/listener-invoker.js +70 -14
- package/dist/src/execution/listener-invoker.js.map +1 -1
- package/dist/src/execution/tasklet-step-executor.d.ts +4 -1
- package/dist/src/execution/tasklet-step-executor.d.ts.map +1 -1
- package/dist/src/execution/tasklet-step-executor.js +20 -16
- package/dist/src/execution/tasklet-step-executor.js.map +1 -1
- package/dist/src/explorer/batch-explorer.d.ts +2 -1
- package/dist/src/explorer/batch-explorer.d.ts.map +1 -1
- package/dist/src/explorer/batch-explorer.js +3 -0
- package/dist/src/explorer/batch-explorer.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/module/batch-schedule-registry.d.ts +13 -14
- package/dist/src/module/batch-schedule-registry.d.ts.map +1 -1
- package/dist/src/module/batch-schedule-registry.js +0 -0
- package/dist/src/module/batch-schedule-registry.js.map +1 -1
- package/dist/src/module/nest-batch.module.d.ts +4 -3
- package/dist/src/module/nest-batch.module.d.ts.map +1 -1
- package/dist/src/module/nest-batch.module.js +3 -2
- package/dist/src/module/nest-batch.module.js.map +1 -1
- package/dist/src/module/tokens.d.ts +5 -6
- package/dist/src/module/tokens.d.ts.map +1 -1
- package/dist/src/module/tokens.js.map +1 -1
- package/dist/src/partition-helpers.d.ts +3 -3
- package/dist/src/partition-helpers.d.ts.map +1 -1
- package/dist/src/partition-helpers.js +3 -3
- package/dist/src/partition-helpers.js.map +1 -1
- package/dist/src/scheduling/batch-scheduled.d.ts +9 -11
- package/dist/src/scheduling/batch-scheduled.d.ts.map +1 -1
- package/dist/src/scheduling/batch-scheduled.js +12 -20
- package/dist/src/scheduling/batch-scheduled.js.map +1 -1
- package/package.json +4 -1
- package/src/adapters/in-process.adapter.ts +18 -13
- package/src/compiler/definition-compiler.ts +12 -5
- package/src/core/ir/listener-definition.ts +2 -0
- package/src/core/item/interfaces.ts +15 -4
- package/src/decorators/listener.decorators.ts +11 -13
- package/src/execution/chunk-step-executor.ts +212 -18
- package/src/execution/in-process-schedule.ts +143 -0
- package/src/execution/index.ts +1 -0
- package/src/execution/job-executor.ts +30 -21
- package/src/execution/listener-invoker.ts +105 -27
- package/src/execution/tasklet-step-executor.ts +40 -16
- package/src/explorer/batch-explorer.ts +10 -4
- package/src/index.ts +1 -0
- package/src/module/batch-schedule-registry.ts +0 -0
- package/src/module/nest-batch.module.ts +21 -42
- package/src/module/tokens.ts +8 -15
- package/src/partition-helpers.ts +13 -17
- package/src/scheduling/batch-scheduled.ts +22 -32
|
@@ -44,7 +44,8 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
44
44
|
async execute(step, context) {
|
|
45
45
|
const skipPolicy = step.skipPolicy ? (0, _skippolicy.compileSkipPolicy)(step.skipPolicy) : null;
|
|
46
46
|
const retryPolicy = step.retryPolicy ? (0, _retrypolicy.compileRetryPolicy)(step.retryPolicy) : null;
|
|
47
|
-
const
|
|
47
|
+
const listenerResolvers = context.listenerResolvers ?? new Map();
|
|
48
|
+
const skipResolvers = context.skipListenerResolvers ?? listenerResolvers;
|
|
48
49
|
const skipLimit = step.skipPolicy?.limit ?? 0;
|
|
49
50
|
const retryLimit = step.retryPolicy?.limit ?? 0;
|
|
50
51
|
// Partition routing (T8): when the transport attached a
|
|
@@ -95,6 +96,7 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
95
96
|
let chunkIndex = 0;
|
|
96
97
|
let openedStreams = [];
|
|
97
98
|
let streamContext = null;
|
|
99
|
+
await context.listenerInvoker.invokeBefore(listenerResolvers, 'step', this.buildListenerContext(step, context));
|
|
98
100
|
try {
|
|
99
101
|
// Resolve inside the try block so a missing provider-token ref
|
|
100
102
|
// surfaces as FAILED/{exitMessage: <err>}, matching the tasklet
|
|
@@ -130,8 +132,9 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
130
132
|
// slice on the next iteration.
|
|
131
133
|
const skipCap = partition !== null ? Math.max(0, partition.to - partition.from - readCount) : step.chunkSize;
|
|
132
134
|
if (skipCap === 0) break;
|
|
135
|
+
const itemContext = this.buildItemContext(step, context, chunkIndex);
|
|
133
136
|
for(let i = 0; i < skipCap; i++){
|
|
134
|
-
const item = await reader.read();
|
|
137
|
+
const item = await reader.read(itemContext);
|
|
135
138
|
if (item == null) break;
|
|
136
139
|
drained += 1;
|
|
137
140
|
}
|
|
@@ -153,9 +156,14 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
153
156
|
if (remaining === 0) {
|
|
154
157
|
break;
|
|
155
158
|
}
|
|
159
|
+
const itemContext = this.buildItemContext(step, context, chunkIndex);
|
|
160
|
+
const listenerContext = this.buildListenerContext(step, context, {
|
|
161
|
+
chunkIndex
|
|
162
|
+
});
|
|
163
|
+
await context.listenerInvoker.invokeBefore(listenerResolvers, 'chunk', listenerContext);
|
|
156
164
|
// ---- READ PHASE: per-item retry+skip ----
|
|
157
165
|
for(let i = 0; i < remaining && !eof; i++){
|
|
158
|
-
const r = await this.runPhase(()=>reader.read(), {
|
|
166
|
+
const r = await this.runPhase(()=>reader.read(itemContext), {
|
|
159
167
|
phase: 'read',
|
|
160
168
|
item: null,
|
|
161
169
|
skipPolicy,
|
|
@@ -166,6 +174,17 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
166
174
|
onSkip: async (err)=>{
|
|
167
175
|
skipCount += 1;
|
|
168
176
|
await context.listenerInvoker.invokeOnSkipRead(skipResolvers, err, null);
|
|
177
|
+
},
|
|
178
|
+
before: async ()=>{
|
|
179
|
+
await context.listenerInvoker.invokeBeforeRead(listenerResolvers, listenerContext);
|
|
180
|
+
},
|
|
181
|
+
after: async (item)=>{
|
|
182
|
+
if (item !== null && item !== undefined) {
|
|
183
|
+
await context.listenerInvoker.invokeAfterRead(listenerResolvers, item, listenerContext);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
onError: async (err)=>{
|
|
187
|
+
await context.listenerInvoker.invokeOnReadError(listenerResolvers, err, listenerContext);
|
|
169
188
|
}
|
|
170
189
|
});
|
|
171
190
|
if (r.kind === 'skipped') continue;
|
|
@@ -177,7 +196,16 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
177
196
|
items.push(r.value);
|
|
178
197
|
readCount += 1;
|
|
179
198
|
}
|
|
180
|
-
if (items.length === 0)
|
|
199
|
+
if (items.length === 0) {
|
|
200
|
+
await context.listenerInvoker.invokeAfter(listenerResolvers, 'chunk', listenerContext, {
|
|
201
|
+
status: _status.StepStatus.COMPLETED,
|
|
202
|
+
readCount,
|
|
203
|
+
writeCount,
|
|
204
|
+
skipCount,
|
|
205
|
+
commitCount
|
|
206
|
+
});
|
|
207
|
+
break; // EOF (either before first read or after skips)
|
|
208
|
+
}
|
|
181
209
|
// ---- PROCESS PHASE: per-item retry+skip ----
|
|
182
210
|
const processed = [];
|
|
183
211
|
for (const item of items){
|
|
@@ -185,7 +213,7 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
185
213
|
processed.push(item);
|
|
186
214
|
continue;
|
|
187
215
|
}
|
|
188
|
-
const r = await this.runPhase(()=>processor.process(item), {
|
|
216
|
+
const r = await this.runPhase(()=>processor.process(item, itemContext), {
|
|
189
217
|
phase: 'process',
|
|
190
218
|
item,
|
|
191
219
|
skipPolicy,
|
|
@@ -196,6 +224,15 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
196
224
|
onSkip: async (err)=>{
|
|
197
225
|
skipCount += 1;
|
|
198
226
|
await context.listenerInvoker.invokeOnSkipProcess(skipResolvers, item, err);
|
|
227
|
+
},
|
|
228
|
+
before: async ()=>{
|
|
229
|
+
await context.listenerInvoker.invokeBeforeProcess(listenerResolvers, item, listenerContext);
|
|
230
|
+
},
|
|
231
|
+
after: async (value)=>{
|
|
232
|
+
await context.listenerInvoker.invokeAfterProcess(listenerResolvers, item, value, listenerContext);
|
|
233
|
+
},
|
|
234
|
+
onError: async (err)=>{
|
|
235
|
+
await context.listenerInvoker.invokeOnProcessError(listenerResolvers, item, err, listenerContext);
|
|
199
236
|
}
|
|
200
237
|
});
|
|
201
238
|
if (r.kind === 'skipped') continue;
|
|
@@ -210,7 +247,7 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
210
247
|
// ---- WRITE PHASE: per-chunk retry+skip, wrapped in transaction ----
|
|
211
248
|
if (processed.length > 0) {
|
|
212
249
|
const r = await this.runPhase(()=>context.transactionManager.withTransaction(async ()=>{
|
|
213
|
-
return writer.write(processed);
|
|
250
|
+
return writer.write(processed, itemContext);
|
|
214
251
|
}), {
|
|
215
252
|
phase: 'write',
|
|
216
253
|
item: processed,
|
|
@@ -222,6 +259,15 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
222
259
|
onSkip: async (err)=>{
|
|
223
260
|
skipCount += 1;
|
|
224
261
|
await context.listenerInvoker.invokeOnSkipWrite(skipResolvers, processed, err);
|
|
262
|
+
},
|
|
263
|
+
before: async ()=>{
|
|
264
|
+
await context.listenerInvoker.invokeBeforeWrite(listenerResolvers, processed, listenerContext);
|
|
265
|
+
},
|
|
266
|
+
after: async (value)=>{
|
|
267
|
+
await context.listenerInvoker.invokeAfterWrite(listenerResolvers, processed, value, listenerContext);
|
|
268
|
+
},
|
|
269
|
+
onError: async (err)=>{
|
|
270
|
+
await context.listenerInvoker.invokeOnWriteError(listenerResolvers, processed, err, listenerContext);
|
|
225
271
|
}
|
|
226
272
|
});
|
|
227
273
|
if (r.kind === 'ok') {
|
|
@@ -251,13 +297,20 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
251
297
|
streamContext = await context.jobRepository.getExecutionContext({
|
|
252
298
|
stepExecutionId: context.stepExecutionId
|
|
253
299
|
});
|
|
300
|
+
await context.listenerInvoker.invokeAfter(listenerResolvers, 'chunk', listenerContext, {
|
|
301
|
+
status: _status.StepStatus.COMPLETED,
|
|
302
|
+
readCount,
|
|
303
|
+
writeCount,
|
|
304
|
+
skipCount,
|
|
305
|
+
commitCount: commitCount + 1
|
|
306
|
+
});
|
|
254
307
|
commitCount += 1;
|
|
255
308
|
chunkIndex += 1;
|
|
256
309
|
}
|
|
257
310
|
const streamsToClose = openedStreams;
|
|
258
311
|
openedStreams = [];
|
|
259
312
|
await this.closeStreams(streamsToClose);
|
|
260
|
-
|
|
313
|
+
const result = {
|
|
261
314
|
status: _status.StepStatus.COMPLETED,
|
|
262
315
|
exitCode: 'COMPLETED',
|
|
263
316
|
exitMessage: '',
|
|
@@ -266,6 +319,8 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
266
319
|
skipCount,
|
|
267
320
|
commitCount
|
|
268
321
|
};
|
|
322
|
+
await context.listenerInvoker.invokeAfter(listenerResolvers, 'step', this.buildListenerContext(step, context), result);
|
|
323
|
+
return result;
|
|
269
324
|
} catch (err) {
|
|
270
325
|
let finalError = err;
|
|
271
326
|
const streamsToClose = openedStreams;
|
|
@@ -275,7 +330,10 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
275
330
|
} catch (closeErr) {
|
|
276
331
|
finalError = closeErr;
|
|
277
332
|
}
|
|
278
|
-
|
|
333
|
+
await context.listenerInvoker.invokeOnError(listenerResolvers, 'chunk', this.buildListenerContext(step, context, {
|
|
334
|
+
chunkIndex
|
|
335
|
+
}), finalError);
|
|
336
|
+
const result = {
|
|
279
337
|
status: _status.StepStatus.FAILED,
|
|
280
338
|
exitCode: 'FAILED',
|
|
281
339
|
exitMessage: finalError instanceof Error ? finalError.message : String(finalError),
|
|
@@ -284,8 +342,37 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
284
342
|
skipCount,
|
|
285
343
|
commitCount
|
|
286
344
|
};
|
|
345
|
+
await context.listenerInvoker.invokeOnError(listenerResolvers, 'step', this.buildListenerContext(step, context), finalError);
|
|
346
|
+
await context.listenerInvoker.invokeAfter(listenerResolvers, 'step', this.buildListenerContext(step, context), result);
|
|
347
|
+
return result;
|
|
287
348
|
}
|
|
288
349
|
}
|
|
350
|
+
buildListenerContext(step, context, extra = {}) {
|
|
351
|
+
return {
|
|
352
|
+
jobExecutionId: context.jobExecutionId,
|
|
353
|
+
stepExecutionId: context.stepExecutionId,
|
|
354
|
+
stepName: context.stepName ?? step.id,
|
|
355
|
+
jobParameters: context.jobParameters ?? {},
|
|
356
|
+
...extra
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
buildItemContext(step, context, chunkIndex) {
|
|
360
|
+
return {
|
|
361
|
+
jobExecutionId: context.jobExecutionId,
|
|
362
|
+
stepExecutionId: context.stepExecutionId,
|
|
363
|
+
stepName: context.stepName ?? step.id,
|
|
364
|
+
jobParameters: context.jobParameters ?? {},
|
|
365
|
+
chunkIndex,
|
|
366
|
+
getExecutionContext: async ()=>context.jobRepository.getExecutionContext({
|
|
367
|
+
stepExecutionId: context.stepExecutionId
|
|
368
|
+
}),
|
|
369
|
+
saveExecutionContext: async (ctx)=>{
|
|
370
|
+
await context.jobRepository.saveExecutionContext({
|
|
371
|
+
stepExecutionId: context.stepExecutionId
|
|
372
|
+
}, ctx);
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
}
|
|
289
376
|
async updateStreams(streams, ctx) {
|
|
290
377
|
let next = ctx;
|
|
291
378
|
for (const stream of streams){
|
|
@@ -320,13 +407,12 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
320
407
|
// exactly one attempt.
|
|
321
408
|
const outerCap = options.retryPolicy ? 999 : 1;
|
|
322
409
|
while(attempt <= outerCap){
|
|
410
|
+
if (options.before) await options.before();
|
|
411
|
+
let value;
|
|
323
412
|
try {
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
kind: 'ok',
|
|
327
|
-
value
|
|
328
|
-
};
|
|
413
|
+
value = await op();
|
|
329
414
|
} catch (err) {
|
|
415
|
+
if (options.onError) await options.onError(err);
|
|
330
416
|
// 1) Skip consultation: is this error skippable, and is there budget?
|
|
331
417
|
if (options.skipPolicy) {
|
|
332
418
|
// Use the policy's `shouldSkip` with `skipCount: 0` to get a pure
|
|
@@ -384,6 +470,11 @@ let ChunkStepExecutor = class ChunkStepExecutor {
|
|
|
384
470
|
// 3) Neither skippable nor retryable: re-throw the original error.
|
|
385
471
|
throw err;
|
|
386
472
|
}
|
|
473
|
+
if (options.after) await options.after(value);
|
|
474
|
+
return {
|
|
475
|
+
kind: 'ok',
|
|
476
|
+
value
|
|
477
|
+
};
|
|
387
478
|
}
|
|
388
479
|
// Defensive: the outer cap should never be reached when a retry policy
|
|
389
480
|
// gates us, but if no retry policy exists and the very first attempt
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/execution/chunk-step-executor.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport type { ChunkStepDefinition, ReaderRef, ProcessorRef, WriterRef } from '../core/ir';\nimport { RefKind } from '../core/ir';\nimport type { ItemReader, ItemProcessor, ItemStream, ItemWriter } from '../core/item';\nimport type { ExecutionContext, JobRepository } from '../core/repository';\nimport type { TransactionManager } from '../core/transaction';\nimport { StepStatus } from '../core/status';\nimport type { SkipPolicy, SkipContext } from '../policies/skip-policy';\nimport type { RetryPolicy, RetryContext } from '../policies/retry-policy';\nimport { compileSkipPolicy } from '../policies/skip-policy';\nimport { compileRetryPolicy } from '../policies/retry-policy';\nimport { SkipLimitExceededError, RetryLimitExceededError } from '../core/errors';\nimport { enforcePartitionIndex } from '../partition-helpers';\nimport type { ResolverMap } from './listener-invoker';\nimport { ListenerInvoker } from './listener-invoker';\nimport { resolveProviderToken, type ProviderResolvers } from './ref-resolver';\n\nexport interface ChunkExecutionContext {\n jobExecutionId: string;\n /** Step execution id, used to scope the chunk-progress checkpoint in the\n * step's ExecutionContext (saved as `{ lastChunkIndex }`). */\n stepExecutionId: string;\n jobRepository: JobRepository;\n transactionManager: TransactionManager;\n listenerInvoker: ListenerInvoker;\n /** Map of resolved reader/processor/writer functions by name. */\n resolvers: Map<string, (...args: unknown[]) => unknown | Promise<unknown>>;\n jobExecutionId2: string; // unique key for listener resolver namespacing\n /** Optional skip listener resolver map. Keys follow the `on-skip:<kind>:<name>` shape. */\n skipListenerResolvers?: ResolverMap;\n /**\n * Optional map of provider-token id → already-resolved provider instance.\n * Populated by the JobExecutor (or test fixtures) so that\n * `RefKind.ProviderToken` refs on the reader/processor/writer slots can\n * be looked up without coupling the executor to the Nest DI container.\n */\n providerResolvers?: ProviderResolvers;\n /**\n * When set, the executor skips any chunk whose 0-based index is less\n * than or equal to this value. The reader is still advanced by\n * `chunkSize` calls per skipped chunk (so the data stream position\n * stays correct), but no read/process/write happens and `commitCount`\n * is not incremented. The checkpoint value is loaded from the prior\n * FAILED step execution's ExecutionContext by `JobExecutor`.\n */\n resumeFromChunkIndex?: number;\n /**\n * Partition ordinal for this chunk execution. Set by the transport\n * (BullMQ / Kafka) when the step declares `partitions.count >= 2`;\n * `undefined` means \"non-partitioned\" (the 0.1.0 path). The executor\n * uses this together with `partitionCount` and the step's\n * `partitions.range` to bound the read loop to the partition's\n * `[from, to)` range — see `packages/core/src/partition-helpers.ts`\n * and `docs/RELEASE-0.2.0.md §6`.\n */\n partitionIndex?: number;\n /**\n * Mirror of the step's `partitions.count`. Carried on the context\n * (rather than read from the step) so the executor does not have to\n * reason about which step is being executed when it consults the\n * partition range.\n */\n partitionCount?: number;\n}\n\nexport interface ChunkExecutionResult {\n status: StepStatus;\n exitCode: string;\n exitMessage: string;\n readCount: number;\n writeCount: number;\n skipCount: number;\n commitCount: number;\n}\n\n/** Phase tag used by skip/retry policies. Mirrors the union in policy modules. */\ntype Phase = 'read' | 'process' | 'write';\n\n/**\n * Outcome of a single per-phase attempt after skip/retry consultation.\n * - `ok` — the operation succeeded; `value` is the op's return value.\n * - `skipped` — the error was skippable (and within budget); the op was\n * abandoned, the on-skip listener was invoked, and `value` is\n * `undefined`.\n */\ntype PhaseResult<T> = { kind: 'ok'; value: T } | { kind: 'skipped' };\n\n/**\n * Options for `runPhase`. `getSkipCount` is a closure over the executor's\n * live `skipCount` accumulator so the policy's budget check is consistent\n * with the accounting in the outer loop.\n */\ninterface RunPhaseOptions {\n phase: Phase;\n item: unknown;\n skipPolicy: SkipPolicy | null;\n retryPolicy: RetryPolicy | null;\n skipLimit: number;\n retryLimit: number;\n /** Live read of the executor's skipCount accumulator. */\n getSkipCount: () => number;\n /** Invoked when an error is actually skipped (within budget). The caller\n * is responsible for incrementing its own skipCount here. */\n onSkip: (err: unknown) => Promise<void>;\n}\n\nfunction isItemStream(value: unknown): value is ItemStream {\n if (value === null || typeof value !== 'object') return false;\n const stream = value as Partial<ItemStream>;\n return (\n typeof stream.open === 'function' &&\n typeof stream.update === 'function' &&\n typeof stream.close === 'function'\n );\n}\n\nfunction withLastChunkIndex(ctx: ExecutionContext, chunkIndex: number): ExecutionContext {\n const current = ctx.data;\n const data =\n current !== null && typeof current === 'object' && !Array.isArray(current)\n ? { ...(current as Record<string, unknown>), lastChunkIndex: chunkIndex }\n : { lastChunkIndex: chunkIndex };\n\n return {\n data: data as ExecutionContext['data'],\n version: ctx.version,\n };\n}\n\n@Injectable()\nexport class ChunkStepExecutor {\n async execute(\n step: ChunkStepDefinition,\n context: ChunkExecutionContext,\n ): Promise<ChunkExecutionResult> {\n const skipPolicy = step.skipPolicy ? compileSkipPolicy(step.skipPolicy) : null;\n const retryPolicy = step.retryPolicy ? compileRetryPolicy(step.retryPolicy) : null;\n const skipResolvers: ResolverMap = context.skipListenerResolvers ?? new Map();\n\n const skipLimit = step.skipPolicy?.limit ?? 0;\n const retryLimit = step.retryPolicy?.limit ?? 0;\n\n // Partition routing (T8): when the transport attached a\n // `partitionIndex` / `partitionCount` pair to this execution, the\n // step's `partitions.range` (or, when absent, the runtime\n // \"read until EOF\" default) bounds the chunk loop. We resolve the\n // range once up-front so the hot loop does not re-invoke the\n // user's resolver.\n //\n // The resolve helper throws when the index is out of range; we\n // surface the violation as a FAILED step (caught by the outer\n // try/catch) rather than a propagated throw, matching the\n // chunk executor's \"errors become FAILED step\" contract.\n let partition: { from: number; to: number } | null = null;\n if (context.partitionIndex !== undefined && context.partitionCount !== undefined) {\n // Defensive: enforce the index range here too. The runtime\n // service validates at enqueue time, but the chunk executor is\n // also a public surface (tests drive it directly), so a\n // double-check costs almost nothing and pins the contract.\n enforcePartitionIndex(context.partitionIndex, context.partitionCount);\n if (step.partitions?.range === undefined) {\n // No `range` resolver: each partition reads from the start of\n // the input. This is the documented \"default\" behaviour in\n // `docs/RELEASE-0.2.0.md §6.1` — the host's reader is\n // responsible for not over-processing. We still cap the read\n // loop to the partition's \"share\" via a sentinel that the\n // hot loop checks.\n partition = null;\n } else {\n const range = step.partitions.range(context.partitionIndex, context.partitionCount);\n const [from, to] = range;\n if (from < 0 || to < from) {\n throw new Error(\n `[ChunkStepExecutor] partitions.range(${context.partitionIndex}, ` +\n `${context.partitionCount}) returned invalid [${from}, ${to})`,\n );\n }\n partition = { from, to };\n }\n }\n\n let readCount = 0;\n let writeCount = 0;\n let skipCount = 0;\n let commitCount = 0;\n // 0-based index of the chunk currently being assembled. Used by the\n // restart path to decide which chunks to skip and where to record\n // the last-committed checkpoint.\n let chunkIndex = 0;\n let openedStreams: ItemStream[] = [];\n let streamContext: ExecutionContext | null = null;\n\n try {\n // Resolve inside the try block so a missing provider-token ref\n // surfaces as FAILED/{exitMessage: <err>}, matching the tasklet\n // executor's contract — not as a propagated throw.\n const reader = this.resolveReader(step.reader, context);\n const processor = step.processor ? this.resolveProcessor(step.processor, context) : null;\n const writer = this.resolveWriter(step.writer, context);\n\n const streams: ItemStream[] = [];\n if (isItemStream(reader)) streams.push(reader);\n if (isItemStream(writer)) streams.push(writer);\n streamContext = await context.jobRepository.getExecutionContext({\n stepExecutionId: context.stepExecutionId,\n });\n for (const stream of streams) {\n await stream.open(streamContext);\n openedStreams.push(stream);\n }\n\n // Outer loop: keep reading chunks until reader returns null\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // ---- SKIP PHASE (restart-only): drain up to chunkSize items so\n // the reader advances past already-committed chunks, but skip\n // the read/process/write pipeline entirely. commitCount is not\n // incremented (those chunks were already counted in the prior\n // run) and no checkpoint is written (the prior run owns it).\n if (\n context.resumeFromChunkIndex !== undefined &&\n chunkIndex <= context.resumeFromChunkIndex\n ) {\n let drained = 0;\n // Cap the restart skip-drain by the partition's remaining\n // budget too (T8). A restart on a partitioned step is rare\n // but possible — without the cap, the skip-drain could\n // pull items the partition's range is not allowed to\n // touch, leaving the chunk pipeline reading the wrong\n // slice on the next iteration.\n const skipCap =\n partition !== null\n ? Math.max(0, partition.to - partition.from - readCount)\n : step.chunkSize;\n if (skipCap === 0) break;\n for (let i = 0; i < skipCap; i++) {\n const item = await reader.read();\n if (item == null) break;\n drained += 1;\n }\n if (drained === 0) break; // EOF reached while skipping\n chunkIndex += 1;\n continue;\n }\n\n const items: unknown[] = [];\n let eof = false;\n\n // ---- PARTITION CAP (T8) ----\n // When the transport attached a partition's [from, to) range,\n // bound this chunk's read loop so the partition never\n // overshoots. `readCount` is the running count of items the\n // partition has read so far; `partition.to - partition.from`\n // is the partition's total budget. The cap is `min(chunkSize,\n // remaining)` and `0` is a valid \"no more items\" cap (we\n // exit the outer loop on the next iteration).\n const remaining =\n partition !== null ? Math.max(0, partition.to - partition.from - readCount) : step.chunkSize;\n if (remaining === 0) {\n // Partition is fully drained. Exit the outer loop so the\n // chunk executor returns COMPLETED with the partition's\n // commitCount + readCount + writeCount.\n break;\n }\n\n // ---- READ PHASE: per-item retry+skip ----\n for (let i = 0; i < remaining && !eof; i++) {\n const r = await this.runPhase<unknown>(() => reader.read(), {\n phase: 'read',\n item: null,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipRead(skipResolvers, err, null);\n },\n });\n if (r.kind === 'skipped') continue;\n if (r.value == null) {\n // Natural EOF from the reader\n eof = true;\n break;\n }\n items.push(r.value);\n readCount += 1;\n }\n if (items.length === 0) break; // EOF (either before first read or after skips)\n\n // ---- PROCESS PHASE: per-item retry+skip ----\n const processed: unknown[] = [];\n for (const item of items) {\n if (!processor) {\n processed.push(item);\n continue;\n }\n const r = await this.runPhase<unknown>(() => processor.process(item), {\n phase: 'process',\n item,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipProcess(skipResolvers, item, err);\n },\n });\n if (r.kind === 'skipped') continue;\n if (r.value !== null && r.value !== undefined) {\n processed.push(r.value);\n } else {\n // Processor filtered the item out (returned null/undefined) — not a\n // skip-policy skip, but counts as a skip for accounting parity.\n skipCount += 1;\n }\n }\n\n // ---- WRITE PHASE: per-chunk retry+skip, wrapped in transaction ----\n if (processed.length > 0) {\n const r = await this.runPhase<{ written: number; skipped: number } | void>(\n () =>\n context.transactionManager.withTransaction(async () => {\n return writer.write(processed);\n }),\n {\n phase: 'write',\n item: processed,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipWrite(skipResolvers, processed, err);\n },\n },\n );\n if (r.kind === 'ok') {\n if (r.value) {\n writeCount += r.value.written;\n skipCount += r.value.skipped;\n } else {\n writeCount += processed.length;\n }\n }\n // If the write was skipped, writeCount is not incremented and the\n // chunk is still considered \"committed\" (commitCount++) so that\n // progress tracking reflects that we moved past it.\n }\n\n // Persist the last-committed-chunk checkpoint in the step's\n // ExecutionContext. The next restart will read this value and\n // skip every chunk with index ≤ `lastChunkIndex`. The save is\n // intentionally outside the per-chunk transaction: the\n // checkpoint reflects \"we successfully moved past this chunk\",\n // and we only reach this line after the write completed\n // (either OK or skipped — both are forward progress).\n streamContext = withLastChunkIndex(streamContext, chunkIndex);\n streamContext = await this.updateStreams(openedStreams, streamContext);\n await context.jobRepository.saveExecutionContext(\n { stepExecutionId: context.stepExecutionId },\n streamContext,\n );\n streamContext = await context.jobRepository.getExecutionContext({\n stepExecutionId: context.stepExecutionId,\n });\n\n commitCount += 1;\n chunkIndex += 1;\n }\n\n const streamsToClose = openedStreams;\n openedStreams = [];\n await this.closeStreams(streamsToClose);\n\n return {\n status: StepStatus.COMPLETED,\n exitCode: 'COMPLETED',\n exitMessage: '',\n readCount,\n writeCount,\n skipCount,\n commitCount,\n };\n } catch (err) {\n let finalError = err;\n const streamsToClose = openedStreams;\n openedStreams = [];\n try {\n await this.closeStreams(streamsToClose);\n } catch (closeErr) {\n finalError = closeErr;\n }\n return {\n status: StepStatus.FAILED,\n exitCode: 'FAILED',\n exitMessage: finalError instanceof Error ? finalError.message : String(finalError),\n readCount,\n writeCount,\n skipCount,\n commitCount,\n };\n }\n }\n\n private async updateStreams(\n streams: readonly ItemStream[],\n ctx: ExecutionContext,\n ): Promise<ExecutionContext> {\n let next = ctx;\n for (const stream of streams) {\n const updated = await stream.update(next);\n if (updated !== undefined) {\n next = updated;\n }\n }\n return next;\n }\n\n private async closeStreams(streams: readonly ItemStream[]): Promise<void> {\n for (let i = streams.length - 1; i >= 0; i--) {\n await streams[i]!.close();\n }\n }\n\n /**\n * Run a single per-phase operation (one read, one process, or one write) with\n * skip/retry policy consultation. Throws when the operation cannot be\n * skipped (either non-skippable error or skip-limit exceeded) or when\n * retries are exhausted.\n *\n * The contract on budget accounting:\n * - The caller's `skipCount` accumulator is consulted *before* a skip is\n * honored. If the post-skip count would exceed the limit, the error\n * becomes a `SkipLimitExceededError` instead of a skip.\n * - The caller's `onSkip` callback is responsible for incrementing the\n * skipCount; it runs only when a skip is actually honored.\n */\n private async runPhase<T>(\n op: () => Promise<T>,\n options: RunPhaseOptions,\n ): Promise<PhaseResult<T>> {\n let attempt = 1;\n // Outer safety cap: when a retry policy exists, allow many iterations;\n // the policy's `canRetry` is the actual gate. When no retry policy,\n // exactly one attempt.\n const outerCap = options.retryPolicy ? 999 : 1;\n\n while (attempt <= outerCap) {\n try {\n const value = await op();\n return { kind: 'ok', value };\n } catch (err) {\n // 1) Skip consultation: is this error skippable, and is there budget?\n if (options.skipPolicy) {\n // Use the policy's `shouldSkip` with `skipCount: 0` to get a pure\n // membership check (the policy's own budget gate is bypassed).\n // We apply the budget ourselves with the caller's live accounting.\n const membership: SkipContext = {\n item: options.item,\n phase: options.phase,\n skipCount: 0,\n skipLimit: options.skipLimit,\n };\n if (options.skipPolicy.shouldSkip(err, membership)) {\n // Error is in the skippable list. Now check the budget: would\n // honoring this skip exceed the limit?\n const projected = options.getSkipCount() + 1;\n if (projected > options.skipLimit) {\n throw new SkipLimitExceededError(options.skipLimit);\n }\n await options.onSkip(err);\n return { kind: 'skipped' };\n }\n // Not in the skippable list — fall through to retry/throw.\n }\n\n // 2) Retry consultation\n if (options.retryPolicy) {\n const retryCtx: RetryContext = {\n item: options.item,\n phase: options.phase,\n attempt,\n retryLimit: options.retryLimit,\n };\n if (options.retryPolicy.canRetry(err, retryCtx)) {\n const ms = options.retryPolicy.backoffMs(attempt);\n if (ms > 0) await new Promise((r) => setTimeout(r, ms));\n attempt += 1;\n continue;\n }\n // canRetry returned false. Distinguish \"exhausted\" from \"not\n // retryable\" by re-checking membership with an effectively\n // infinite budget.\n if (attempt > options.retryLimit) {\n const isRetryable = options.retryPolicy.canRetry(err, {\n item: options.item,\n phase: options.phase,\n attempt: 1,\n retryLimit: Number.POSITIVE_INFINITY,\n });\n if (isRetryable) {\n throw new RetryLimitExceededError(options.retryLimit);\n }\n }\n }\n\n // 3) Neither skippable nor retryable: re-throw the original error.\n throw err;\n }\n }\n\n // Defensive: the outer cap should never be reached when a retry policy\n // gates us, but if no retry policy exists and the very first attempt\n // somehow re-entered the loop, fall through with a clear failure.\n throw new Error(\n `ChunkStepExecutor: phase \"${options.phase}\" exhausted attempts without a retry policy`,\n );\n }\n\n private resolveReader(ref: ReaderRef, context: ChunkExecutionContext): ItemReader {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { read: result as ItemReader['read'] };\n }\n if (result !== null && typeof result === 'object' && typeof (result as ItemReader).read === 'function') {\n return result as ItemReader;\n }\n return { read: ref.fn as ItemReader['read'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemReader>('reader', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::reader::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Reader not resolved: ${ref.classToken}.${ref.methodName}`);\n return { read: fn as ItemReader['read'] };\n }\n throw new Error(`Unsupported reader ref kind: ${ref.kind}`);\n }\n\n private resolveProcessor(ref: ProcessorRef, context: ChunkExecutionContext): ItemProcessor {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { process: result as ItemProcessor['process'] };\n }\n if (result !== null && typeof result === 'object' && typeof (result as ItemProcessor).process === 'function') {\n return result as ItemProcessor;\n }\n return { process: ref.fn as ItemProcessor['process'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemProcessor>('processor', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::processor::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Processor not resolved: ${ref.classToken}.${ref.methodName}`);\n return { process: fn as ItemProcessor['process'] };\n }\n throw new Error(`Unsupported processor ref kind: ${ref.kind}`);\n }\n\n private resolveWriter(ref: WriterRef, context: ChunkExecutionContext): ItemWriter {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { write: result as ItemWriter['write'] };\n }\n if (result !== null && typeof result === 'object' && typeof (result as ItemWriter).write === 'function') {\n return result as ItemWriter;\n }\n return { write: ref.fn as ItemWriter['write'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemWriter>('writer', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::writer::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Writer not resolved: ${ref.classToken}.${ref.methodName}`);\n return { write: fn as ItemWriter['write'] };\n }\n throw new Error(`Unsupported writer ref kind: ${ref.kind}`);\n }\n}\n"],"names":["ChunkStepExecutor","isItemStream","value","stream","open","update","close","withLastChunkIndex","ctx","chunkIndex","current","data","Array","isArray","lastChunkIndex","version","execute","step","context","skipPolicy","compileSkipPolicy","retryPolicy","compileRetryPolicy","skipResolvers","skipListenerResolvers","Map","skipLimit","limit","retryLimit","partition","partitionIndex","undefined","partitionCount","enforcePartitionIndex","partitions","range","from","to","Error","readCount","writeCount","skipCount","commitCount","openedStreams","streamContext","reader","resolveReader","processor","resolveProcessor","writer","resolveWriter","streams","push","jobRepository","getExecutionContext","stepExecutionId","resumeFromChunkIndex","drained","skipCap","Math","max","chunkSize","i","item","read","items","eof","remaining","r","runPhase","phase","getSkipCount","onSkip","err","listenerInvoker","invokeOnSkipRead","kind","length","processed","process","invokeOnSkipProcess","transactionManager","withTransaction","write","invokeOnSkipWrite","written","skipped","updateStreams","saveExecutionContext","streamsToClose","closeStreams","status","StepStatus","COMPLETED","exitCode","exitMessage","finalError","closeErr","FAILED","message","String","next","updated","op","options","attempt","outerCap","membership","shouldSkip","projected","SkipLimitExceededError","retryCtx","canRetry","ms","backoffMs","Promise","setTimeout","isRetryable","Number","POSITIVE_INFINITY","RetryLimitExceededError","ref","RefKind","BuilderLambda","fn","result","ProviderToken","resolveProviderToken","providerResolvers","Method","classToken","methodName","key","jobExecutionId2","resolvers","get"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/execution/chunk-step-executor.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport type { ChunkStepDefinition, ReaderRef, ProcessorRef, WriterRef } from '../core/ir';\nimport { RefKind } from '../core/ir';\nimport type {\n ItemExecutionContext,\n ItemReader,\n ItemProcessor,\n ItemStream,\n ItemWriter,\n} from '../core/item';\nimport type { ExecutionContext, JobParameters, JobRepository } from '../core/repository';\nimport type { TransactionManager } from '../core/transaction';\nimport { StepStatus } from '../core/status';\nimport type { SkipPolicy, SkipContext } from '../policies/skip-policy';\nimport type { RetryPolicy, RetryContext } from '../policies/retry-policy';\nimport { compileSkipPolicy } from '../policies/skip-policy';\nimport { compileRetryPolicy } from '../policies/retry-policy';\nimport { SkipLimitExceededError, RetryLimitExceededError } from '../core/errors';\nimport { enforcePartitionIndex } from '../partition-helpers';\nimport type { ResolverMap } from './listener-invoker';\nimport { ListenerInvoker } from './listener-invoker';\nimport { resolveProviderToken, type ProviderResolvers } from './ref-resolver';\n\nexport interface ChunkExecutionContext {\n jobExecutionId: string;\n /** Step execution id, used to scope the chunk-progress checkpoint in the\n * step's ExecutionContext (saved as `{ lastChunkIndex }`). */\n stepExecutionId: string;\n stepName?: string;\n jobParameters?: JobParameters;\n jobRepository: JobRepository;\n transactionManager: TransactionManager;\n listenerInvoker: ListenerInvoker;\n /** Full listener resolver map keyed by `${phase}:${kind}:${name}`. */\n listenerResolvers?: ResolverMap;\n /** Map of resolved reader/processor/writer functions by name. */\n resolvers: Map<string, (...args: unknown[]) => unknown | Promise<unknown>>;\n jobExecutionId2: string; // unique key for listener resolver namespacing\n /** Optional skip listener resolver map. Keys follow the `on-skip:<kind>:<name>` shape. */\n skipListenerResolvers?: ResolverMap;\n /**\n * Optional map of provider-token id → already-resolved provider instance.\n * Populated by the JobExecutor (or test fixtures) so that\n * `RefKind.ProviderToken` refs on the reader/processor/writer slots can\n * be looked up without coupling the executor to the Nest DI container.\n */\n providerResolvers?: ProviderResolvers;\n /**\n * When set, the executor skips any chunk whose 0-based index is less\n * than or equal to this value. The reader is still advanced by\n * `chunkSize` calls per skipped chunk (so the data stream position\n * stays correct), but no read/process/write happens and `commitCount`\n * is not incremented. The checkpoint value is loaded from the prior\n * FAILED step execution's ExecutionContext by `JobExecutor`.\n */\n resumeFromChunkIndex?: number;\n /**\n * Partition ordinal for this chunk execution. Set by the transport\n * (BullMQ / Kafka) when the step declares `partitions.count >= 2`;\n * `undefined` means \"non-partitioned\" (the 0.1.0 path). The executor\n * uses this together with `partitionCount` and the step's\n * `partitions.range` to bound the read loop to the partition's\n * `[from, to)` range — see `packages/core/src/partition-helpers.ts`\n * and `docs/RELEASE-0.2.0.md §6`.\n */\n partitionIndex?: number;\n /**\n * Mirror of the step's `partitions.count`. Carried on the context\n * (rather than read from the step) so the executor does not have to\n * reason about which step is being executed when it consults the\n * partition range.\n */\n partitionCount?: number;\n}\n\nexport interface ChunkExecutionResult {\n status: StepStatus;\n exitCode: string;\n exitMessage: string;\n readCount: number;\n writeCount: number;\n skipCount: number;\n commitCount: number;\n}\n\n/** Phase tag used by skip/retry policies. Mirrors the union in policy modules. */\ntype Phase = 'read' | 'process' | 'write';\n\n/**\n * Outcome of a single per-phase attempt after skip/retry consultation.\n * - `ok` — the operation succeeded; `value` is the op's return value.\n * - `skipped` — the error was skippable (and within budget); the op was\n * abandoned, the on-skip listener was invoked, and `value` is\n * `undefined`.\n */\ntype PhaseResult<T> = { kind: 'ok'; value: T } | { kind: 'skipped' };\n\n/**\n * Options for `runPhase`. `getSkipCount` is a closure over the executor's\n * live `skipCount` accumulator so the policy's budget check is consistent\n * with the accounting in the outer loop.\n */\ninterface RunPhaseOptions<T = unknown> {\n phase: Phase;\n item: unknown;\n skipPolicy: SkipPolicy | null;\n retryPolicy: RetryPolicy | null;\n skipLimit: number;\n retryLimit: number;\n /** Live read of the executor's skipCount accumulator. */\n getSkipCount: () => number;\n /** Invoked when an error is actually skipped (within budget). The caller\n * is responsible for incrementing its own skipCount here. */\n onSkip: (err: unknown) => Promise<void>;\n before?: () => Promise<void>;\n after?: (value: T) => Promise<void>;\n onError?: (err: unknown) => Promise<void>;\n}\n\nfunction isItemStream(value: unknown): value is ItemStream {\n if (value === null || typeof value !== 'object') return false;\n const stream = value as Partial<ItemStream>;\n return (\n typeof stream.open === 'function' &&\n typeof stream.update === 'function' &&\n typeof stream.close === 'function'\n );\n}\n\nfunction withLastChunkIndex(ctx: ExecutionContext, chunkIndex: number): ExecutionContext {\n const current = ctx.data;\n const data =\n current !== null && typeof current === 'object' && !Array.isArray(current)\n ? { ...(current as Record<string, unknown>), lastChunkIndex: chunkIndex }\n : { lastChunkIndex: chunkIndex };\n\n return {\n data: data as ExecutionContext['data'],\n version: ctx.version,\n };\n}\n\n@Injectable()\nexport class ChunkStepExecutor {\n async execute(\n step: ChunkStepDefinition,\n context: ChunkExecutionContext,\n ): Promise<ChunkExecutionResult> {\n const skipPolicy = step.skipPolicy ? compileSkipPolicy(step.skipPolicy) : null;\n const retryPolicy = step.retryPolicy ? compileRetryPolicy(step.retryPolicy) : null;\n const listenerResolvers: ResolverMap = context.listenerResolvers ?? new Map();\n const skipResolvers: ResolverMap = context.skipListenerResolvers ?? listenerResolvers;\n\n const skipLimit = step.skipPolicy?.limit ?? 0;\n const retryLimit = step.retryPolicy?.limit ?? 0;\n\n // Partition routing (T8): when the transport attached a\n // `partitionIndex` / `partitionCount` pair to this execution, the\n // step's `partitions.range` (or, when absent, the runtime\n // \"read until EOF\" default) bounds the chunk loop. We resolve the\n // range once up-front so the hot loop does not re-invoke the\n // user's resolver.\n //\n // The resolve helper throws when the index is out of range; we\n // surface the violation as a FAILED step (caught by the outer\n // try/catch) rather than a propagated throw, matching the\n // chunk executor's \"errors become FAILED step\" contract.\n let partition: { from: number; to: number } | null = null;\n if (context.partitionIndex !== undefined && context.partitionCount !== undefined) {\n // Defensive: enforce the index range here too. The runtime\n // service validates at enqueue time, but the chunk executor is\n // also a public surface (tests drive it directly), so a\n // double-check costs almost nothing and pins the contract.\n enforcePartitionIndex(context.partitionIndex, context.partitionCount);\n if (step.partitions?.range === undefined) {\n // No `range` resolver: each partition reads from the start of\n // the input. This is the documented \"default\" behaviour in\n // `docs/RELEASE-0.2.0.md §6.1` — the host's reader is\n // responsible for not over-processing. We still cap the read\n // loop to the partition's \"share\" via a sentinel that the\n // hot loop checks.\n partition = null;\n } else {\n const range = step.partitions.range(context.partitionIndex, context.partitionCount);\n const [from, to] = range;\n if (from < 0 || to < from) {\n throw new Error(\n `[ChunkStepExecutor] partitions.range(${context.partitionIndex}, ` +\n `${context.partitionCount}) returned invalid [${from}, ${to})`,\n );\n }\n partition = { from, to };\n }\n }\n\n let readCount = 0;\n let writeCount = 0;\n let skipCount = 0;\n let commitCount = 0;\n // 0-based index of the chunk currently being assembled. Used by the\n // restart path to decide which chunks to skip and where to record\n // the last-committed checkpoint.\n let chunkIndex = 0;\n let openedStreams: ItemStream[] = [];\n let streamContext: ExecutionContext | null = null;\n\n await context.listenerInvoker.invokeBefore(\n listenerResolvers,\n 'step',\n this.buildListenerContext(step, context),\n );\n\n try {\n // Resolve inside the try block so a missing provider-token ref\n // surfaces as FAILED/{exitMessage: <err>}, matching the tasklet\n // executor's contract — not as a propagated throw.\n const reader = this.resolveReader(step.reader, context);\n const processor = step.processor ? this.resolveProcessor(step.processor, context) : null;\n const writer = this.resolveWriter(step.writer, context);\n\n const streams: ItemStream[] = [];\n if (isItemStream(reader)) streams.push(reader);\n if (isItemStream(writer)) streams.push(writer);\n streamContext = await context.jobRepository.getExecutionContext({\n stepExecutionId: context.stepExecutionId,\n });\n for (const stream of streams) {\n await stream.open(streamContext);\n openedStreams.push(stream);\n }\n\n // Outer loop: keep reading chunks until reader returns null\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // ---- SKIP PHASE (restart-only): drain up to chunkSize items so\n // the reader advances past already-committed chunks, but skip\n // the read/process/write pipeline entirely. commitCount is not\n // incremented (those chunks were already counted in the prior\n // run) and no checkpoint is written (the prior run owns it).\n if (\n context.resumeFromChunkIndex !== undefined &&\n chunkIndex <= context.resumeFromChunkIndex\n ) {\n let drained = 0;\n // Cap the restart skip-drain by the partition's remaining\n // budget too (T8). A restart on a partitioned step is rare\n // but possible — without the cap, the skip-drain could\n // pull items the partition's range is not allowed to\n // touch, leaving the chunk pipeline reading the wrong\n // slice on the next iteration.\n const skipCap =\n partition !== null\n ? Math.max(0, partition.to - partition.from - readCount)\n : step.chunkSize;\n if (skipCap === 0) break;\n const itemContext = this.buildItemContext(step, context, chunkIndex);\n for (let i = 0; i < skipCap; i++) {\n const item = await reader.read(itemContext);\n if (item == null) break;\n drained += 1;\n }\n if (drained === 0) break; // EOF reached while skipping\n chunkIndex += 1;\n continue;\n }\n\n const items: unknown[] = [];\n let eof = false;\n\n // ---- PARTITION CAP (T8) ----\n // When the transport attached a partition's [from, to) range,\n // bound this chunk's read loop so the partition never\n // overshoots. `readCount` is the running count of items the\n // partition has read so far; `partition.to - partition.from`\n // is the partition's total budget. The cap is `min(chunkSize,\n // remaining)` and `0` is a valid \"no more items\" cap (we\n // exit the outer loop on the next iteration).\n const remaining =\n partition !== null\n ? Math.max(0, partition.to - partition.from - readCount)\n : step.chunkSize;\n if (remaining === 0) {\n // Partition is fully drained. Exit the outer loop so the\n // chunk executor returns COMPLETED with the partition's\n // commitCount + readCount + writeCount.\n break;\n }\n\n const itemContext = this.buildItemContext(step, context, chunkIndex);\n const listenerContext = this.buildListenerContext(step, context, { chunkIndex });\n\n await context.listenerInvoker.invokeBefore(listenerResolvers, 'chunk', listenerContext);\n\n // ---- READ PHASE: per-item retry+skip ----\n for (let i = 0; i < remaining && !eof; i++) {\n const r = await this.runPhase<unknown>(() => reader.read(itemContext), {\n phase: 'read',\n item: null,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipRead(skipResolvers, err, null);\n },\n before: async () => {\n await context.listenerInvoker.invokeBeforeRead(listenerResolvers, listenerContext);\n },\n after: async (item) => {\n if (item !== null && item !== undefined) {\n await context.listenerInvoker.invokeAfterRead(\n listenerResolvers,\n item,\n listenerContext,\n );\n }\n },\n onError: async (err) => {\n await context.listenerInvoker.invokeOnReadError(\n listenerResolvers,\n err,\n listenerContext,\n );\n },\n });\n if (r.kind === 'skipped') continue;\n if (r.value == null) {\n // Natural EOF from the reader\n eof = true;\n break;\n }\n items.push(r.value);\n readCount += 1;\n }\n if (items.length === 0) {\n await context.listenerInvoker.invokeAfter(listenerResolvers, 'chunk', listenerContext, {\n status: StepStatus.COMPLETED,\n readCount,\n writeCount,\n skipCount,\n commitCount,\n });\n break; // EOF (either before first read or after skips)\n }\n\n // ---- PROCESS PHASE: per-item retry+skip ----\n const processed: unknown[] = [];\n for (const item of items) {\n if (!processor) {\n processed.push(item);\n continue;\n }\n const r = await this.runPhase<unknown>(() => processor.process(item, itemContext), {\n phase: 'process',\n item,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipProcess(skipResolvers, item, err);\n },\n before: async () => {\n await context.listenerInvoker.invokeBeforeProcess(\n listenerResolvers,\n item,\n listenerContext,\n );\n },\n after: async (value) => {\n await context.listenerInvoker.invokeAfterProcess(\n listenerResolvers,\n item,\n value,\n listenerContext,\n );\n },\n onError: async (err) => {\n await context.listenerInvoker.invokeOnProcessError(\n listenerResolvers,\n item,\n err,\n listenerContext,\n );\n },\n });\n if (r.kind === 'skipped') continue;\n if (r.value !== null && r.value !== undefined) {\n processed.push(r.value);\n } else {\n // Processor filtered the item out (returned null/undefined) — not a\n // skip-policy skip, but counts as a skip for accounting parity.\n skipCount += 1;\n }\n }\n\n // ---- WRITE PHASE: per-chunk retry+skip, wrapped in transaction ----\n if (processed.length > 0) {\n const r = await this.runPhase<{ written: number; skipped: number } | void>(\n () =>\n context.transactionManager.withTransaction(async () => {\n return writer.write(processed, itemContext);\n }),\n {\n phase: 'write',\n item: processed,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipWrite(skipResolvers, processed, err);\n },\n before: async () => {\n await context.listenerInvoker.invokeBeforeWrite(\n listenerResolvers,\n processed,\n listenerContext,\n );\n },\n after: async (value) => {\n await context.listenerInvoker.invokeAfterWrite(\n listenerResolvers,\n processed,\n value,\n listenerContext,\n );\n },\n onError: async (err) => {\n await context.listenerInvoker.invokeOnWriteError(\n listenerResolvers,\n processed,\n err,\n listenerContext,\n );\n },\n },\n );\n if (r.kind === 'ok') {\n if (r.value) {\n writeCount += r.value.written;\n skipCount += r.value.skipped;\n } else {\n writeCount += processed.length;\n }\n }\n // If the write was skipped, writeCount is not incremented and the\n // chunk is still considered \"committed\" (commitCount++) so that\n // progress tracking reflects that we moved past it.\n }\n\n // Persist the last-committed-chunk checkpoint in the step's\n // ExecutionContext. The next restart will read this value and\n // skip every chunk with index ≤ `lastChunkIndex`. The save is\n // intentionally outside the per-chunk transaction: the\n // checkpoint reflects \"we successfully moved past this chunk\",\n // and we only reach this line after the write completed\n // (either OK or skipped — both are forward progress).\n streamContext = withLastChunkIndex(streamContext, chunkIndex);\n streamContext = await this.updateStreams(openedStreams, streamContext);\n await context.jobRepository.saveExecutionContext(\n { stepExecutionId: context.stepExecutionId },\n streamContext,\n );\n streamContext = await context.jobRepository.getExecutionContext({\n stepExecutionId: context.stepExecutionId,\n });\n\n await context.listenerInvoker.invokeAfter(listenerResolvers, 'chunk', listenerContext, {\n status: StepStatus.COMPLETED,\n readCount,\n writeCount,\n skipCount,\n commitCount: commitCount + 1,\n });\n\n commitCount += 1;\n chunkIndex += 1;\n }\n\n const streamsToClose = openedStreams;\n openedStreams = [];\n await this.closeStreams(streamsToClose);\n\n const result: ChunkExecutionResult = {\n status: StepStatus.COMPLETED,\n exitCode: 'COMPLETED',\n exitMessage: '',\n readCount,\n writeCount,\n skipCount,\n commitCount,\n };\n await context.listenerInvoker.invokeAfter(\n listenerResolvers,\n 'step',\n this.buildListenerContext(step, context),\n result,\n );\n return result;\n } catch (err) {\n let finalError = err;\n const streamsToClose = openedStreams;\n openedStreams = [];\n try {\n await this.closeStreams(streamsToClose);\n } catch (closeErr) {\n finalError = closeErr;\n }\n await context.listenerInvoker.invokeOnError(\n listenerResolvers,\n 'chunk',\n this.buildListenerContext(step, context, { chunkIndex }),\n finalError,\n );\n const result: ChunkExecutionResult = {\n status: StepStatus.FAILED,\n exitCode: 'FAILED',\n exitMessage: finalError instanceof Error ? finalError.message : String(finalError),\n readCount,\n writeCount,\n skipCount,\n commitCount,\n };\n await context.listenerInvoker.invokeOnError(\n listenerResolvers,\n 'step',\n this.buildListenerContext(step, context),\n finalError,\n );\n await context.listenerInvoker.invokeAfter(\n listenerResolvers,\n 'step',\n this.buildListenerContext(step, context),\n result,\n );\n return result;\n }\n }\n\n private buildListenerContext(\n step: ChunkStepDefinition,\n context: ChunkExecutionContext,\n extra: Record<string, unknown> = {},\n ): {\n jobExecutionId: string;\n stepExecutionId: string;\n stepName: string;\n jobParameters: JobParameters;\n [extra: string]: unknown;\n } {\n return {\n jobExecutionId: context.jobExecutionId,\n stepExecutionId: context.stepExecutionId,\n stepName: context.stepName ?? step.id,\n jobParameters: context.jobParameters ?? {},\n ...extra,\n };\n }\n\n private buildItemContext(\n step: ChunkStepDefinition,\n context: ChunkExecutionContext,\n chunkIndex: number,\n ): ItemExecutionContext {\n return {\n jobExecutionId: context.jobExecutionId,\n stepExecutionId: context.stepExecutionId,\n stepName: context.stepName ?? step.id,\n jobParameters: context.jobParameters ?? {},\n chunkIndex,\n getExecutionContext: async () =>\n context.jobRepository.getExecutionContext({ stepExecutionId: context.stepExecutionId }),\n saveExecutionContext: async (ctx: ExecutionContext) => {\n await context.jobRepository.saveExecutionContext(\n { stepExecutionId: context.stepExecutionId },\n ctx,\n );\n },\n };\n }\n\n private async updateStreams(\n streams: readonly ItemStream[],\n ctx: ExecutionContext,\n ): Promise<ExecutionContext> {\n let next = ctx;\n for (const stream of streams) {\n const updated = await stream.update(next);\n if (updated !== undefined) {\n next = updated;\n }\n }\n return next;\n }\n\n private async closeStreams(streams: readonly ItemStream[]): Promise<void> {\n for (let i = streams.length - 1; i >= 0; i--) {\n await streams[i]!.close();\n }\n }\n\n /**\n * Run a single per-phase operation (one read, one process, or one write) with\n * skip/retry policy consultation. Throws when the operation cannot be\n * skipped (either non-skippable error or skip-limit exceeded) or when\n * retries are exhausted.\n *\n * The contract on budget accounting:\n * - The caller's `skipCount` accumulator is consulted *before* a skip is\n * honored. If the post-skip count would exceed the limit, the error\n * becomes a `SkipLimitExceededError` instead of a skip.\n * - The caller's `onSkip` callback is responsible for incrementing the\n * skipCount; it runs only when a skip is actually honored.\n */\n private async runPhase<T>(\n op: () => Promise<T>,\n options: RunPhaseOptions<T>,\n ): Promise<PhaseResult<T>> {\n let attempt = 1;\n // Outer safety cap: when a retry policy exists, allow many iterations;\n // the policy's `canRetry` is the actual gate. When no retry policy,\n // exactly one attempt.\n const outerCap = options.retryPolicy ? 999 : 1;\n\n while (attempt <= outerCap) {\n if (options.before) await options.before();\n let value: T;\n try {\n value = await op();\n } catch (err) {\n if (options.onError) await options.onError(err);\n // 1) Skip consultation: is this error skippable, and is there budget?\n if (options.skipPolicy) {\n // Use the policy's `shouldSkip` with `skipCount: 0` to get a pure\n // membership check (the policy's own budget gate is bypassed).\n // We apply the budget ourselves with the caller's live accounting.\n const membership: SkipContext = {\n item: options.item,\n phase: options.phase,\n skipCount: 0,\n skipLimit: options.skipLimit,\n };\n if (options.skipPolicy.shouldSkip(err, membership)) {\n // Error is in the skippable list. Now check the budget: would\n // honoring this skip exceed the limit?\n const projected = options.getSkipCount() + 1;\n if (projected > options.skipLimit) {\n throw new SkipLimitExceededError(options.skipLimit);\n }\n await options.onSkip(err);\n return { kind: 'skipped' };\n }\n // Not in the skippable list — fall through to retry/throw.\n }\n\n // 2) Retry consultation\n if (options.retryPolicy) {\n const retryCtx: RetryContext = {\n item: options.item,\n phase: options.phase,\n attempt,\n retryLimit: options.retryLimit,\n };\n if (options.retryPolicy.canRetry(err, retryCtx)) {\n const ms = options.retryPolicy.backoffMs(attempt);\n if (ms > 0) await new Promise((r) => setTimeout(r, ms));\n attempt += 1;\n continue;\n }\n // canRetry returned false. Distinguish \"exhausted\" from \"not\n // retryable\" by re-checking membership with an effectively\n // infinite budget.\n if (attempt > options.retryLimit) {\n const isRetryable = options.retryPolicy.canRetry(err, {\n item: options.item,\n phase: options.phase,\n attempt: 1,\n retryLimit: Number.POSITIVE_INFINITY,\n });\n if (isRetryable) {\n throw new RetryLimitExceededError(options.retryLimit);\n }\n }\n }\n\n // 3) Neither skippable nor retryable: re-throw the original error.\n throw err;\n }\n if (options.after) await options.after(value);\n return { kind: 'ok', value };\n }\n\n // Defensive: the outer cap should never be reached when a retry policy\n // gates us, but if no retry policy exists and the very first attempt\n // somehow re-entered the loop, fall through with a clear failure.\n throw new Error(\n `ChunkStepExecutor: phase \"${options.phase}\" exhausted attempts without a retry policy`,\n );\n }\n\n private resolveReader(ref: ReaderRef, context: ChunkExecutionContext): ItemReader {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { read: result as ItemReader['read'] };\n }\n if (\n result !== null &&\n typeof result === 'object' &&\n typeof (result as ItemReader).read === 'function'\n ) {\n return result as ItemReader;\n }\n return { read: ref.fn as ItemReader['read'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemReader>('reader', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::reader::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Reader not resolved: ${ref.classToken}.${ref.methodName}`);\n return { read: fn as ItemReader['read'] };\n }\n throw new Error(`Unsupported reader ref kind: ${ref.kind}`);\n }\n\n private resolveProcessor(ref: ProcessorRef, context: ChunkExecutionContext): ItemProcessor {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { process: result as ItemProcessor['process'] };\n }\n if (\n result !== null &&\n typeof result === 'object' &&\n typeof (result as ItemProcessor).process === 'function'\n ) {\n return result as ItemProcessor;\n }\n return { process: ref.fn as ItemProcessor['process'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemProcessor>('processor', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::processor::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Processor not resolved: ${ref.classToken}.${ref.methodName}`);\n return { process: fn as ItemProcessor['process'] };\n }\n throw new Error(`Unsupported processor ref kind: ${ref.kind}`);\n }\n\n private resolveWriter(ref: WriterRef, context: ChunkExecutionContext): ItemWriter {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { write: result as ItemWriter['write'] };\n }\n if (\n result !== null &&\n typeof result === 'object' &&\n typeof (result as ItemWriter).write === 'function'\n ) {\n return result as ItemWriter;\n }\n return { write: ref.fn as ItemWriter['write'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemWriter>('writer', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::writer::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Writer not resolved: ${ref.classToken}.${ref.methodName}`);\n return { write: fn as ItemWriter['write'] };\n }\n throw new Error(`Unsupported writer ref kind: ${ref.kind}`);\n }\n}\n"],"names":["ChunkStepExecutor","isItemStream","value","stream","open","update","close","withLastChunkIndex","ctx","chunkIndex","current","data","Array","isArray","lastChunkIndex","version","execute","step","context","skipPolicy","compileSkipPolicy","retryPolicy","compileRetryPolicy","listenerResolvers","Map","skipResolvers","skipListenerResolvers","skipLimit","limit","retryLimit","partition","partitionIndex","undefined","partitionCount","enforcePartitionIndex","partitions","range","from","to","Error","readCount","writeCount","skipCount","commitCount","openedStreams","streamContext","listenerInvoker","invokeBefore","buildListenerContext","reader","resolveReader","processor","resolveProcessor","writer","resolveWriter","streams","push","jobRepository","getExecutionContext","stepExecutionId","resumeFromChunkIndex","drained","skipCap","Math","max","chunkSize","itemContext","buildItemContext","i","item","read","items","eof","remaining","listenerContext","r","runPhase","phase","getSkipCount","onSkip","err","invokeOnSkipRead","before","invokeBeforeRead","after","invokeAfterRead","onError","invokeOnReadError","kind","length","invokeAfter","status","StepStatus","COMPLETED","processed","process","invokeOnSkipProcess","invokeBeforeProcess","invokeAfterProcess","invokeOnProcessError","transactionManager","withTransaction","write","invokeOnSkipWrite","invokeBeforeWrite","invokeAfterWrite","invokeOnWriteError","written","skipped","updateStreams","saveExecutionContext","streamsToClose","closeStreams","result","exitCode","exitMessage","finalError","closeErr","invokeOnError","FAILED","message","String","extra","jobExecutionId","stepName","id","jobParameters","next","updated","op","options","attempt","outerCap","membership","shouldSkip","projected","SkipLimitExceededError","retryCtx","canRetry","ms","backoffMs","Promise","setTimeout","isRetryable","Number","POSITIVE_INFINITY","RetryLimitExceededError","ref","RefKind","BuilderLambda","fn","ProviderToken","resolveProviderToken","providerResolvers","Method","classToken","methodName","key","jobExecutionId2","resolvers","get"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common';
|
|
2
|
+
import { BatchScheduleRegistry } from '../module/batch-schedule-registry';
|
|
3
|
+
import { JobLauncher } from './job-launcher';
|
|
4
|
+
/**
|
|
5
|
+
* In-process scheduler for `@BatchScheduled` jobs.
|
|
6
|
+
*
|
|
7
|
+
* This is deliberately part of the in-process transport, not the
|
|
8
|
+
* `@BatchScheduled` decorator. The decorator remains metadata-only;
|
|
9
|
+
* this provider consumes the `BatchScheduleRegistry` and turns matching
|
|
10
|
+
* cron ticks into `JobLauncher.launch(...)` calls inside the same server
|
|
11
|
+
* process.
|
|
12
|
+
*/
|
|
13
|
+
export declare class InProcessSchedule implements OnApplicationBootstrap, OnApplicationShutdown {
|
|
14
|
+
private readonly scheduleRegistry;
|
|
15
|
+
private readonly launcher;
|
|
16
|
+
private readonly logger;
|
|
17
|
+
private readonly states;
|
|
18
|
+
private stopped;
|
|
19
|
+
constructor(scheduleRegistry: BatchScheduleRegistry, launcher: JobLauncher);
|
|
20
|
+
onApplicationBootstrap(): void;
|
|
21
|
+
onApplicationShutdown(): void;
|
|
22
|
+
private dispatch;
|
|
23
|
+
private launch;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=in-process-schedule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-process-schedule.d.ts","sourceRoot":"","sources":["../../../src/execution/in-process-schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAGnG,OAAO,EAA2B,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAc7C;;;;;;;;GAQG;AACH,qBACa,iBAAkB,YAAW,sBAAsB,EAAE,qBAAqB;IAMnF,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAN3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,OAAO,CAAQ;gBAGJ,gBAAgB,EAAE,qBAAqB,EACvC,QAAQ,EAAE,WAAW;IAGxC,sBAAsB,IAAI,IAAI;IAmD9B,qBAAqB,IAAI,IAAI;IAQ7B,OAAO,CAAC,QAAQ;YAyBF,MAAM;CAoBrB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "InProcessSchedule", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return InProcessSchedule;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _common = require("@nestjs/common");
|
|
12
|
+
const _cron = require("cron");
|
|
13
|
+
const _batchscheduleregistry = require("../module/batch-schedule-registry");
|
|
14
|
+
const _joblauncher = require("./job-launcher");
|
|
15
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
16
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
17
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
18
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
19
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
20
|
+
}
|
|
21
|
+
function _ts_metadata(k, v) {
|
|
22
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
23
|
+
}
|
|
24
|
+
function scheduleKey(entry) {
|
|
25
|
+
return `${entry.jobId}::${entry.scheduleName}`;
|
|
26
|
+
}
|
|
27
|
+
let InProcessSchedule = class InProcessSchedule {
|
|
28
|
+
scheduleRegistry;
|
|
29
|
+
launcher;
|
|
30
|
+
logger = new _common.Logger(InProcessSchedule.name);
|
|
31
|
+
states = new Map();
|
|
32
|
+
stopped = true;
|
|
33
|
+
constructor(scheduleRegistry, launcher){
|
|
34
|
+
this.scheduleRegistry = scheduleRegistry;
|
|
35
|
+
this.launcher = launcher;
|
|
36
|
+
}
|
|
37
|
+
onApplicationBootstrap() {
|
|
38
|
+
this.stopped = false;
|
|
39
|
+
for (const entry of this.scheduleRegistry.getAll()){
|
|
40
|
+
if (entry.inert) {
|
|
41
|
+
this.logger.log(`Skipping inert schedule: ${entry.jobId}::${entry.scheduleName}`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
let state;
|
|
46
|
+
const job = _cron.CronJob.from({
|
|
47
|
+
cronTime: entry.cron,
|
|
48
|
+
timeZone: entry.timezone,
|
|
49
|
+
start: false,
|
|
50
|
+
unrefTimeout: true,
|
|
51
|
+
name: scheduleKey(entry),
|
|
52
|
+
errorHandler: (err)=>{
|
|
53
|
+
this.logger.warn(`In-process schedule ${scheduleKey(entry)} callback failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
54
|
+
},
|
|
55
|
+
onTick: ()=>{
|
|
56
|
+
if (this.stopped) return;
|
|
57
|
+
this.dispatch(state, state.job.lastDate() ?? new Date());
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
state = {
|
|
61
|
+
entry,
|
|
62
|
+
job,
|
|
63
|
+
runningCount: 0,
|
|
64
|
+
queued: false,
|
|
65
|
+
queuedAt: null
|
|
66
|
+
};
|
|
67
|
+
this.states.set(scheduleKey(entry), state);
|
|
68
|
+
job.start();
|
|
69
|
+
} catch (err) {
|
|
70
|
+
this.logger.warn(`Failed to install in-process schedule ${entry.jobId}::${entry.scheduleName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (this.states.size === 0) return;
|
|
74
|
+
this.logger.log(`InProcessSchedule started: schedules=${this.states.size}`);
|
|
75
|
+
}
|
|
76
|
+
onApplicationShutdown() {
|
|
77
|
+
this.stopped = true;
|
|
78
|
+
for (const state of this.states.values()){
|
|
79
|
+
void state.job.stop();
|
|
80
|
+
}
|
|
81
|
+
this.states.clear();
|
|
82
|
+
}
|
|
83
|
+
dispatch(state, scheduledAt) {
|
|
84
|
+
if (this.stopped) return;
|
|
85
|
+
const overlap = state.entry.overlap ?? 'skip';
|
|
86
|
+
if (state.runningCount > 0) {
|
|
87
|
+
if (overlap === 'skip') return;
|
|
88
|
+
if (overlap === 'queue') {
|
|
89
|
+
state.queued = true;
|
|
90
|
+
state.queuedAt ??= scheduledAt;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
state.runningCount += 1;
|
|
95
|
+
void this.launch(state, scheduledAt).finally(()=>{
|
|
96
|
+
state.runningCount -= 1;
|
|
97
|
+
if (this.stopped) return;
|
|
98
|
+
if (state.runningCount === 0 && state.queued) {
|
|
99
|
+
state.queued = false;
|
|
100
|
+
const queuedAt = state.queuedAt ?? new Date();
|
|
101
|
+
state.queuedAt = null;
|
|
102
|
+
this.dispatch(state, queuedAt);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async launch(state, scheduledAt) {
|
|
107
|
+
const { entry } = state;
|
|
108
|
+
try {
|
|
109
|
+
const execution = await this.launcher.launch(entry.jobId, {
|
|
110
|
+
scheduled: true,
|
|
111
|
+
scheduleName: entry.scheduleName,
|
|
112
|
+
scheduledAt: scheduledAt.toISOString()
|
|
113
|
+
});
|
|
114
|
+
this.logger.log(`Fired schedule ${entry.jobId}::${entry.scheduleName} -> ` + `execution=${execution.id} status=${execution.status}`);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
this.logger.warn(`Failed to fire schedule ${entry.jobId}::${entry.scheduleName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
InProcessSchedule = _ts_decorate([
|
|
121
|
+
(0, _common.Injectable)(),
|
|
122
|
+
_ts_metadata("design:type", Function),
|
|
123
|
+
_ts_metadata("design:paramtypes", [
|
|
124
|
+
typeof _batchscheduleregistry.BatchScheduleRegistry === "undefined" ? Object : _batchscheduleregistry.BatchScheduleRegistry,
|
|
125
|
+
typeof _joblauncher.JobLauncher === "undefined" ? Object : _joblauncher.JobLauncher
|
|
126
|
+
])
|
|
127
|
+
], InProcessSchedule);
|
|
128
|
+
|
|
129
|
+
//# sourceMappingURL=in-process-schedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/execution/in-process-schedule.ts"],"sourcesContent":["import { Injectable, Logger, OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common';\nimport { CronJob } from 'cron';\n\nimport { type BatchScheduleEntry, BatchScheduleRegistry } from '../module/batch-schedule-registry';\nimport { JobLauncher } from './job-launcher';\n\ninterface ScheduleState {\n readonly entry: BatchScheduleEntry;\n readonly job: CronJob;\n runningCount: number;\n queued: boolean;\n queuedAt: Date | null;\n}\n\nfunction scheduleKey(entry: BatchScheduleEntry): string {\n return `${entry.jobId}::${entry.scheduleName}`;\n}\n\n/**\n * In-process scheduler for `@BatchScheduled` jobs.\n *\n * This is deliberately part of the in-process transport, not the\n * `@BatchScheduled` decorator. The decorator remains metadata-only;\n * this provider consumes the `BatchScheduleRegistry` and turns matching\n * cron ticks into `JobLauncher.launch(...)` calls inside the same server\n * process.\n */\n@Injectable()\nexport class InProcessSchedule implements OnApplicationBootstrap, OnApplicationShutdown {\n private readonly logger = new Logger(InProcessSchedule.name);\n private readonly states = new Map<string, ScheduleState>();\n private stopped = true;\n\n constructor(\n private readonly scheduleRegistry: BatchScheduleRegistry,\n private readonly launcher: JobLauncher,\n ) {}\n\n onApplicationBootstrap(): void {\n this.stopped = false;\n for (const entry of this.scheduleRegistry.getAll()) {\n if (entry.inert) {\n this.logger.log(`Skipping inert schedule: ${entry.jobId}::${entry.scheduleName}`);\n continue;\n }\n\n try {\n let state!: ScheduleState;\n const job = CronJob.from({\n cronTime: entry.cron,\n timeZone: entry.timezone,\n start: false,\n unrefTimeout: true,\n name: scheduleKey(entry),\n errorHandler: (err) => {\n this.logger.warn(\n `In-process schedule ${scheduleKey(entry)} callback failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n },\n onTick: () => {\n if (this.stopped) return;\n this.dispatch(state, state.job.lastDate() ?? new Date());\n },\n });\n state = {\n entry,\n job,\n runningCount: 0,\n queued: false,\n queuedAt: null,\n };\n this.states.set(scheduleKey(entry), state);\n job.start();\n } catch (err) {\n this.logger.warn(\n `Failed to install in-process schedule ${entry.jobId}::${entry.scheduleName}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n\n if (this.states.size === 0) return;\n\n this.logger.log(`InProcessSchedule started: schedules=${this.states.size}`);\n }\n\n onApplicationShutdown(): void {\n this.stopped = true;\n for (const state of this.states.values()) {\n void state.job.stop();\n }\n this.states.clear();\n }\n\n private dispatch(state: ScheduleState, scheduledAt: Date): void {\n if (this.stopped) return;\n const overlap = state.entry.overlap ?? 'skip';\n if (state.runningCount > 0) {\n if (overlap === 'skip') return;\n if (overlap === 'queue') {\n state.queued = true;\n state.queuedAt ??= scheduledAt;\n return;\n }\n }\n\n state.runningCount += 1;\n void this.launch(state, scheduledAt).finally(() => {\n state.runningCount -= 1;\n if (this.stopped) return;\n if (state.runningCount === 0 && state.queued) {\n state.queued = false;\n const queuedAt = state.queuedAt ?? new Date();\n state.queuedAt = null;\n this.dispatch(state, queuedAt);\n }\n });\n }\n\n private async launch(state: ScheduleState, scheduledAt: Date): Promise<void> {\n const { entry } = state;\n try {\n const execution = await this.launcher.launch(entry.jobId, {\n scheduled: true,\n scheduleName: entry.scheduleName,\n scheduledAt: scheduledAt.toISOString(),\n });\n this.logger.log(\n `Fired schedule ${entry.jobId}::${entry.scheduleName} -> ` +\n `execution=${execution.id} status=${execution.status}`,\n );\n } catch (err) {\n this.logger.warn(\n `Failed to fire schedule ${entry.jobId}::${entry.scheduleName}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n}\n"],"names":["InProcessSchedule","scheduleKey","entry","jobId","scheduleName","logger","Logger","name","states","Map","stopped","scheduleRegistry","launcher","onApplicationBootstrap","getAll","inert","log","state","job","CronJob","from","cronTime","cron","timeZone","timezone","start","unrefTimeout","errorHandler","err","warn","Error","message","String","onTick","dispatch","lastDate","Date","runningCount","queued","queuedAt","set","size","onApplicationShutdown","values","stop","clear","scheduledAt","overlap","launch","finally","execution","scheduled","toISOString","id","status"],"mappings":"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"}
|