@outloud/adonis-scheduler 1.1.0 → 1.1.1
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 +18 -17
- package/build/src/scheduler.d.ts +1 -0
- package/build/src/scheduler.js +22 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,9 +56,16 @@ export default class TestTask extends Task {
|
|
|
56
56
|
### Register a task
|
|
57
57
|
For task to run it must be registered in the scheduler.
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
#### Auto-discovery (recommended)
|
|
60
|
+
By default tasks are auto-discovered using the locations defined in `config/scheduler.ts`.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const schedulerConfig = defineConfig({
|
|
64
|
+
locations: ['./app/**/*.task.js'],
|
|
65
|
+
})
|
|
66
|
+
```
|
|
61
67
|
|
|
68
|
+
#### Manual registration
|
|
62
69
|
If you want to register tasks manually, you can register tasks in two ways: using the `start/scheduler.ts` preloaded file or in a provider's `start` method.
|
|
63
70
|
|
|
64
71
|
Using `start/scheduler.ts` file.
|
|
@@ -69,7 +76,7 @@ import scheduler from '@outloud/adonis-scheduler/services/main'
|
|
|
69
76
|
scheduler.register(() => import('../app/tasks/test.task.js'))
|
|
70
77
|
```
|
|
71
78
|
|
|
72
|
-
Or
|
|
79
|
+
Or in a any provider's `start` method.
|
|
73
80
|
|
|
74
81
|
```ts
|
|
75
82
|
import type { ApplicationService } from '@adonisjs/core/types'
|
|
@@ -117,11 +124,9 @@ You can also create a custom worker command that runs the scheduler. This is use
|
|
|
117
124
|
```ts [commands/worker.ts]
|
|
118
125
|
import { createServer, type Server as HttpServer } from 'node:http'
|
|
119
126
|
import { BaseCommand } from '@adonisjs/core/ace'
|
|
120
|
-
import { Server } from '@adonisjs/core/http'
|
|
121
127
|
import type { CommandOptions } from '@adonisjs/core/types/ace'
|
|
122
128
|
import { inject } from '@adonisjs/core'
|
|
123
129
|
import { Scheduler } from '@outloud/adonis-scheduler'
|
|
124
|
-
import type { ApplicationService } from '@adonisjs/core/types'
|
|
125
130
|
|
|
126
131
|
export default class extends BaseCommand {
|
|
127
132
|
static commandName = 'worker'
|
|
@@ -136,7 +141,9 @@ export default class extends BaseCommand {
|
|
|
136
141
|
private scheduler?: Scheduler
|
|
137
142
|
|
|
138
143
|
prepare() {
|
|
139
|
-
this.app.terminating(() =>
|
|
144
|
+
this.app.terminating(() => {
|
|
145
|
+
this.server?.close()
|
|
146
|
+
})
|
|
140
147
|
this.app.terminating(() => this.scheduler?.stop())
|
|
141
148
|
}
|
|
142
149
|
|
|
@@ -152,14 +159,14 @@ export default class extends BaseCommand {
|
|
|
152
159
|
|
|
153
160
|
private async startServer() {
|
|
154
161
|
const server = await this.makeServer()
|
|
155
|
-
const httpServer = createServer(server.handle.bind(server))
|
|
156
|
-
this.server = httpServer
|
|
157
162
|
await server.boot()
|
|
158
163
|
|
|
164
|
+
const httpServer = createServer(server.handle.bind(server))
|
|
159
165
|
server.setNodeServer(httpServer)
|
|
166
|
+
this.server = httpServer
|
|
160
167
|
|
|
161
168
|
const host = process.env.HOST || '0.0.0.0'
|
|
162
|
-
const port = Number(process.env.PORT ||
|
|
169
|
+
const port = Number(process.env.PORT || 3000)
|
|
163
170
|
|
|
164
171
|
httpServer.once('listening', () => this.logger.info(`listening to http server, host: ${host}, port: ${port}`))
|
|
165
172
|
|
|
@@ -167,15 +174,9 @@ export default class extends BaseCommand {
|
|
|
167
174
|
}
|
|
168
175
|
|
|
169
176
|
private async makeServer() {
|
|
170
|
-
const server =
|
|
171
|
-
this.app,
|
|
172
|
-
await this.app.container.make('encryption'),
|
|
173
|
-
await this.app.container.make('emitter'),
|
|
174
|
-
await this.app.container.make('logger'),
|
|
175
|
-
this.app.config.get<any>('app.http'),
|
|
176
|
-
)
|
|
177
|
-
|
|
177
|
+
const server = await this.app.container.make('server')
|
|
178
178
|
const router = server.getRouter()
|
|
179
|
+
|
|
179
180
|
router.get('/health', () => ({ status: 'ok' }))
|
|
180
181
|
|
|
181
182
|
return server
|
package/build/src/scheduler.d.ts
CHANGED
package/build/src/scheduler.js
CHANGED
|
@@ -22,7 +22,11 @@ var Scheduler = class {
|
|
|
22
22
|
state: "created",
|
|
23
23
|
jobs: []
|
|
24
24
|
};
|
|
25
|
-
if (
|
|
25
|
+
if (Task.isTask(options)) {
|
|
26
|
+
definition.task = options;
|
|
27
|
+
Object.assign(definition, options.options ?? {});
|
|
28
|
+
} else if (typeof options === "function") definition.loader = options;
|
|
29
|
+
else {
|
|
26
30
|
const command = Array.isArray(options.command) ? options.command : [options.command];
|
|
27
31
|
Object.assign(definition, options);
|
|
28
32
|
definition.loader = () => import("./command.task.js").then((module) => {
|
|
@@ -30,10 +34,7 @@ var Scheduler = class {
|
|
|
30
34
|
static command = command;
|
|
31
35
|
};
|
|
32
36
|
});
|
|
33
|
-
}
|
|
34
|
-
definition.task = options;
|
|
35
|
-
Object.assign(definition, options.options ?? {});
|
|
36
|
-
} else definition.loader = options;
|
|
37
|
+
}
|
|
37
38
|
if (!definition.task && !definition.loader) throw new Error("Task definition must have either a command or a task defined.");
|
|
38
39
|
this.definitions.push(definition);
|
|
39
40
|
if (this.hasState(["starting", "running"])) this.schedule(definition);
|
|
@@ -85,19 +86,25 @@ var Scheduler = class {
|
|
|
85
86
|
async make(definition) {
|
|
86
87
|
return await this.resolver.make(definition.task);
|
|
87
88
|
}
|
|
89
|
+
resolveLock(task, definition) {
|
|
90
|
+
if (!definition.lock) return;
|
|
91
|
+
if (!this.locks) {
|
|
92
|
+
this.logger.warn("Lock is not available, install @adonisjs/lock to use task locking.");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const lockDuration = definition.lock && typeof definition.lock !== "boolean" ? definition.lock : this.config.lockDuration;
|
|
96
|
+
return this.locks.createLock(`scheduler:${task.name}`, lockDuration);
|
|
97
|
+
}
|
|
88
98
|
async run(definition) {
|
|
89
99
|
const task = await this.make(definition);
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (!await lock.acquireImmediately()) {
|
|
100
|
+
const lock = this.resolveLock(task, definition);
|
|
101
|
+
try {
|
|
102
|
+
definition.jobs.push(task);
|
|
103
|
+
if (lock && !await lock.acquireImmediately()) {
|
|
95
104
|
this.config.warnWhenLocked && this.logger.warn(`Task "${definition.task?.name}" is locked and cannot be run.`);
|
|
96
105
|
return;
|
|
97
106
|
}
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
definition.jobs.push(task);
|
|
107
|
+
if (task.isCanceled) return;
|
|
101
108
|
const promise = this.resolver.call(task, "run").finally(() => lock?.release());
|
|
102
109
|
task.promise = promise.catch(() => {});
|
|
103
110
|
await promise;
|
|
@@ -117,12 +124,12 @@ var Scheduler = class {
|
|
|
117
124
|
}
|
|
118
125
|
async handleError(error, _, task) {
|
|
119
126
|
await task.onError?.(error);
|
|
120
|
-
this.emitter.emit("scheduler:error", {
|
|
127
|
+
await this.emitter.emit("scheduler:error", {
|
|
121
128
|
error,
|
|
122
129
|
task
|
|
123
130
|
});
|
|
124
131
|
await this.errorHandler?.(error, task);
|
|
125
|
-
if (!this.emitter.hasListeners("scheduler:error")
|
|
132
|
+
if (!(task.onError || this.emitter.hasListeners("scheduler:error") || this.errorHandler)) throw error;
|
|
126
133
|
}
|
|
127
134
|
async cancel(job) {
|
|
128
135
|
await job.cancel();
|