@unchainedshop/core-worker 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.npm/package/README +7 -0
  2. package/.npm/package/npm-shrinkwrap.json +257 -0
  3. package/.versions +53 -0
  4. package/README.md +9 -0
  5. package/lib/db/WorkQueueCollection.d.ts +3 -0
  6. package/lib/db/WorkQueueCollection.js +32 -0
  7. package/lib/db/WorkQueueCollection.js.map +1 -0
  8. package/lib/db/WorkQueueSchema.d.ts +1 -0
  9. package/lib/db/WorkQueueSchema.js +20 -0
  10. package/lib/db/WorkQueueSchema.js.map +1 -0
  11. package/lib/director/WorkStatus.d.ts +7 -0
  12. package/lib/director/WorkStatus.js +9 -0
  13. package/lib/director/WorkStatus.js.map +1 -0
  14. package/lib/director/WorkerAdapter.d.ts +2 -0
  15. package/lib/director/WorkerAdapter.js +10 -0
  16. package/lib/director/WorkerAdapter.js.map +1 -0
  17. package/lib/director/WorkerDirector.d.ts +3 -0
  18. package/lib/director/WorkerDirector.js +46 -0
  19. package/lib/director/WorkerDirector.js.map +1 -0
  20. package/lib/director/WorkerEventTypes.d.ts +8 -0
  21. package/lib/director/WorkerEventTypes.js +13 -0
  22. package/lib/director/WorkerEventTypes.js.map +1 -0
  23. package/lib/module/configureWorkerModule.d.ts +3 -0
  24. package/lib/module/configureWorkerModule.js +272 -0
  25. package/lib/module/configureWorkerModule.js.map +1 -0
  26. package/lib/schedulers/FailedRescheduler.d.ts +2 -0
  27. package/lib/schedulers/FailedRescheduler.js +38 -0
  28. package/lib/schedulers/FailedRescheduler.js.map +1 -0
  29. package/lib/worker-index.d.ts +9 -0
  30. package/lib/worker-index.js +10 -0
  31. package/lib/worker-index.js.map +1 -0
  32. package/lib/workers/BaseWorker.d.ts +7 -0
  33. package/lib/workers/BaseWorker.js +85 -0
  34. package/lib/workers/BaseWorker.js.map +1 -0
  35. package/lib/workers/EventListenerWorker.d.ts +4 -0
  36. package/lib/workers/EventListenerWorker.js +44 -0
  37. package/lib/workers/EventListenerWorker.js.map +1 -0
  38. package/lib/workers/IntervalWorker.d.ts +9 -0
  39. package/lib/workers/IntervalWorker.js +38 -0
  40. package/lib/workers/IntervalWorker.js.map +1 -0
  41. package/package.js +33 -0
  42. package/package.json +43 -0
  43. package/src/db/WorkQueueCollection.ts +37 -0
  44. package/src/db/WorkQueueSchema.js +23 -0
  45. package/src/director/WorkStatus.ts +7 -0
  46. package/src/director/WorkerAdapter.ts +12 -0
  47. package/src/director/WorkerDirector.ts +73 -0
  48. package/src/director/WorkerEventTypes.ts +11 -0
  49. package/src/module/configureWorkerModule.ts +397 -0
  50. package/src/schedulers/FailedRescheduler.ts +53 -0
  51. package/src/worker-index.ts +12 -0
  52. package/src/workers/BaseWorker.ts +116 -0
  53. package/src/workers/EventListenerWorker.ts +55 -0
  54. package/src/workers/IntervalWorker.ts +57 -0
  55. package/tests/worker-index.test.ts +32 -0
  56. package/tsconfig.build.json +26 -0
  57. package/tsconfig.json +8 -0
@@ -0,0 +1,272 @@
1
+ import { log, LogLevel } from '@unchainedshop/logger';
2
+ import { generateDbFilterById, generateDbMutations, buildSortOptions } from '@unchainedshop/utils';
3
+ import os from 'os';
4
+ import { WorkQueueCollection } from '../db/WorkQueueCollection';
5
+ import { WorkQueueSchema } from '../db/WorkQueueSchema';
6
+ import { DIRECTOR_MARKED_FAILED_ERROR, WorkerDirector } from '../director/WorkerDirector';
7
+ import { WorkerEventTypes } from '../director/WorkerEventTypes';
8
+ import { WorkStatus } from '../director/WorkStatus';
9
+ const { UNCHAINED_WORKER_ID = os.hostname() } = process.env;
10
+ const buildQuerySelector = ({ created, scheduled, types, status, workId, queryString, ...rest }) => {
11
+ const filterMap = {
12
+ [WorkStatus.DELETED]: { deleted: { $exists: true } },
13
+ [WorkStatus.NEW]: {
14
+ started: { $exists: false },
15
+ deleted: { $exists: false },
16
+ },
17
+ [WorkStatus.ALLOCATED]: {
18
+ started: { $exists: true },
19
+ finished: { $exists: false },
20
+ deleted: { $exists: false },
21
+ },
22
+ [WorkStatus.SUCCESS]: {
23
+ finished: { $exists: true },
24
+ success: true,
25
+ deleted: { $exists: false },
26
+ },
27
+ [WorkStatus.FAILED]: {
28
+ finished: { $exists: true },
29
+ success: false,
30
+ deleted: { $exists: false },
31
+ },
32
+ };
33
+ const statusQuery = {
34
+ $or: Object.entries(filterMap).reduce((acc, [key, filter]) => (status?.includes(key) ? [...acc, filter] : acc), []),
35
+ };
36
+ let query = statusQuery.$or.length > 0 ? statusQuery : { deleted: { $exists: false } };
37
+ if (created) {
38
+ query.created = created?.end
39
+ ? { $gte: created.start || new Date(0), $lte: created.end }
40
+ : { $gte: created.start || new Date(0) };
41
+ }
42
+ if (scheduled) {
43
+ query.scheduled = scheduled?.end
44
+ ? { $gte: scheduled.start || new Date(0), $lte: scheduled.end }
45
+ : { $gte: scheduled.start || new Date(0) };
46
+ }
47
+ if (types) {
48
+ query.type = { $in: types };
49
+ }
50
+ if (workId) {
51
+ query = generateDbFilterById(workId, query);
52
+ }
53
+ if (queryString)
54
+ query.$text = { $search: queryString };
55
+ return { ...query, ...rest };
56
+ };
57
+ const defaultSort = [
58
+ { key: 'started', value: 'DESC' },
59
+ { key: 'priority', value: 'DESC' },
60
+ { key: 'originalWorkId', value: 'ASC' },
61
+ { key: 'created', value: 'ASC' },
62
+ ];
63
+ export const configureWorkerModule = async ({ db, }) => {
64
+ const WorkQueue = await WorkQueueCollection(db);
65
+ const mutations = generateDbMutations(WorkQueue, WorkQueueSchema);
66
+ const finishWork = async (workId, { error, finished = new Date(), result, started = new Date(), success, worker = UNCHAINED_WORKER_ID, }, userId) => {
67
+ const workBeforeUpdate = await WorkQueue.findOne(buildQuerySelector({ workId, status: [WorkStatus.ALLOCATED] }));
68
+ if (!workBeforeUpdate)
69
+ return null;
70
+ await mutations.update(workId, {
71
+ $set: {
72
+ finished,
73
+ success,
74
+ error,
75
+ result,
76
+ ...(!workBeforeUpdate.started ? { started } : {}),
77
+ worker,
78
+ },
79
+ }, userId);
80
+ const work = await WorkQueue.findOne(generateDbFilterById(workId), {});
81
+ log(`Finished work ${workId}`, {
82
+ level: LogLevel.Verbose,
83
+ work,
84
+ });
85
+ WorkerDirector.events.emit(WorkerEventTypes.FINISHED, { work, userId });
86
+ return work;
87
+ };
88
+ return {
89
+ // Queries
90
+ activeWorkTypes: async () => {
91
+ const typeList = await WorkQueue.aggregate([{ $group: { _id: '$type' } }]).toArray();
92
+ return typeList
93
+ .map((t) => t._id)
94
+ .filter((type) => {
95
+ return WorkerDirector.getActivePluginTypes().includes(type);
96
+ });
97
+ },
98
+ findWork: async ({ workId, originalWorkId }) => WorkQueue.findOne(workId ? generateDbFilterById(workId) : { originalWorkId }, {}),
99
+ findWorkQueue: async ({ limit, skip, sort, ...selectorOptions }) => {
100
+ const selector = buildQuerySelector(selectorOptions);
101
+ const workQueues = WorkQueue.find(selector, {
102
+ skip,
103
+ limit,
104
+ sort: buildSortOptions(sort),
105
+ });
106
+ return workQueues.toArray();
107
+ },
108
+ count: async (query) => {
109
+ return WorkQueue.countDocuments(buildQuerySelector(query));
110
+ },
111
+ workExists: async ({ workId, originalWorkId }) => {
112
+ const queueCount = await WorkQueue.countDocuments(workId ? generateDbFilterById(workId) : { originalWorkId }, { limit: 1 });
113
+ return !!queueCount;
114
+ },
115
+ // Transformations
116
+ type: (work) => {
117
+ if (WorkerDirector.getActivePluginTypes().includes(work.type)) {
118
+ return work.type;
119
+ }
120
+ return 'UNKNOWN';
121
+ },
122
+ status: (work) => {
123
+ if (work.deleted) {
124
+ return WorkStatus.DELETED;
125
+ }
126
+ if (!work.started && !work.finished) {
127
+ return WorkStatus.NEW;
128
+ }
129
+ if (work.started && !work.finished) {
130
+ return WorkStatus.ALLOCATED;
131
+ }
132
+ if (work.started && work.finished && work.success) {
133
+ return WorkStatus.SUCCESS;
134
+ }
135
+ if (work.started && work.finished && !work.success) {
136
+ return WorkStatus.FAILED;
137
+ }
138
+ log('Unexpected work status', { level: LogLevel.Warning });
139
+ throw new Error('Unexpected work status');
140
+ },
141
+ // Mutations
142
+ addWork: async ({ type, input, priority = 0, scheduled, originalWorkId, retries = 20 }, userId) => {
143
+ if (!WorkerDirector.getAdapter(type)) {
144
+ throw new Error(`No plugin registered for type ${type}`);
145
+ }
146
+ const created = new Date();
147
+ const workId = await mutations.create({
148
+ type,
149
+ input,
150
+ priority,
151
+ scheduled: scheduled || created,
152
+ originalWorkId,
153
+ retries,
154
+ created,
155
+ }, userId);
156
+ log(`WorkerDirector -> Work added ${workId} (${type} / ${scheduled || created} / ${retries})`, {
157
+ userId,
158
+ });
159
+ const work = await WorkQueue.findOne(generateDbFilterById(workId), {});
160
+ WorkerDirector.events.emit(WorkerEventTypes.ADDED, { work, userId });
161
+ return work;
162
+ },
163
+ rescheduleWork: async (currentWork, scheduled, { userId }) => {
164
+ await mutations.update(currentWork._id, {
165
+ $set: {
166
+ scheduled,
167
+ },
168
+ }, userId);
169
+ const work = await WorkQueue.findOne(generateDbFilterById(currentWork._id), {});
170
+ WorkerDirector.events.emit(WorkerEventTypes.RESCHEDULED, {
171
+ work,
172
+ oldScheduled: currentWork.scheduled,
173
+ userId,
174
+ });
175
+ return work;
176
+ },
177
+ allocateWork: async ({ types, worker = UNCHAINED_WORKER_ID }) => {
178
+ // Find a work item that is scheduled for now and is not started.
179
+ // Also:
180
+ // - Restrict by types and worker if provided
181
+ // - Sort by default queue order
182
+ const query = buildQuerySelector({
183
+ status: [WorkStatus.NEW],
184
+ scheduled: { end: new Date() },
185
+ worker: { $in: [null, worker] },
186
+ ...(types ? { type: { $in: types } } : {}),
187
+ });
188
+ const result = await WorkQueue.findOneAndUpdate(query, {
189
+ $set: { started: new Date(), worker },
190
+ }, { sort: buildSortOptions(defaultSort), returnDocument: 'after' });
191
+ WorkerDirector.events.emit(WorkerEventTypes.ALLOCATED, {
192
+ work: result.value,
193
+ });
194
+ return result.value;
195
+ },
196
+ ensureOneWork: async ({ type, input, priority = 0, scheduled, originalWorkId, retries = 20 }) => {
197
+ const created = new Date();
198
+ const query = buildQuerySelector({
199
+ type,
200
+ status: [WorkStatus.NEW],
201
+ });
202
+ try {
203
+ const workId = `${type}:${scheduled.getTime()}`;
204
+ const result = await WorkQueue.findOneAndUpdate(query, {
205
+ $set: {
206
+ input,
207
+ priority,
208
+ worker: null,
209
+ updated: created,
210
+ },
211
+ $setOnInsert: {
212
+ _id: workId,
213
+ type,
214
+ originalWorkId,
215
+ scheduled,
216
+ retries,
217
+ created,
218
+ },
219
+ }, {
220
+ sort: buildSortOptions(defaultSort),
221
+ returnDocument: 'after',
222
+ upsert: true,
223
+ });
224
+ if (!result.lastErrorObject.updatedExisting) {
225
+ log(`WorkerDirector -> Work added again (ensure) ${type} ${scheduled} ${retries}`);
226
+ WorkerDirector.events.emit(WorkerEventTypes.ADDED, {
227
+ work: result.value,
228
+ });
229
+ }
230
+ return result.value;
231
+ }
232
+ catch (e) {
233
+ return null;
234
+ }
235
+ },
236
+ doWork: (work, requestContext) => {
237
+ return WorkerDirector.doWork(work, requestContext);
238
+ },
239
+ finishWork,
240
+ deleteWork: async (workId, userId) => {
241
+ const workBeforeRemoval = await WorkQueue.findOne(buildQuerySelector({
242
+ workId,
243
+ status: [WorkStatus.NEW, WorkStatus.ALLOCATED],
244
+ }));
245
+ if (!workBeforeRemoval)
246
+ return null;
247
+ await mutations.delete(workId, userId);
248
+ const work = await WorkQueue.findOne(generateDbFilterById(workId), {});
249
+ WorkerDirector.events.emit(WorkerEventTypes.DELETED, { work, userId });
250
+ return work;
251
+ },
252
+ markOldWorkAsFailed: async ({ types, worker, referenceDate }, userId) => {
253
+ const workQueue = await WorkQueue.find(buildQuerySelector({
254
+ status: [WorkStatus.ALLOCATED],
255
+ started: { $lte: referenceDate },
256
+ worker,
257
+ type: { $in: types },
258
+ }), { type: 1, sort: { test: 1 } }).toArray();
259
+ return Promise.all(workQueue.map(({ _id }) => finishWork(_id, {
260
+ finished: new Date(),
261
+ result: null,
262
+ success: false,
263
+ error: {
264
+ name: DIRECTOR_MARKED_FAILED_ERROR,
265
+ message: 'Director marked old work as failed after restart. This work was eventually running at the moment when node.js exited.',
266
+ },
267
+ worker,
268
+ }, userId)));
269
+ },
270
+ };
271
+ };
272
+ //# sourceMappingURL=configureWorkerModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configureWorkerModule.js","sourceRoot":"","sources":["../../src/module/configureWorkerModule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { IScheduler } from '@unchainedshop/types/worker';
2
+ export declare const FailedRescheduler: IScheduler;
@@ -0,0 +1,38 @@
1
+ import { log } from '@unchainedshop/logger';
2
+ import { WorkerDirector } from '../director/WorkerDirector';
3
+ import { WorkerEventTypes } from '../director/WorkerEventTypes';
4
+ export const FailedRescheduler = {
5
+ key: 'shop.unchained.scheduler.failed',
6
+ label: 'Reschedule failed works',
7
+ version: '1.0',
8
+ actions: (requestContext) => {
9
+ const handleFinishedWork = ({ work, userId }) => {
10
+ if (!work.success && work.retries > 0) {
11
+ const now = new Date();
12
+ const workDelayMs = work.scheduled.getTime() - work.created.getTime();
13
+ // In short: Double the delay of the old work or delay for 5 seconds
14
+ const scheduled = workDelayMs > 1000
15
+ ? new Date(now.getTime() + workDelayMs * 2)
16
+ : new Date(now.setSeconds(now.getSeconds() + 5));
17
+ log(`${FailedRescheduler.key} -> Reschedule failed work ${work._id} ${work.type} for ${scheduled} (in ${Math.round(workDelayMs / 1000)}). Remaining retries: ${work.retries}`);
18
+ requestContext.modules.worker.addWork({
19
+ type: work.type,
20
+ input: work.input,
21
+ priority: work.priority,
22
+ originalWorkId: work.originalWorkId || work._id,
23
+ retries: work.retries - 1,
24
+ scheduled,
25
+ }, userId);
26
+ }
27
+ };
28
+ return {
29
+ start() {
30
+ WorkerDirector.events.on(WorkerEventTypes.FINISHED, handleFinishedWork);
31
+ },
32
+ stop() {
33
+ WorkerDirector.events.off(WorkerEventTypes.FINISHED, handleFinishedWork);
34
+ },
35
+ };
36
+ },
37
+ };
38
+ //# sourceMappingURL=FailedRescheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FailedRescheduler.js","sourceRoot":"","sources":["../../src/schedulers/FailedRescheduler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,MAAM,CAAC,MAAM,iBAAiB,GAAe;IAC3C,GAAG,EAAE,iCAAiC;IACtC,KAAK,EAAE,yBAAyB;IAChC,OAAO,EAAE,KAAK;IAEd,OAAO,EAAE,CAAC,cAAc,EAAE,EAAE;QAC1B,MAAM,kBAAkB,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAkC,EAAE,EAAE;YAC9E,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE;gBACrC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBAEtE,oEAAoE;gBACpE,MAAM,SAAS,GACb,WAAW,GAAG,IAAI;oBAChB,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,WAAW,GAAG,CAAC,CAAC;oBAC3C,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;gBAErD,GAAG,CACD,GAAG,iBAAiB,CAAC,GAAG,8BAA8B,IAAI,CAAC,GAAG,IAC5D,IAAI,CAAC,IACP,QAAQ,SAAS,QAAQ,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,yBAAyB,IAAI,CAAC,OAAO,EAAE,CAC/F,CAAC;gBAEF,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CACnC;oBACE,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,GAAG;oBAC/C,OAAO,EAAE,IAAI,CAAC,OAAO,GAAG,CAAC;oBACzB,SAAS;iBACV,EACD,MAAM,CACP,CAAC;aACH;QACH,CAAC,CAAC;QAEF,OAAO;YACL,KAAK;gBACH,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;YAC1E,CAAC;YAED,IAAI;gBACF,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;YAC3E,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { configureWorkerModule } from './module/configureWorkerModule';
2
+ export { WorkerDirector } from './director/WorkerDirector';
3
+ export { WorkerAdapter } from './director/WorkerAdapter';
4
+ export { WorkStatus } from './director/WorkStatus';
5
+ export { WorkerEventTypes } from './director/WorkerEventTypes';
6
+ export { FailedRescheduler } from './schedulers/FailedRescheduler';
7
+ export { BaseWorker } from './workers/BaseWorker';
8
+ export { EventListenerWorker } from './workers/EventListenerWorker';
9
+ export { IntervalWorker } from './workers/IntervalWorker';
@@ -0,0 +1,10 @@
1
+ export { configureWorkerModule } from './module/configureWorkerModule';
2
+ export { WorkerDirector } from './director/WorkerDirector';
3
+ export { WorkerAdapter } from './director/WorkerAdapter';
4
+ export { WorkStatus } from './director/WorkStatus';
5
+ export { WorkerEventTypes } from './director/WorkerEventTypes';
6
+ export { FailedRescheduler } from './schedulers/FailedRescheduler';
7
+ export { BaseWorker } from './workers/BaseWorker';
8
+ export { EventListenerWorker } from './workers/EventListenerWorker';
9
+ export { IntervalWorker } from './workers/IntervalWorker';
10
+ //# sourceMappingURL=worker-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-index.js","sourceRoot":"","sources":["../src/worker-index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAEvE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAE/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { IWorker } from '@unchainedshop/types/worker';
2
+ interface WorkerParams {
3
+ workerId: string;
4
+ worker: IWorker<any>;
5
+ }
6
+ export declare const BaseWorker: IWorker<WorkerParams>;
7
+ export {};
@@ -0,0 +1,85 @@
1
+ import later from 'later';
2
+ import { log } from '@unchainedshop/logger';
3
+ import os from 'os';
4
+ import { WorkerDirector } from '../director/WorkerDirector';
5
+ const { UNCHAINED_WORKER_ID } = process.env;
6
+ const resolveWorkerId = (customWorkerId, type) => customWorkerId || UNCHAINED_WORKER_ID || `${os.hostname()}:${type}`;
7
+ export const BaseWorker = {
8
+ key: 'shop.unchained.worker.base',
9
+ label: 'Base worker. Do not use this directly.',
10
+ version: '1.0',
11
+ type: 'BASE',
12
+ getFloorDate: (date = new Date()) => {
13
+ const floored = new Date(Math.floor(date.getTime() / 1000) * 1000);
14
+ return floored;
15
+ },
16
+ getInternalTypes() {
17
+ return WorkerDirector.getActivePluginTypes().filter((type) => type !== 'EXTERNAL');
18
+ },
19
+ actions: ({ workerId, worker }, requestContext) => {
20
+ const resolvedWorkerId = resolveWorkerId(workerId, worker.type);
21
+ log(`${worker.key} -> Initialized: ${resolvedWorkerId}`);
22
+ const workerActions = {
23
+ start() {
24
+ throw new Error(`Not implemented on ${this.constructor.key}`);
25
+ },
26
+ stop() {
27
+ throw new Error(`Not implemented on ${this.constructor.key}`);
28
+ },
29
+ reset: async (referenceDate = new Date()) => {
30
+ await requestContext.modules.worker.markOldWorkAsFailed({
31
+ types: BaseWorker.getInternalTypes(),
32
+ worker: workerId,
33
+ referenceDate,
34
+ });
35
+ },
36
+ autorescheduleTypes: async ({ referenceDate }) => {
37
+ return Promise.all(WorkerDirector.getAutoSchedules().map(async ([type, work]) => {
38
+ const { schedule, input, ...rest } = work;
39
+ const fixedSchedule = { ...schedule };
40
+ fixedSchedule.schedules[0].s = [0]; // ignore seconds, always run on second 0
41
+ const nextDate = later.schedule(fixedSchedule).next(1, referenceDate);
42
+ nextDate.setMilliseconds(0);
43
+ return requestContext.modules.worker.ensureOneWork({
44
+ type,
45
+ input: input(),
46
+ scheduled: nextDate,
47
+ worker: workerId,
48
+ ...rest,
49
+ retries: 0,
50
+ });
51
+ }));
52
+ },
53
+ process: async ({ maxWorkItemCount, referenceDate }) => {
54
+ await workerActions.autorescheduleTypes({
55
+ referenceDate,
56
+ });
57
+ const processRecursively = async (recursionCounter = 0) => {
58
+ if (maxWorkItemCount && maxWorkItemCount < recursionCounter)
59
+ return null;
60
+ const work = await requestContext.modules.worker.allocateWork({
61
+ types: BaseWorker.getInternalTypes(),
62
+ worker: workerId,
63
+ });
64
+ let doneWork = null;
65
+ if (work) {
66
+ const output = await requestContext.modules.worker.doWork(work, requestContext);
67
+ doneWork = await requestContext.modules.worker.finishWork(work._id, {
68
+ ...output,
69
+ finished: work.finished || new Date(),
70
+ started: work.started,
71
+ worker: workerId,
72
+ }, '0');
73
+ }
74
+ if (doneWork)
75
+ return processRecursively(recursionCounter + 1);
76
+ return null;
77
+ };
78
+ await processRecursively();
79
+ },
80
+ };
81
+ workerActions.reset();
82
+ return workerActions;
83
+ },
84
+ };
85
+ //# sourceMappingURL=BaseWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseWorker.js","sourceRoot":"","sources":["../../src/workers/BaseWorker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { IWorker } from '@unchainedshop/types/worker';
2
+ export declare const EventListenerWorker: IWorker<{
3
+ workerId: string;
4
+ }>;
@@ -0,0 +1,44 @@
1
+ import { WorkerEventTypes } from '../director/WorkerEventTypes';
2
+ import { WorkerDirector } from '../director/WorkerDirector';
3
+ import { BaseWorker } from './BaseWorker';
4
+ export const EventListenerWorker = {
5
+ ...BaseWorker,
6
+ key: 'shop.unchained.worker.event-listener',
7
+ label: 'Allocates work on events. This worker does not make sense on multiple containers.',
8
+ version: '1.0',
9
+ type: 'EVENT_LISTENER',
10
+ actions: ({ workerId }, requestContext) => {
11
+ let onAdded;
12
+ let onFinished;
13
+ const baseWorkerActions = BaseWorker.actions({ workerId, worker: EventListenerWorker }, requestContext);
14
+ return {
15
+ ...baseWorkerActions,
16
+ start() {
17
+ onAdded = async () => {
18
+ await baseWorkerActions.process({
19
+ maxWorkItemCount: 0,
20
+ referenceDate: EventListenerWorker.getFloorDate(),
21
+ });
22
+ };
23
+ onFinished = async () => {
24
+ await baseWorkerActions.process({
25
+ maxWorkItemCount: 0,
26
+ referenceDate: EventListenerWorker.getFloorDate(),
27
+ });
28
+ };
29
+ WorkerDirector.events.on(WorkerEventTypes.ADDED, onAdded);
30
+ WorkerDirector.events.on(WorkerEventTypes.FINISHED, onFinished);
31
+ setTimeout(async () => {
32
+ await baseWorkerActions.autorescheduleTypes({
33
+ referenceDate: EventListenerWorker.getFloorDate(),
34
+ });
35
+ }, 300);
36
+ },
37
+ stop() {
38
+ WorkerDirector.events.off(WorkerEventTypes.ADDED, onAdded);
39
+ WorkerDirector.events.off(WorkerEventTypes.FINISHED, onFinished);
40
+ },
41
+ };
42
+ },
43
+ };
44
+ //# sourceMappingURL=EventListenerWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventListenerWorker.js","sourceRoot":"","sources":["../../src/workers/EventListenerWorker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,CAAC,MAAM,mBAAmB,GAAkC;IAChE,GAAG,UAAU;IAEb,GAAG,EAAE,sCAAsC;IAC3C,KAAK,EAAE,mFAAmF;IAC1F,OAAO,EAAE,KAAK;IACd,IAAI,EAAE,gBAAgB;IAEtB,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,cAAc,EAAE,EAAE;QACxC,IAAI,OAA4B,CAAC;QACjC,IAAI,UAA+B,CAAC;QAEpC,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,CAC1C,EAAE,QAAQ,EAAE,MAAM,EAAE,mBAAmB,EAAE,EACzC,cAAc,CACf,CAAC;QACF,OAAO;YACL,GAAG,iBAAiB;YAEpB,KAAK;gBACH,OAAO,GAAG,KAAK,IAAI,EAAE;oBACnB,MAAM,iBAAiB,CAAC,OAAO,CAAC;wBAC9B,gBAAgB,EAAE,CAAC;wBACnB,aAAa,EAAE,mBAAmB,CAAC,YAAY,EAAE;qBAClD,CAAC,CAAC;gBACL,CAAC,CAAC;gBACF,UAAU,GAAG,KAAK,IAAI,EAAE;oBACtB,MAAM,iBAAiB,CAAC,OAAO,CAAC;wBAC9B,gBAAgB,EAAE,CAAC;wBACnB,aAAa,EAAE,mBAAmB,CAAC,YAAY,EAAE;qBAClD,CAAC,CAAC;gBACL,CAAC,CAAC;gBAEF,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBAC1D,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;gBAEhE,UAAU,CAAC,KAAK,IAAI,EAAE;oBACpB,MAAM,iBAAiB,CAAC,mBAAmB,CAAC;wBAC1C,aAAa,EAAE,mBAAmB,CAAC,YAAY,EAAE;qBAClD,CAAC,CAAC;gBACL,CAAC,EAAE,GAAG,CAAC,CAAC;YACV,CAAC;YAED,IAAI;gBACF,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBAC3D,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YACnE,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { IWorker, WorkerSchedule } from '@unchainedshop/types/worker';
2
+ interface WorkerParams {
3
+ workerId: string;
4
+ batchCount?: number;
5
+ schedule: WorkerSchedule | string;
6
+ }
7
+ export declare const scheduleToInterval: (scheduleRaw: WorkerSchedule | string) => number;
8
+ export declare const IntervalWorker: IWorker<WorkerParams>;
9
+ export {};
@@ -0,0 +1,38 @@
1
+ import later from 'later';
2
+ import { BaseWorker } from './BaseWorker';
3
+ const { NODE_ENV } = process.env;
4
+ const defaultSchedule = later.parse.text(NODE_ENV !== 'production' ? 'every 2 seconds' : 'every 30 seconds');
5
+ export const scheduleToInterval = (scheduleRaw) => {
6
+ const referenceDate = new Date(1000);
7
+ const schedule = typeof scheduleRaw === 'string' ? later.parse.text(scheduleRaw) : scheduleRaw;
8
+ const [one, two] = later.schedule(schedule).next(2, referenceDate);
9
+ const diff = new Date(two).getTime() - new Date(one).getTime();
10
+ return Math.min(1000 * 60 * 60, diff); // at least once every hour!
11
+ };
12
+ export const IntervalWorker = {
13
+ ...BaseWorker,
14
+ key: 'shop.unchained.worker.cron',
15
+ label: 'Allocates work on fixed intervals with native node setInterval',
16
+ version: '1.0',
17
+ type: 'CRON',
18
+ actions: ({ workerId, batchCount = 0, schedule = defaultSchedule }, requestContext) => {
19
+ const baseWorkerActions = BaseWorker.actions({ workerId, worker: IntervalWorker }, requestContext);
20
+ const intervalDelay = scheduleToInterval(schedule);
21
+ let intervalHandle;
22
+ return {
23
+ ...baseWorkerActions,
24
+ start() {
25
+ intervalHandle = setInterval(() => {
26
+ baseWorkerActions.process({
27
+ maxWorkItemCount: batchCount,
28
+ referenceDate: IntervalWorker.getFloorDate(),
29
+ });
30
+ }, intervalDelay);
31
+ },
32
+ stop() {
33
+ clearInterval(intervalHandle);
34
+ },
35
+ };
36
+ },
37
+ };
38
+ //# sourceMappingURL=IntervalWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntervalWorker.js","sourceRoot":"","sources":["../../src/workers/IntervalWorker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;AAQjC,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CACtC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,CACjD,CAAC;AAEpB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,WAAoC,EAAE,EAAE;IACzE,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;IAC/F,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,4BAA4B;AACrE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAA0B;IACnD,GAAG,UAAU;IAEb,GAAG,EAAE,4BAA4B;IACjC,KAAK,EAAE,gEAAgE;IACvE,OAAO,EAAE,KAAK;IACd,IAAI,EAAE,MAAM;IAEZ,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,GAAG,CAAC,EAAE,QAAQ,GAAG,eAAe,EAAE,EAAE,cAAuB,EAAE,EAAE;QAC7F,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,cAAc,CAAC,CAAC;QAEnG,MAAM,aAAa,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,cAA4B,CAAC;QAEjC,OAAO;YACL,GAAG,iBAAiB;YAEpB,KAAK;gBACH,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;oBAChC,iBAAiB,CAAC,OAAO,CAAC;wBACxB,gBAAgB,EAAE,UAAU;wBAC5B,aAAa,EAAE,cAAc,CAAC,YAAY,EAAE;qBAC7C,CAAC,CAAC;gBACL,CAAC,EAAE,aAAa,CAAC,CAAC;YACpB,CAAC;YAED,IAAI;gBACF,aAAa,CAAC,cAAc,CAAC,CAAC;YAChC,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
package/package.js ADDED
@@ -0,0 +1,33 @@
1
+ Package.describe({
2
+ name: 'unchained:core-worker',
3
+ version: '1.1.3',
4
+ summary: 'Unchained Engine Core: Worker',
5
+ git: 'https://github.com/unchainedshop/unchained',
6
+ documentation: 'README.md',
7
+ });
8
+
9
+ Npm.depends({
10
+ later: '1.2.0',
11
+ 'simpl-schema': '1.12.0',
12
+ '@unchainedshop/logger': '1.1.3',
13
+ '@unchainedshop/utils': '1.1.3',
14
+ });
15
+
16
+ Package.onUse((api) => {
17
+ api.versionsFrom('2.7.3');
18
+ api.use('ecmascript');
19
+ api.use('typescript');
20
+
21
+ api.mainModule('src/worker-index.ts', 'server');
22
+ });
23
+
24
+ Package.onTest((api) => {
25
+ api.use('meteortesting:mocha');
26
+ api.use('ecmascript');
27
+ api.use('typescript');
28
+
29
+ api.use('unchained:mongodb');
30
+ api.use('unchained:core-worker');
31
+
32
+ api.mainModule('tests/worker-index.test.ts');
33
+ });