@spinajs/queue 2.0.481 → 2.0.482
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/lib/cjs/config/queue.d.ts +10 -0
- package/lib/cjs/config/queue.d.ts.map +1 -1
- package/lib/cjs/config/queue.js +21 -0
- package/lib/cjs/config/queue.js.map +1 -1
- package/lib/cjs/decorators.d.ts.map +1 -1
- package/lib/cjs/decorators.js.map +1 -1
- package/lib/cjs/fp.d.ts.map +1 -1
- package/lib/cjs/fp.js.map +1 -1
- package/lib/cjs/index.d.ts +13 -2
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +103 -14
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/interfaces.d.ts +178 -7
- package/lib/cjs/interfaces.d.ts.map +1 -1
- package/lib/cjs/interfaces.js +59 -11
- package/lib/cjs/interfaces.js.map +1 -1
- package/lib/cjs/migrations/Queue_2026_06_30_00_00_00.d.ts +10 -0
- package/lib/cjs/migrations/Queue_2026_06_30_00_00_00.d.ts.map +1 -0
- package/lib/cjs/migrations/Queue_2026_06_30_00_00_00.js +29 -0
- package/lib/cjs/migrations/Queue_2026_06_30_00_00_00.js.map +1 -0
- package/lib/cjs/migrations/Queue_2026_07_02_00_00_00.d.ts +14 -0
- package/lib/cjs/migrations/Queue_2026_07_02_00_00_00.d.ts.map +1 -0
- package/lib/cjs/migrations/Queue_2026_07_02_00_00_00.js +53 -0
- package/lib/cjs/migrations/Queue_2026_07_02_00_00_00.js.map +1 -0
- package/lib/cjs/migrations/Queue_2026_07_10_00_00_00.d.ts +10 -0
- package/lib/cjs/migrations/Queue_2026_07_10_00_00_00.d.ts.map +1 -0
- package/lib/cjs/migrations/Queue_2026_07_10_00_00_00.js +31 -0
- package/lib/cjs/migrations/Queue_2026_07_10_00_00_00.js.map +1 -0
- package/lib/cjs/migrations/Queue_2026_07_17_00_00_00.d.ts +16 -0
- package/lib/cjs/migrations/Queue_2026_07_17_00_00_00.d.ts.map +1 -0
- package/lib/cjs/migrations/Queue_2026_07_17_00_00_00.js +49 -0
- package/lib/cjs/migrations/Queue_2026_07_17_00_00_00.js.map +1 -0
- package/lib/cjs/models/JobModel.d.ts +66 -4
- package/lib/cjs/models/JobModel.d.ts.map +1 -1
- package/lib/cjs/models/JobModel.js +43 -2
- package/lib/cjs/models/JobModel.js.map +1 -1
- package/lib/cjs/services/JobRetentionService.d.ts +26 -0
- package/lib/cjs/services/JobRetentionService.d.ts.map +1 -0
- package/lib/cjs/services/JobRetentionService.js +87 -0
- package/lib/cjs/services/JobRetentionService.js.map +1 -0
- package/lib/mjs/config/queue.d.ts +10 -0
- package/lib/mjs/config/queue.d.ts.map +1 -1
- package/lib/mjs/config/queue.js +21 -0
- package/lib/mjs/config/queue.js.map +1 -1
- package/lib/mjs/decorators.d.ts.map +1 -1
- package/lib/mjs/decorators.js.map +1 -1
- package/lib/mjs/fp.d.ts.map +1 -1
- package/lib/mjs/fp.js.map +1 -1
- package/lib/mjs/index.d.ts +13 -2
- package/lib/mjs/index.d.ts.map +1 -1
- package/lib/mjs/index.js +105 -16
- package/lib/mjs/index.js.map +1 -1
- package/lib/mjs/interfaces.d.ts +178 -7
- package/lib/mjs/interfaces.d.ts.map +1 -1
- package/lib/mjs/interfaces.js +57 -10
- package/lib/mjs/interfaces.js.map +1 -1
- package/lib/mjs/migrations/Queue_2026_06_30_00_00_00.d.ts +10 -0
- package/lib/mjs/migrations/Queue_2026_06_30_00_00_00.d.ts.map +1 -0
- package/lib/mjs/migrations/Queue_2026_06_30_00_00_00.js +26 -0
- package/lib/mjs/migrations/Queue_2026_06_30_00_00_00.js.map +1 -0
- package/lib/mjs/migrations/Queue_2026_07_02_00_00_00.d.ts +14 -0
- package/lib/mjs/migrations/Queue_2026_07_02_00_00_00.d.ts.map +1 -0
- package/lib/mjs/migrations/Queue_2026_07_02_00_00_00.js +50 -0
- package/lib/mjs/migrations/Queue_2026_07_02_00_00_00.js.map +1 -0
- package/lib/mjs/migrations/Queue_2026_07_10_00_00_00.d.ts +10 -0
- package/lib/mjs/migrations/Queue_2026_07_10_00_00_00.d.ts.map +1 -0
- package/lib/mjs/migrations/Queue_2026_07_10_00_00_00.js +28 -0
- package/lib/mjs/migrations/Queue_2026_07_10_00_00_00.js.map +1 -0
- package/lib/mjs/migrations/Queue_2026_07_17_00_00_00.d.ts +16 -0
- package/lib/mjs/migrations/Queue_2026_07_17_00_00_00.d.ts.map +1 -0
- package/lib/mjs/migrations/Queue_2026_07_17_00_00_00.js +46 -0
- package/lib/mjs/migrations/Queue_2026_07_17_00_00_00.js.map +1 -0
- package/lib/mjs/models/JobModel.d.ts +66 -4
- package/lib/mjs/models/JobModel.d.ts.map +1 -1
- package/lib/mjs/models/JobModel.js +42 -2
- package/lib/mjs/models/JobModel.js.map +1 -1
- package/lib/mjs/services/JobRetentionService.d.ts +26 -0
- package/lib/mjs/services/JobRetentionService.d.ts.map +1 -0
- package/lib/mjs/services/JobRetentionService.js +84 -0
- package/lib/mjs/services/JobRetentionService.js.map +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +9 -6
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
8
|
+
import { OrmMigration, Migration } from '@spinajs/orm';
|
|
9
|
+
/**
|
|
10
|
+
* Widens `queue_jobs.Status` to the six states the code actually writes.
|
|
11
|
+
* `retrying` / `dead` arrived with retry support (see index.ts) but the enum
|
|
12
|
+
* was never widened, so strict-enum drivers reject or coerce them.
|
|
13
|
+
*
|
|
14
|
+
* NOT NULL and the 'created' default are restated deliberately: MySQL's MODIFY
|
|
15
|
+
* COLUMN replaces the whole definition and silently drops anything omitted.
|
|
16
|
+
* On sqlite this alteration is a logged no-op - it renders enum as
|
|
17
|
+
* unconstrained TEXT, so there is nothing to widen there.
|
|
18
|
+
*/
|
|
19
|
+
let Queue_2026_07_17_00_00_00 = class Queue_2026_07_17_00_00_00 extends OrmMigration {
|
|
20
|
+
async up(connection) {
|
|
21
|
+
// sqlite renders enum as unconstrained TEXT and has no MODIFY COLUMN, so there is
|
|
22
|
+
// nothing to widen there. Emitting the MODIFY anyway produces a MySQL-flavoured
|
|
23
|
+
// `ALTER TABLE ... MODIFY ... ENUM(...)` that sqlite rejects, so skip it explicitly
|
|
24
|
+
// ( mirrors the guard in Queue_2026_07_02, and matches this migration's documented
|
|
25
|
+
// "no-op on sqlite" contract above ).
|
|
26
|
+
const isSqlite = connection.Options.Driver.toLowerCase().includes('sqlite');
|
|
27
|
+
if (isSqlite) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
await connection.schema().alterTable('queue_jobs', (table) => {
|
|
31
|
+
// `.default().value('created')` returns the ColumnQueryBuilder, which has no
|
|
32
|
+
// `.modify()` - that lives on AlterColumnQueryBuilder. So `.modify()` must be
|
|
33
|
+
// called on the column builder itself, separately from the default.
|
|
34
|
+
const status = table.enum('Status', ['error', 'success', 'created', 'executing', 'retrying', 'dead']).notNull();
|
|
35
|
+
status.default().value('created');
|
|
36
|
+
status.modify();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
40
|
+
async down(_connection) { }
|
|
41
|
+
};
|
|
42
|
+
Queue_2026_07_17_00_00_00 = __decorate([
|
|
43
|
+
Migration('queue')
|
|
44
|
+
], Queue_2026_07_17_00_00_00);
|
|
45
|
+
export { Queue_2026_07_17_00_00_00 };
|
|
46
|
+
//# sourceMappingURL=Queue_2026_07_17_00_00_00.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Queue_2026_07_17_00_00_00.js","sourceRoot":"","sources":["../../../src/migrations/Queue_2026_07_17_00_00_00.ts"],"names":[],"mappings":";;;;;;AAAA,sDAAsD;AACtD,OAAO,EAAE,YAAY,EAAa,SAAS,EAAE,MAAM,cAAc,CAAC;AAElE;;;;;;;;;GASG;AAEI,IAAM,yBAAyB,GAA/B,MAAM,yBAA0B,SAAQ,YAAY;IAClD,KAAK,CAAC,EAAE,CAAC,UAAqB;QACnC,kFAAkF;QAClF,gFAAgF;QAChF,oFAAoF;QACpF,mFAAmF;QACnF,sCAAsC;QACtC,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC5E,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QAED,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3D,6EAA6E;YAC7E,8EAA8E;YAC9E,oEAAoE;YACpE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;YAChH,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gEAAgE;IACzD,KAAK,CAAC,IAAI,CAAC,WAAsB,IAAkB,CAAC;CAC5D,CAAA;AAxBY,yBAAyB;IADrC,SAAS,CAAC,OAAO,CAAC;GACN,yBAAyB,CAwBrC"}
|
|
@@ -1,18 +1,80 @@
|
|
|
1
1
|
import { DateTime } from 'luxon';
|
|
2
|
-
import { ModelBase } from '@spinajs/orm';
|
|
2
|
+
import { ModelBase, QueryScope, ISelectQueryBuilder } from '@spinajs/orm';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Possible job lifecycle statuses.
|
|
5
|
+
* - created: dispatched, not yet picked up
|
|
6
|
+
* - executing: currently running
|
|
7
|
+
* - retrying: failed, will be retried
|
|
8
|
+
* - success: finished ok ( terminal )
|
|
9
|
+
* - error: legacy generic failure ( terminal )
|
|
10
|
+
* - dead: failed and retries exhausted / dead-lettered ( terminal )
|
|
11
|
+
*/
|
|
12
|
+
export type JobStatus = 'created' | 'executing' | 'retrying' | 'success' | 'error' | 'dead';
|
|
13
|
+
/** Statuses a job can no longer leave - safe to report as done / purge. */
|
|
14
|
+
export declare const JOB_TERMINAL_STATUSES: JobStatus[];
|
|
15
|
+
/** Statuses of jobs still in flight. */
|
|
16
|
+
export declare const JOB_ACTIVE_STATUSES: JobStatus[];
|
|
17
|
+
/**
|
|
18
|
+
* Reusable query scopes for {@link JobModel}. Available on any JobModel query
|
|
19
|
+
* ( select / update / destroy ), so callers compose readable, safe queries eg.
|
|
5
20
|
*
|
|
21
|
+
* await JobModel.destroy().terminal().olderThan(cutoff);
|
|
22
|
+
* await JobModel.query().byJobId(id).first();
|
|
23
|
+
*/
|
|
24
|
+
export declare class JobQueryScopes implements QueryScope {
|
|
25
|
+
byJobId(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes, jobId: string): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
|
|
26
|
+
withStatus(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes, status: JobStatus | JobStatus[]): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
|
|
27
|
+
/** Jobs in a terminal state ( success / error / dead ). */
|
|
28
|
+
terminal(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
|
|
29
|
+
/** Jobs still in flight ( created / executing / retrying ). */
|
|
30
|
+
active(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
|
|
31
|
+
/** Jobs created before the given moment ( row age ). */
|
|
32
|
+
olderThan(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes, date: DateTime): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
|
|
33
|
+
/** Jobs finished before the given moment. */
|
|
34
|
+
finishedBefore(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes, date: DateTime): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Tracks a dispatched job: its status, progress, result / error and timing.
|
|
38
|
+
* One row per emitted job, keyed by the unique {@link JobModel.JobId}.
|
|
6
39
|
*/
|
|
7
|
-
export declare class JobModel<T> extends ModelBase {
|
|
40
|
+
export declare class JobModel<T = unknown> extends ModelBase {
|
|
41
|
+
static readonly _queryScopes: JobQueryScopes;
|
|
8
42
|
Id: number;
|
|
9
43
|
JobId: string;
|
|
10
44
|
Name: string;
|
|
45
|
+
/**
|
|
46
|
+
* The job's successful result. Failures are recorded in {@link JobModel.LastError},
|
|
47
|
+
* so this holds only the actual output.
|
|
48
|
+
*/
|
|
11
49
|
Result: T;
|
|
12
|
-
Status:
|
|
50
|
+
Status: JobStatus;
|
|
13
51
|
Progress: number;
|
|
52
|
+
/**
|
|
53
|
+
* Optional human-facing progress phase (e.g. `loading`, `rendering`), set by
|
|
54
|
+
* jobs that report richer progress than a bare percentage. Nullable.
|
|
55
|
+
*/
|
|
56
|
+
Phase: string;
|
|
57
|
+
/**
|
|
58
|
+
* Optional human-facing progress message accompanying {@link Phase}. Nullable.
|
|
59
|
+
*/
|
|
60
|
+
Message: string;
|
|
61
|
+
/**
|
|
62
|
+
* Number of times job execution has been attempted. Incremented on every failed run.
|
|
63
|
+
* Once it reaches {@link JobModel.MaxAttempts} the job is marked as `dead`.
|
|
64
|
+
*/
|
|
65
|
+
Attempt: number;
|
|
66
|
+
/**
|
|
67
|
+
* Total allowed executions for the job ( RetryCount + 1 ) captured at first receipt.
|
|
68
|
+
* Lets consumers render `Attempt / MaxAttempts` ( e.g. "attempt 2 of 2" ).
|
|
69
|
+
*/
|
|
70
|
+
MaxAttempts: number;
|
|
71
|
+
/**
|
|
72
|
+
* Last error message when the job failed or was retried. Kept separate from Result.
|
|
73
|
+
*/
|
|
74
|
+
LastError: string;
|
|
14
75
|
Connection: string;
|
|
15
76
|
CreatedAt: DateTime;
|
|
77
|
+
UpdatedAt: DateTime;
|
|
16
78
|
ExecutedAt: DateTime;
|
|
17
79
|
FinishedAt: DateTime;
|
|
18
80
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JobModel.d.ts","sourceRoot":"","sources":["../../../src/models/JobModel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"JobModel.d.ts","sourceRoot":"","sources":["../../../src/models/JobModel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,SAAS,EAA0E,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAElJ;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5F,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,EAAE,SAAS,EAAiC,CAAC;AAE/E,wCAAwC;AACxC,eAAO,MAAM,mBAAmB,EAAE,SAAS,EAAyC,CAAC;AAErF;;;;;;GAMG;AACH,qBAAa,cAAe,YAAW,UAAU;IACxC,OAAO,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc,EAAE,KAAK,EAAE,MAAM;IAI7E,UAAU,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc,EAAE,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE;IAIzG,2DAA2D;IACpD,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc;IAItE,+DAA+D;IACxD,MAAM,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc;IAIpE,wDAAwD;IACjD,SAAS,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,QAAQ;IAIvF,6CAA6C;IACtC,cAAc,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,QAAQ;CAG7F;AAED;;;GAGG;AACH,qBAEa,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAE,SAAQ,SAAS;IAClD,gBAAuB,YAAY,EAAE,cAAc,CAAwB;IAGpE,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,IAAI,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IAEI,MAAM,EAAE,CAAC,CAAC;IAEV,MAAM,EAAE,SAAS,CAAC;IAElB,QAAQ,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACI,KAAK,EAAE,MAAM,CAAC;IAErB;;OAEG;IACI,OAAO,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACI,OAAO,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACI,WAAW,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACI,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,EAAE,MAAM,CAAC;IAGnB,SAAS,EAAE,QAAQ,CAAC;IAGpB,SAAS,EAAE,QAAQ,CAAC;IAGpB,UAAU,EAAE,QAAQ,CAAC;IAGrB,UAAU,EAAE,QAAQ,CAAC;CAC7B"}
|
|
@@ -8,13 +8,49 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import { DateTime } from 'luxon';
|
|
11
|
-
import { ModelBase, Connection, Model, Json, CreatedAt, DateTime as DT, Primary } from '@spinajs/orm';
|
|
11
|
+
import { ModelBase, Connection, Model, Json, CreatedAt, UpdatedAt, DateTime as DT, Primary } from '@spinajs/orm';
|
|
12
|
+
/** Statuses a job can no longer leave - safe to report as done / purge. */
|
|
13
|
+
export const JOB_TERMINAL_STATUSES = ['success', 'error', 'dead'];
|
|
14
|
+
/** Statuses of jobs still in flight. */
|
|
15
|
+
export const JOB_ACTIVE_STATUSES = ['created', 'executing', 'retrying'];
|
|
12
16
|
/**
|
|
13
|
-
*
|
|
17
|
+
* Reusable query scopes for {@link JobModel}. Available on any JobModel query
|
|
18
|
+
* ( select / update / destroy ), so callers compose readable, safe queries eg.
|
|
14
19
|
*
|
|
20
|
+
* await JobModel.destroy().terminal().olderThan(cutoff);
|
|
21
|
+
* await JobModel.query().byJobId(id).first();
|
|
22
|
+
*/
|
|
23
|
+
export class JobQueryScopes {
|
|
24
|
+
byJobId(jobId) {
|
|
25
|
+
return this.where('JobId', jobId);
|
|
26
|
+
}
|
|
27
|
+
withStatus(status) {
|
|
28
|
+
return Array.isArray(status) ? this.whereIn('Status', status) : this.where('Status', status);
|
|
29
|
+
}
|
|
30
|
+
/** Jobs in a terminal state ( success / error / dead ). */
|
|
31
|
+
terminal() {
|
|
32
|
+
return this.whereIn('Status', JOB_TERMINAL_STATUSES);
|
|
33
|
+
}
|
|
34
|
+
/** Jobs still in flight ( created / executing / retrying ). */
|
|
35
|
+
active() {
|
|
36
|
+
return this.whereIn('Status', JOB_ACTIVE_STATUSES);
|
|
37
|
+
}
|
|
38
|
+
/** Jobs created before the given moment ( row age ). */
|
|
39
|
+
olderThan(date) {
|
|
40
|
+
return this.where('CreatedAt', '<', date);
|
|
41
|
+
}
|
|
42
|
+
/** Jobs finished before the given moment. */
|
|
43
|
+
finishedBefore(date) {
|
|
44
|
+
return this.where('FinishedAt', '<', date);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Tracks a dispatched job: its status, progress, result / error and timing.
|
|
49
|
+
* One row per emitted job, keyed by the unique {@link JobModel.JobId}.
|
|
15
50
|
*/
|
|
16
51
|
let JobModel = class JobModel extends ModelBase {
|
|
17
52
|
};
|
|
53
|
+
JobModel._queryScopes = new JobQueryScopes();
|
|
18
54
|
__decorate([
|
|
19
55
|
Primary(),
|
|
20
56
|
__metadata("design:type", Number)
|
|
@@ -27,6 +63,10 @@ __decorate([
|
|
|
27
63
|
CreatedAt(),
|
|
28
64
|
__metadata("design:type", DateTime)
|
|
29
65
|
], JobModel.prototype, "CreatedAt", void 0);
|
|
66
|
+
__decorate([
|
|
67
|
+
UpdatedAt(),
|
|
68
|
+
__metadata("design:type", DateTime)
|
|
69
|
+
], JobModel.prototype, "UpdatedAt", void 0);
|
|
30
70
|
__decorate([
|
|
31
71
|
DT(),
|
|
32
72
|
__metadata("design:type", DateTime)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JobModel.js","sourceRoot":"","sources":["../../../src/models/JobModel.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"JobModel.js","sourceRoot":"","sources":["../../../src/models/JobModel.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,IAAI,EAAE,EAAE,OAAO,EAAmC,MAAM,cAAc,CAAC;AAalJ,2EAA2E;AAC3E,MAAM,CAAC,MAAM,qBAAqB,GAAgB,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAE/E,wCAAwC;AACxC,MAAM,CAAC,MAAM,mBAAmB,GAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;AAErF;;;;;;GAMG;AACH,MAAM,OAAO,cAAc;IAClB,OAAO,CAAyD,KAAa;QAClF,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IAEM,UAAU,CAAyD,MAA+B;QACvG,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/F,CAAC;IAED,2DAA2D;IACpD,QAAQ;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAC;IACvD,CAAC;IAED,+DAA+D;IACxD,MAAM;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;IACrD,CAAC;IAED,wDAAwD;IACjD,SAAS,CAAyD,IAAc;QACrF,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,6CAA6C;IACtC,cAAc,CAAyD,IAAc;QAC1F,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;CACF;AAED;;;GAGG;AAGI,IAAM,QAAQ,GAAd,MAAM,QAAsB,SAAQ,SAAS;;AAC3B,qBAAY,GAAmB,IAAI,cAAc,EAAE,AAAvC,CAAwC;AAGpE;IADN,OAAO,EAAE;;oCACQ;AAWX;IADN,IAAI,EAAE;;wCACU;AAqCV;IADN,SAAS,EAAE;8BACM,QAAQ;2CAAC;AAGpB;IADN,SAAS,EAAE;8BACM,QAAQ;2CAAC;AAGpB;IADN,EAAE,EAAE;8BACc,QAAQ;4CAAC;AAGrB;IADN,EAAE,EAAE;8BACc,QAAQ;4CAAC;AA7DjB,QAAQ;IAFpB,UAAU,CAAC,OAAO,CAAC;IACnB,KAAK,CAAC,YAAY,CAAC;GACP,QAAQ,CA8DpB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Log } from '@spinajs/log';
|
|
2
|
+
import { DateTime } from 'luxon';
|
|
3
|
+
import { JobRetentionService } from '../interfaces.js';
|
|
4
|
+
/**
|
|
5
|
+
* Built-in {@link JobRetentionService}. Periodically deletes terminal jobs older than the
|
|
6
|
+
* configured `maxAge` so the `queue_jobs` table doesn't grow unbounded. All timings / statuses
|
|
7
|
+
* come from the injected `queue.retention` config block - nothing is hardcoded except the
|
|
8
|
+
* fallback interval used when none is configured.
|
|
9
|
+
*/
|
|
10
|
+
export declare class DefaultJobRetentionService extends JobRetentionService {
|
|
11
|
+
protected Log: Log;
|
|
12
|
+
protected RetentionTimer?: ReturnType<typeof setInterval>;
|
|
13
|
+
resolve(): Promise<void>;
|
|
14
|
+
dispose(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Starts the periodic job-retention purge when configured. No-op if retention is disabled
|
|
17
|
+
* or no `maxAge` is set.
|
|
18
|
+
*/
|
|
19
|
+
protected startRetention(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Deletes terminal ( or given ) jobs created before `olderThan`. The set is selected with the
|
|
22
|
+
* JobModel query scopes ( status + date ), then removed by id.
|
|
23
|
+
*/
|
|
24
|
+
purgeJobs(olderThan: DateTime, statuses?: string[]): Promise<number>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=JobRetentionService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JobRetentionService.d.ts","sourceRoot":"","sources":["../../../src/services/JobRetentionService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAQvD;;;;;GAKG;AACH,qBACa,0BAA2B,SAAQ,mBAAmB;IAEjE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEnB,SAAS,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IAE7C,OAAO;IAIP,OAAO;IAOpB;;;OAGG;IACH,SAAS,CAAC,cAAc;IAyBxB;;;OAGG;IACU,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAclF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Injectable } from '@spinajs/di';
|
|
11
|
+
import { Log, Logger } from '@spinajs/log';
|
|
12
|
+
import { DateTime } from 'luxon';
|
|
13
|
+
import { JobRetentionService } from '../interfaces.js';
|
|
14
|
+
import { JobModel, JOB_TERMINAL_STATUSES } from '../models/JobModel.js';
|
|
15
|
+
/**
|
|
16
|
+
* How often ( ms ) to run the purge when `queue.retention.interval` is not set.
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
|
19
|
+
/**
|
|
20
|
+
* Built-in {@link JobRetentionService}. Periodically deletes terminal jobs older than the
|
|
21
|
+
* configured `maxAge` so the `queue_jobs` table doesn't grow unbounded. All timings / statuses
|
|
22
|
+
* come from the injected `queue.retention` config block - nothing is hardcoded except the
|
|
23
|
+
* fallback interval used when none is configured.
|
|
24
|
+
*/
|
|
25
|
+
let DefaultJobRetentionService = class DefaultJobRetentionService extends JobRetentionService {
|
|
26
|
+
async resolve() {
|
|
27
|
+
this.startRetention();
|
|
28
|
+
}
|
|
29
|
+
async dispose() {
|
|
30
|
+
if (this.RetentionTimer) {
|
|
31
|
+
clearInterval(this.RetentionTimer);
|
|
32
|
+
this.RetentionTimer = undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Starts the periodic job-retention purge when configured. No-op if retention is disabled
|
|
37
|
+
* or no `maxAge` is set.
|
|
38
|
+
*/
|
|
39
|
+
startRetention() {
|
|
40
|
+
if (!this.Options?.enabled || !this.Options.maxAge) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const interval = this.Options.interval ?? DEFAULT_INTERVAL_MS;
|
|
44
|
+
const maxAge = this.Options.maxAge;
|
|
45
|
+
this.RetentionTimer = setInterval(() => {
|
|
46
|
+
const cutoff = DateTime.now().minus({ milliseconds: maxAge });
|
|
47
|
+
this.purgeJobs(cutoff, this.Options.statuses)
|
|
48
|
+
.then((n) => {
|
|
49
|
+
if (n > 0) {
|
|
50
|
+
this.Log.info(`Retention purge removed ${n} job(s) older than ${cutoff.toISO()}`);
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
.catch((err) => this.Log.error(err, 'Job retention purge failed'));
|
|
54
|
+
}, interval);
|
|
55
|
+
// don't keep the process alive just for the purge timer
|
|
56
|
+
this.RetentionTimer.unref?.();
|
|
57
|
+
this.Log.info(`Job retention enabled: purging jobs older than ${maxAge}ms every ${interval}ms`);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Deletes terminal ( or given ) jobs created before `olderThan`. The set is selected with the
|
|
61
|
+
* JobModel query scopes ( status + date ), then removed by id.
|
|
62
|
+
*/
|
|
63
|
+
async purgeJobs(olderThan, statuses) {
|
|
64
|
+
const rows = await JobModel.query()
|
|
65
|
+
.columns(['Id'])
|
|
66
|
+
.withStatus(statuses ?? JOB_TERMINAL_STATUSES)
|
|
67
|
+
.olderThan(olderThan);
|
|
68
|
+
const ids = rows.map((r) => r.Id);
|
|
69
|
+
if (ids.length === 0) {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
await JobModel.destroy(ids);
|
|
73
|
+
return ids.length;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
__decorate([
|
|
77
|
+
Logger('queue'),
|
|
78
|
+
__metadata("design:type", Log)
|
|
79
|
+
], DefaultJobRetentionService.prototype, "Log", void 0);
|
|
80
|
+
DefaultJobRetentionService = __decorate([
|
|
81
|
+
Injectable(JobRetentionService)
|
|
82
|
+
], DefaultJobRetentionService);
|
|
83
|
+
export { DefaultJobRetentionService };
|
|
84
|
+
//# sourceMappingURL=JobRetentionService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JobRetentionService.js","sourceRoot":"","sources":["../../../src/services/JobRetentionService.ts"],"names":[],"mappings":"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"}
|