mongodash 2.0.0 → 2.1.0
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 +40 -22
- package/dist/dashboard/index.html +40 -0
- package/dist/lib/playground/server.js +131 -0
- package/dist/lib/playground/server.js.map +1 -0
- package/dist/lib/src/ConcurrentRunner.js +148 -0
- package/dist/lib/src/ConcurrentRunner.js.map +1 -0
- package/dist/lib/{OnError.js → src/OnError.js} +3 -3
- package/dist/lib/src/OnError.js.map +1 -0
- package/dist/lib/{OnInfo.js → src/OnInfo.js} +6 -3
- package/dist/lib/src/OnInfo.js.map +1 -0
- package/dist/lib/{createContinuousLock.js → src/createContinuousLock.js} +5 -3
- package/dist/lib/src/createContinuousLock.js.map +1 -0
- package/dist/lib/{cronTasks.js → src/cronTasks.js} +129 -73
- package/dist/lib/src/cronTasks.js.map +1 -0
- package/dist/lib/{getCollection.js → src/getCollection.js} +2 -2
- package/dist/lib/src/getCollection.js.map +1 -0
- package/dist/lib/{getMongoClient.js → src/getMongoClient.js} +2 -2
- package/dist/lib/src/getMongoClient.js.map +1 -0
- package/dist/lib/src/globalsCollection.js +10 -0
- package/dist/lib/src/globalsCollection.js.map +1 -0
- package/dist/lib/src/index.js +101 -0
- package/dist/lib/src/index.js.map +1 -0
- package/dist/lib/{initPromise.js → src/initPromise.js} +2 -3
- package/dist/lib/src/initPromise.js.map +1 -0
- package/dist/lib/src/mongoCompatibility.js +10 -0
- package/dist/lib/src/mongoCompatibility.js.map +1 -0
- package/dist/lib/src/parseInterval.js +60 -0
- package/dist/lib/src/parseInterval.js.map +1 -0
- package/dist/lib/src/prefixFilterKeys.js +69 -0
- package/dist/lib/src/prefixFilterKeys.js.map +1 -0
- package/dist/lib/src/processInBatches.js +46 -0
- package/dist/lib/src/processInBatches.js.map +1 -0
- package/dist/lib/src/reactiveTasks/LeaderElector.js +155 -0
- package/dist/lib/src/reactiveTasks/LeaderElector.js.map +1 -0
- package/dist/lib/src/reactiveTasks/MetricsCollector.js +410 -0
- package/dist/lib/src/reactiveTasks/MetricsCollector.js.map +1 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskManager.js +288 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskManager.js.map +1 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskOps.js +185 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskOps.js.map +1 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskPlanner.js +443 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskPlanner.js.map +1 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskReconciler.js +218 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskReconciler.js.map +1 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskRegistry.js +184 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskRegistry.js.map +1 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskRepository.js +355 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskRepository.js.map +1 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskRetryStrategy.js +153 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskRetryStrategy.js.map +1 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskTypes.js +34 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskTypes.js.map +1 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskWorker.js +186 -0
- package/dist/lib/src/reactiveTasks/ReactiveTaskWorker.js.map +1 -0
- package/dist/lib/src/reactiveTasks/compileWatchProjection.js +65 -0
- package/dist/lib/src/reactiveTasks/compileWatchProjection.js.map +1 -0
- package/dist/lib/src/reactiveTasks/index.js +298 -0
- package/dist/lib/src/reactiveTasks/index.js.map +1 -0
- package/dist/lib/src/reactiveTasks/queryToExpression.js +160 -0
- package/dist/lib/src/reactiveTasks/queryToExpression.js.map +1 -0
- package/dist/lib/src/reactiveTasks/validateTaskFilter.js +88 -0
- package/dist/lib/src/reactiveTasks/validateTaskFilter.js.map +1 -0
- package/dist/lib/src/task-management/OperationalTaskController.js +162 -0
- package/dist/lib/src/task-management/OperationalTaskController.js.map +1 -0
- package/dist/lib/src/task-management/index.js +27 -0
- package/dist/lib/src/task-management/index.js.map +1 -0
- package/dist/lib/src/task-management/serveDashboard.js +149 -0
- package/dist/lib/src/task-management/serveDashboard.js.map +1 -0
- package/dist/lib/src/task-management/types.js +10 -0
- package/dist/lib/src/task-management/types.js.map +1 -0
- package/dist/lib/{withLock.js → src/withLock.js} +3 -4
- package/dist/lib/src/withLock.js.map +1 -0
- package/dist/lib/{withTransaction.js → src/withTransaction.js} +4 -4
- package/dist/lib/src/withTransaction.js.map +1 -0
- package/dist/lib/tools/check-db-connection.js +28 -0
- package/dist/lib/tools/check-db-connection.js.map +1 -0
- package/dist/lib/tools/clean-testing-databases.js +12 -0
- package/dist/lib/tools/clean-testing-databases.js.map +1 -0
- package/dist/lib/tools/prepare-republish.js +27 -0
- package/dist/lib/tools/prepare-republish.js.map +1 -0
- package/dist/lib/tools/test-matrix-local.js +212 -0
- package/dist/lib/tools/test-matrix-local.js.map +1 -0
- package/dist/lib/tools/testingDatabase.js +55 -0
- package/dist/lib/tools/testingDatabase.js.map +1 -0
- package/dist/types/playground/server.d.ts +1 -0
- package/dist/types/src/ConcurrentRunner.d.ts +30 -0
- package/dist/types/{OnInfo.d.ts → src/OnInfo.d.ts} +1 -1
- package/dist/types/{cronTasks.d.ts → src/cronTasks.d.ts} +44 -1
- package/dist/types/src/globalsCollection.d.ts +4 -0
- package/dist/types/src/index.d.ts +28 -0
- package/dist/types/src/mongoCompatibility.d.ts +29 -0
- package/dist/types/src/parseInterval.d.ts +12 -0
- package/dist/types/src/prefixFilterKeys.d.ts +11 -0
- package/dist/types/src/processInBatches.d.ts +10 -0
- package/dist/types/src/reactiveTasks/LeaderElector.d.ts +42 -0
- package/dist/types/src/reactiveTasks/MetricsCollector.d.ts +73 -0
- package/dist/types/src/reactiveTasks/ReactiveTaskManager.d.ts +18 -0
- package/dist/types/src/reactiveTasks/ReactiveTaskOps.d.ts +17 -0
- package/dist/types/src/reactiveTasks/ReactiveTaskPlanner.d.ts +62 -0
- package/dist/types/src/reactiveTasks/ReactiveTaskReconciler.d.ts +29 -0
- package/dist/types/src/reactiveTasks/ReactiveTaskRegistry.d.ts +34 -0
- package/dist/types/src/reactiveTasks/ReactiveTaskRepository.d.ts +59 -0
- package/dist/types/src/reactiveTasks/ReactiveTaskRetryStrategy.d.ts +21 -0
- package/dist/types/src/reactiveTasks/ReactiveTaskTypes.d.ts +389 -0
- package/dist/types/src/reactiveTasks/ReactiveTaskWorker.d.ts +36 -0
- package/dist/types/src/reactiveTasks/compileWatchProjection.d.ts +12 -0
- package/dist/types/src/reactiveTasks/index.d.ts +82 -0
- package/dist/types/src/reactiveTasks/queryToExpression.d.ts +13 -0
- package/dist/types/src/reactiveTasks/validateTaskFilter.d.ts +10 -0
- package/dist/types/src/task-management/OperationalTaskController.d.ts +59 -0
- package/dist/types/src/task-management/index.d.ts +3 -0
- package/dist/types/src/task-management/serveDashboard.d.ts +12 -0
- package/dist/types/src/task-management/types.d.ts +95 -0
- package/dist/types/tools/check-db-connection.d.ts +2 -0
- package/dist/types/tools/clean-testing-databases.d.ts +1 -0
- package/dist/types/tools/prepare-republish.d.ts +2 -0
- package/dist/types/tools/test-matrix-local.d.ts +1 -0
- package/dist/types/tools/testingDatabase.d.ts +2 -0
- package/docs/.vitepress/cache/deps/_metadata.json +31 -0
- package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js +12824 -0
- package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js.map +7 -0
- package/docs/.vitepress/cache/deps/package.json +3 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4505 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +9731 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
- package/docs/.vitepress/cache/deps/vue.js +347 -0
- package/docs/.vitepress/cache/deps/vue.js.map +7 -0
- package/docs/.vitepress/config.mts +48 -0
- package/docs/.vitepress/theme/index.ts +4 -0
- package/docs/.vitepress/theme/style.css +16 -0
- package/docs/assets/dashboard.png +0 -0
- package/docs/cron-tasks.md +172 -0
- package/docs/dashboard.md +117 -0
- package/docs/getters.md +31 -0
- package/docs/getting-started.md +120 -0
- package/docs/index.md +29 -0
- package/docs/initialization.md +59 -0
- package/docs/process-in-batches.md +73 -0
- package/docs/reactive-tasks.md +914 -0
- package/docs/with-lock.md +45 -0
- package/docs/with-transaction.md +65 -0
- package/grafana/reactive_tasks.json +765 -0
- package/package.json +127 -116
- package/dist/lib/OnError.js.map +0 -1
- package/dist/lib/OnInfo.js.map +0 -1
- package/dist/lib/createContinuousLock.js.map +0 -1
- package/dist/lib/cronTasks.js.map +0 -1
- package/dist/lib/getCollection.js.map +0 -1
- package/dist/lib/getMongoClient.js.map +0 -1
- package/dist/lib/index.js +0 -64
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/initPromise.js.map +0 -1
- package/dist/lib/withLock.js.map +0 -1
- package/dist/lib/withTransaction.js.map +0 -1
- package/dist/types/index.d.ts +0 -17
- /package/dist/types/{OnError.d.ts → src/OnError.d.ts} +0 -0
- /package/dist/types/{createContinuousLock.d.ts → src/createContinuousLock.d.ts} +0 -0
- /package/dist/types/{getCollection.d.ts → src/getCollection.d.ts} +0 -0
- /package/dist/types/{getMongoClient.d.ts → src/getMongoClient.d.ts} +0 -0
- /package/dist/types/{initPromise.d.ts → src/initPromise.d.ts} +0 -0
- /package/dist/types/{withLock.d.ts → src/withLock.d.ts} +0 -0
- /package/dist/types/{withTransaction.d.ts → src/withTransaction.d.ts} +0 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**!
|
|
2
|
+
* mongodash v2.1.0
|
|
3
|
+
* git+https://github.com/VaclavObornik/mongodash.git
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2025 Václav Oborník
|
|
6
|
+
* Released under the MIT license
|
|
7
|
+
*/
|
|
8
|
+
"use strict";
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ReactiveTaskReconciler = void 0;
|
|
11
|
+
const _debug = require("debug");
|
|
12
|
+
const processInBatches_1 = require("../processInBatches");
|
|
13
|
+
const withLock_1 = require("../withLock");
|
|
14
|
+
const ReactiveTaskTypes_1 = require("./ReactiveTaskTypes");
|
|
15
|
+
const debug = _debug('mongodash:reactiveTasks:reconciler');
|
|
16
|
+
/**
|
|
17
|
+
* Responsible for reconciling reactive tasks when the Change Stream history is lost or on startup.
|
|
18
|
+
*
|
|
19
|
+
* Responsibilities:
|
|
20
|
+
* - Scans source collections to identify tasks that should exist.
|
|
21
|
+
* - Uses `ReactiveTaskOps` to generate and execute task operations.
|
|
22
|
+
* - Tracks reconciliation status in the meta document.
|
|
23
|
+
*/
|
|
24
|
+
class ReactiveTaskReconciler {
|
|
25
|
+
constructor(instanceId, globalsCollection, registry, ops, onInfo, internalOptions) {
|
|
26
|
+
this.instanceId = instanceId;
|
|
27
|
+
this.globalsCollection = globalsCollection;
|
|
28
|
+
this.registry = registry;
|
|
29
|
+
this.ops = ops;
|
|
30
|
+
this.onInfo = onInfo;
|
|
31
|
+
this.internalOptions = internalOptions;
|
|
32
|
+
this.nextCleanupTime = null;
|
|
33
|
+
}
|
|
34
|
+
async reconcile(shouldStop) {
|
|
35
|
+
var _a, _b;
|
|
36
|
+
debug(`[Scheduler ${this.instanceId}] Reconciliation started.`);
|
|
37
|
+
this.onInfo({
|
|
38
|
+
message: `Reconciliation started.`,
|
|
39
|
+
code: ReactiveTaskTypes_1.CODE_REACTIVE_TASK_PLANNER_RECONCILIATION_STARTED,
|
|
40
|
+
taskCount: this.registry.getAllTasks().length,
|
|
41
|
+
});
|
|
42
|
+
const metaDoc = (await this.globalsCollection.findOne({ _id: ReactiveTaskTypes_1.REACTIVE_TASK_META_DOC_ID }));
|
|
43
|
+
debug(`[Scheduler ${this.instanceId}] Meta doc loaded:`, metaDoc);
|
|
44
|
+
// Iterate over all tasks and reconcile
|
|
45
|
+
const taskEntries = this.registry.getAllEntries();
|
|
46
|
+
for (const entry of taskEntries) {
|
|
47
|
+
if (shouldStop()) {
|
|
48
|
+
debug(`[Scheduler ${this.instanceId}] Reconciliation stopped.`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
debug(`[Scheduler ${this.instanceId}] Reconciling collection: ${entry.tasksCollection.collectionName}`);
|
|
52
|
+
// Filter tasks that need reconciliation
|
|
53
|
+
const tasksToReconcile = new Set();
|
|
54
|
+
for (const task of entry.tasks.values()) {
|
|
55
|
+
if (!((_a = metaDoc === null || metaDoc === void 0 ? void 0 : metaDoc.reconciliation) === null || _a === void 0 ? void 0 : _a[task.task])) {
|
|
56
|
+
tasksToReconcile.add(task.task);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
debug(`[Scheduler ${this.instanceId}] Task ${task.task} is already reconciled. Skipping.`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (tasksToReconcile.size === 0) {
|
|
63
|
+
debug(`[Scheduler ${this.instanceId}] No tasks to reconcile for collection ${entry.tasksCollection.collectionName}.`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
debug(`[Scheduler ${this.instanceId}] Checks for existing reconciliation state for collection: ${entry.tasksCollection.collectionName}`);
|
|
67
|
+
// Check for existing checkpoint
|
|
68
|
+
const collectionName = entry.sourceCollection.collectionName;
|
|
69
|
+
const checkpoint = (_b = metaDoc === null || metaDoc === void 0 ? void 0 : metaDoc.reconciliationState) === null || _b === void 0 ? void 0 : _b[collectionName];
|
|
70
|
+
let lastId = null;
|
|
71
|
+
let resume = false;
|
|
72
|
+
if (checkpoint) {
|
|
73
|
+
// Validate if the set of tasks matches
|
|
74
|
+
// We must ensure that the tasks currently needing reconciliation are the subset of what was being reconciled
|
|
75
|
+
// ACTUALLY: The checkpoint stores the set of tasks that WERE being reconciled.
|
|
76
|
+
// If the current set `tasksToReconcile` is DIFFERENT from `checkpoint.taskNames`, we cannot guarantee consistency.
|
|
77
|
+
// Example: We were reconciling "A" and "B". Now we need to reconcile "A", "B", "C". We must start over to include "C" for the already processed range.
|
|
78
|
+
// Example 2: We were reconciling "A". Now we need "A" and "B". Start over.
|
|
79
|
+
// Example 3: We were reconciling "A" and "B". Now we need only "A". Technically we could resume, but for safety/simplicity, we restart if sets enforce strict equality.
|
|
80
|
+
const savedTasksSet = new Set(checkpoint.taskNames);
|
|
81
|
+
const currentTasksSet = tasksToReconcile;
|
|
82
|
+
const areSetsEqual = savedTasksSet.size === currentTasksSet.size && [...savedTasksSet].every((t) => currentTasksSet.has(t));
|
|
83
|
+
if (areSetsEqual) {
|
|
84
|
+
debug(`[Scheduler ${this.instanceId}] Resuming reconciliation for ${collectionName} from id: ${checkpoint.lastId}`);
|
|
85
|
+
lastId = checkpoint.lastId;
|
|
86
|
+
resume = true;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
debug(`[Scheduler ${this.instanceId}] Reconciliation checkpoint invalid (task definitions changed). Restarting.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const pipeline = [
|
|
93
|
+
{ $sort: { _id: 1 } }, // Ensure stable sort for checkpointing
|
|
94
|
+
{ $project: { _id: 1 } },
|
|
95
|
+
];
|
|
96
|
+
if (resume && lastId) {
|
|
97
|
+
pipeline.unshift({ $match: { _id: { $gt: lastId } } });
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
// Use processInBatches to iterate over source documents and trigger planning for batches
|
|
101
|
+
await (0, processInBatches_1.processInBatches)(entry.sourceCollection, pipeline, (doc) => doc._id, // Transform to ID
|
|
102
|
+
async (ids) => {
|
|
103
|
+
await this.ops.executePlanningPipeline(entry.tasksCollection.collectionName, ids, tasksToReconcile);
|
|
104
|
+
// Update Checkpoint
|
|
105
|
+
if (ids.length > 0) {
|
|
106
|
+
const lastProcessedId = ids[ids.length - 1];
|
|
107
|
+
const updatePath = `reconciliationState.${collectionName}`;
|
|
108
|
+
await this.globalsCollection.updateOne({ _id: ReactiveTaskTypes_1.REACTIVE_TASK_META_DOC_ID }, {
|
|
109
|
+
$set: {
|
|
110
|
+
[`${updatePath}.lastId`]: lastProcessedId,
|
|
111
|
+
[`${updatePath}.taskNames`]: Array.from(tasksToReconcile),
|
|
112
|
+
[`${updatePath}.updatedAt`]: new Date(),
|
|
113
|
+
},
|
|
114
|
+
}, { upsert: true });
|
|
115
|
+
}
|
|
116
|
+
}, {
|
|
117
|
+
batchSize: this.internalOptions.batchSize,
|
|
118
|
+
shouldStop,
|
|
119
|
+
});
|
|
120
|
+
if (shouldStop()) {
|
|
121
|
+
debug(`[Scheduler ${this.instanceId}] Reconciliation stopped during processing. Checkpoint preserved.`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
debug(`[Scheduler ${this.instanceId}] Reconciled collection: ${entry.tasksCollection.collectionName}`);
|
|
125
|
+
// Mark processed tasks as reconciled AND remove checkpoint
|
|
126
|
+
const update = {
|
|
127
|
+
$unset: {
|
|
128
|
+
[`reconciliationState.${collectionName}`]: '',
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
for (const taskName of tasksToReconcile) {
|
|
132
|
+
const taskDef = entry.tasks.get(taskName);
|
|
133
|
+
if (taskDef) {
|
|
134
|
+
await entry.repository.deleteOrphanedTasks(taskName, entry.sourceCollection.collectionName, taskDef.filter || {}, taskDef.cleanupPolicyParsed, shouldStop);
|
|
135
|
+
}
|
|
136
|
+
update.$set = update.$set || {};
|
|
137
|
+
update.$set[`reconciliation.${taskName}`] = true;
|
|
138
|
+
}
|
|
139
|
+
await this.globalsCollection.updateOne({ _id: ReactiveTaskTypes_1.REACTIVE_TASK_META_DOC_ID }, update, { upsert: true });
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
debug(`[Scheduler ${this.instanceId}] Error reconciling collection: ${entry.tasksCollection.collectionName}`, error);
|
|
143
|
+
// Continue with other collections
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
debug(`[Scheduler ${this.instanceId}] Reconciliation complete.`);
|
|
147
|
+
try {
|
|
148
|
+
await this.globalsCollection.updateOne({ _id: ReactiveTaskTypes_1.REACTIVE_TASK_META_DOC_ID }, { $set: { lastReconciledAt: new Date() } }, { upsert: true });
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
// Ignore error, metrics are best-effort
|
|
152
|
+
debug(`[Scheduler ${this.instanceId}] Failed to update reconciliation timestamp`, e);
|
|
153
|
+
}
|
|
154
|
+
this.onInfo({
|
|
155
|
+
message: `Reconciliation complete.`,
|
|
156
|
+
code: ReactiveTaskTypes_1.CODE_REACTIVE_TASK_PLANNER_RECONCILIATION_FINISHED,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async markAsUnreconciled(taskNames) {
|
|
160
|
+
if (taskNames.length === 0)
|
|
161
|
+
return;
|
|
162
|
+
const update = { $unset: {} };
|
|
163
|
+
for (const task of taskNames) {
|
|
164
|
+
update.$unset[`reconciliation.${task}`] = '';
|
|
165
|
+
}
|
|
166
|
+
await this.globalsCollection.updateOne({ _id: ReactiveTaskTypes_1.REACTIVE_TASK_META_DOC_ID }, update, { upsert: true });
|
|
167
|
+
}
|
|
168
|
+
async performPeriodicCleanup(shouldStop) {
|
|
169
|
+
const now = Date.now();
|
|
170
|
+
// Fast path: check in-memory cache first to avoid database query
|
|
171
|
+
if (this.nextCleanupTime !== null && now < this.nextCleanupTime) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Slow path: query database to get accurate lastCleanupAt
|
|
175
|
+
const metaDoc = (await this.globalsCollection.findOne({ _id: ReactiveTaskTypes_1.REACTIVE_TASK_META_DOC_ID }));
|
|
176
|
+
const lastCleanupDate = (metaDoc === null || metaDoc === void 0 ? void 0 : metaDoc.lastCleanupAt) ? new Date(metaDoc.lastCleanupAt) : undefined;
|
|
177
|
+
// Calculate next run time based on last run
|
|
178
|
+
const nextRun = this.internalOptions.getNextCleanupDate(lastCleanupDate);
|
|
179
|
+
this.nextCleanupTime = nextRun.getTime();
|
|
180
|
+
if (now < nextRun.getTime()) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Acquire lock to prevent parallel runs during deployment transitions
|
|
184
|
+
const lockKey = `${ReactiveTaskTypes_1.REACTIVE_TASK_META_DOC_ID}:cleanup`;
|
|
185
|
+
try {
|
|
186
|
+
await (0, withLock_1.withLock)(lockKey, async () => {
|
|
187
|
+
// Double-check after acquiring lock
|
|
188
|
+
const freshMetaDoc = (await this.globalsCollection.findOne({ _id: ReactiveTaskTypes_1.REACTIVE_TASK_META_DOC_ID }));
|
|
189
|
+
const freshLastCleanupDate = (freshMetaDoc === null || freshMetaDoc === void 0 ? void 0 : freshMetaDoc.lastCleanupAt) ? new Date(freshMetaDoc.lastCleanupAt) : undefined;
|
|
190
|
+
const freshNextRun = this.internalOptions.getNextCleanupDate(freshLastCleanupDate);
|
|
191
|
+
if (now < freshNextRun.getTime()) {
|
|
192
|
+
this.nextCleanupTime = freshNextRun.getTime();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
debug(`[Scheduler ${this.instanceId}] Starting periodic cleanup of orphaned tasks.`);
|
|
196
|
+
const entries = this.registry.getAllEntries();
|
|
197
|
+
for (const entry of entries) {
|
|
198
|
+
if (shouldStop())
|
|
199
|
+
return;
|
|
200
|
+
for (const task of entry.tasks.values()) {
|
|
201
|
+
await entry.repository.deleteOrphanedTasks(task.task, entry.sourceCollection.collectionName, task.filter || {}, task.cleanupPolicyParsed, shouldStop);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Update lastCleanupAt in meta document
|
|
205
|
+
const cleanupTime = new Date();
|
|
206
|
+
await this.globalsCollection.updateOne({ _id: ReactiveTaskTypes_1.REACTIVE_TASK_META_DOC_ID }, { $set: { lastCleanupAt: cleanupTime } }, { upsert: true });
|
|
207
|
+
// Update in-memory cache with next cleanup time
|
|
208
|
+
this.nextCleanupTime = this.internalOptions.getNextCleanupDate(cleanupTime).getTime();
|
|
209
|
+
}, { maxWaitForLock: 1000, expireIn: 5 * 60 * 1000 });
|
|
210
|
+
}
|
|
211
|
+
catch (_a) {
|
|
212
|
+
// Lock already acquired by another process, skip this run
|
|
213
|
+
debug(`[Scheduler ${this.instanceId}] Cleanup skipped - lock already acquired`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
exports.ReactiveTaskReconciler = ReactiveTaskReconciler;
|
|
218
|
+
//# sourceMappingURL=ReactiveTaskReconciler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactiveTaskReconciler.js","sourceRoot":"","sources":["../../../../src/reactiveTasks/ReactiveTaskReconciler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**!
|
|
2
|
+
* mongodash v2.1.0
|
|
3
|
+
* git+https://github.com/VaclavObornik/mongodash.git
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2025 Václav Oborník
|
|
6
|
+
* Released under the MIT license
|
|
7
|
+
*/
|
|
8
|
+
"use strict";
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ReactiveTaskRegistry = void 0;
|
|
11
|
+
const _debug = require("debug");
|
|
12
|
+
const getCollection_1 = require("../getCollection");
|
|
13
|
+
const OnError_1 = require("../OnError");
|
|
14
|
+
const OnInfo_1 = require("../OnInfo");
|
|
15
|
+
const compileWatchProjection_1 = require("./compileWatchProjection");
|
|
16
|
+
const ReactiveTaskRepository_1 = require("./ReactiveTaskRepository");
|
|
17
|
+
const ReactiveTaskRetryStrategy_1 = require("./ReactiveTaskRetryStrategy");
|
|
18
|
+
const ReactiveTaskTypes_1 = require("./ReactiveTaskTypes");
|
|
19
|
+
const validateTaskFilter_1 = require("./validateTaskFilter");
|
|
20
|
+
const debug = _debug('mongodash:ReactiveTaskRegistry');
|
|
21
|
+
const { Duration } = require('@sapphire/duration');
|
|
22
|
+
/**
|
|
23
|
+
* Registry for managing reactive task definitions and their associated collections.
|
|
24
|
+
*
|
|
25
|
+
* Responsibilities:
|
|
26
|
+
* - Stores task definitions and maps them to source and task collections.
|
|
27
|
+
* - Initializes the `ReactiveTaskRepository` for each task collection.
|
|
28
|
+
* - Provides lookup methods to retrieve tasks by name or collection.
|
|
29
|
+
* - Ensures task uniqueness and validates configuration.
|
|
30
|
+
*/
|
|
31
|
+
class ReactiveTaskRegistry {
|
|
32
|
+
constructor(onInfo = OnInfo_1.defaultOnInfo, onError = OnError_1.defaultOnError) {
|
|
33
|
+
this.map = new Map();
|
|
34
|
+
this.tasksByIdentifiers = new Map();
|
|
35
|
+
this.onInfo = onInfo;
|
|
36
|
+
this.onError = onError;
|
|
37
|
+
}
|
|
38
|
+
setCallbacks(onInfo, onError) {
|
|
39
|
+
this.onInfo = onInfo;
|
|
40
|
+
this.onError = onError;
|
|
41
|
+
}
|
|
42
|
+
async addTask(taskDef) {
|
|
43
|
+
var _a, _b, _c;
|
|
44
|
+
if (this.tasksByIdentifiers.has(taskDef.task)) {
|
|
45
|
+
throw new Error(`Task with name '${taskDef.task}' already exists.`);
|
|
46
|
+
}
|
|
47
|
+
const normalizedFilter = (0, validateTaskFilter_1.normalizeTaskFilter)(taskDef.filter, taskDef.task);
|
|
48
|
+
try {
|
|
49
|
+
(0, compileWatchProjection_1.compileWatchProjection)(taskDef.watchProjection);
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
throw new Error(`Task '${taskDef.task}': ${e.message}`);
|
|
53
|
+
}
|
|
54
|
+
// Parse debounce (Fail Fast)
|
|
55
|
+
let debounceMs = 1000;
|
|
56
|
+
if (taskDef.debounce !== undefined) {
|
|
57
|
+
if (typeof taskDef.debounce === 'number') {
|
|
58
|
+
if (taskDef.debounce < 0) {
|
|
59
|
+
throw new Error(`Task '${taskDef.task}': 'debounce' must be a non-negative number.`);
|
|
60
|
+
}
|
|
61
|
+
debounceMs = taskDef.debounce;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const parsed = new Duration(taskDef.debounce).offset;
|
|
65
|
+
if (parsed === null || parsed === undefined || parsed < 0 || Number.isNaN(parsed)) {
|
|
66
|
+
throw new Error(`Task '${taskDef.task}': Invalid duration format for 'debounce': '${taskDef.debounce}'. Use formats like '100ms', '1s', '5m'.`);
|
|
67
|
+
}
|
|
68
|
+
debounceMs = parsed;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (taskDef.evolution) {
|
|
72
|
+
this.validateEvolutionConfig(taskDef.task, taskDef.evolution);
|
|
73
|
+
}
|
|
74
|
+
const sourceCollectionName = typeof taskDef.collection === 'string' ? taskDef.collection : taskDef.collection.collectionName;
|
|
75
|
+
const tasksCollectionName = sourceCollectionName + '_tasks';
|
|
76
|
+
if (!this.map.has(sourceCollectionName)) {
|
|
77
|
+
const tasksCollection = (0, getCollection_1.getCollection)(tasksCollectionName);
|
|
78
|
+
const repository = new ReactiveTaskRepository_1.ReactiveTaskRepository(tasksCollection, this.onInfo, this.onError);
|
|
79
|
+
const entry = {
|
|
80
|
+
sourceCollection: (0, getCollection_1.getCollection)(sourceCollectionName),
|
|
81
|
+
tasks: new Map(),
|
|
82
|
+
tasksCollection,
|
|
83
|
+
repository,
|
|
84
|
+
};
|
|
85
|
+
// store in both directions for easy lookup
|
|
86
|
+
this.map.set(sourceCollectionName, entry);
|
|
87
|
+
this.map.set(tasksCollectionName, entry);
|
|
88
|
+
}
|
|
89
|
+
const collectionsMapEntry = this.map.get(sourceCollectionName);
|
|
90
|
+
// Determine Retry Policy with defaults
|
|
91
|
+
let retryPolicy = taskDef.retryPolicy;
|
|
92
|
+
if (!retryPolicy) {
|
|
93
|
+
// Default policy: Exponential backoff
|
|
94
|
+
retryPolicy = {
|
|
95
|
+
type: 'exponential',
|
|
96
|
+
min: '10s',
|
|
97
|
+
max: '1d',
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (retryPolicy.resetRetriesOnDataChange === undefined) {
|
|
101
|
+
retryPolicy.resetRetriesOnDataChange = true;
|
|
102
|
+
}
|
|
103
|
+
if (retryPolicy.maxAttempts === undefined && !retryPolicy.maxDuration) {
|
|
104
|
+
retryPolicy.maxAttempts = 5;
|
|
105
|
+
}
|
|
106
|
+
// Parse cleanupPolicy
|
|
107
|
+
const policy = taskDef.cleanupPolicy;
|
|
108
|
+
const deleteWhen = (_a = policy === null || policy === void 0 ? void 0 : policy.deleteWhen) !== null && _a !== void 0 ? _a : 'sourceDocumentDeleted';
|
|
109
|
+
const keepForInput = (_b = policy === null || policy === void 0 ? void 0 : policy.keepFor) !== null && _b !== void 0 ? _b : '24h';
|
|
110
|
+
let keepForMs;
|
|
111
|
+
if (typeof keepForInput === 'number') {
|
|
112
|
+
keepForMs = keepForInput;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const parsed = new Duration(keepForInput).offset;
|
|
116
|
+
if (parsed === null || parsed === undefined || parsed < 0 || Number.isNaN(parsed)) {
|
|
117
|
+
throw new Error(`Task '${taskDef.task}': Invalid duration format for 'cleanupPolicy.keepFor': '${keepForInput}'. Use formats like '100ms', '1s', '5m', '1h', '24h'.`);
|
|
118
|
+
}
|
|
119
|
+
keepForMs = parsed;
|
|
120
|
+
}
|
|
121
|
+
const internalTask = Object.assign(Object.assign({}, taskDef), { filter: normalizedFilter, debounceMs, executionHistoryLimit: (_c = taskDef.executionHistoryLimit) !== null && _c !== void 0 ? _c : 5, retryStrategy: new ReactiveTaskRetryStrategy_1.ReactiveTaskRetryStrategy(retryPolicy), sourceCollection: collectionsMapEntry.sourceCollection, tasksCollection: collectionsMapEntry.tasksCollection, initPromise: collectionsMapEntry.repository.initPromise, cleanupPolicyParsed: {
|
|
122
|
+
deleteWhen,
|
|
123
|
+
keepForMs,
|
|
124
|
+
}, repository: collectionsMapEntry.repository });
|
|
125
|
+
collectionsMapEntry.tasks.set(taskDef.task, internalTask);
|
|
126
|
+
this.tasksByIdentifiers.set(taskDef.task, internalTask);
|
|
127
|
+
await internalTask.initPromise;
|
|
128
|
+
this.onInfo({
|
|
129
|
+
message: `Initialized ${taskDef.task}`,
|
|
130
|
+
code: ReactiveTaskTypes_1.CODE_REACTIVE_TASK_INITIALIZED,
|
|
131
|
+
task: taskDef.task,
|
|
132
|
+
sourceCollection: collectionsMapEntry.sourceCollection.collectionName,
|
|
133
|
+
tasksCollection: collectionsMapEntry.tasksCollection.collectionName,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
getTask(taskName) {
|
|
137
|
+
return this.tasksByIdentifiers.get(taskName);
|
|
138
|
+
}
|
|
139
|
+
getAllTasks() {
|
|
140
|
+
return Array.from(this.tasksByIdentifiers.values());
|
|
141
|
+
}
|
|
142
|
+
getEntry(collectionName) {
|
|
143
|
+
const entry = this.map.get(collectionName);
|
|
144
|
+
if (!entry) {
|
|
145
|
+
throw new Error(`Entry for collection '${collectionName}' not found.`);
|
|
146
|
+
}
|
|
147
|
+
return entry;
|
|
148
|
+
}
|
|
149
|
+
getAllEntries() {
|
|
150
|
+
// Filter out duplicates (map has entries for both source and tasks collection names)
|
|
151
|
+
// We can iterate over source collection names if we knew them, or filter by unique tasksCollection
|
|
152
|
+
const uniqueEntries = new Set();
|
|
153
|
+
for (const entry of this.map.values()) {
|
|
154
|
+
uniqueEntries.add(entry);
|
|
155
|
+
}
|
|
156
|
+
return Array.from(uniqueEntries);
|
|
157
|
+
}
|
|
158
|
+
validateEvolutionConfig(taskName, config) {
|
|
159
|
+
debug(`[DEBUG] Validating config for ${taskName}:`, config);
|
|
160
|
+
if (typeof config.handlerVersion !== 'undefined') {
|
|
161
|
+
if (typeof config.handlerVersion !== 'number' || config.handlerVersion < 0) {
|
|
162
|
+
debug(`[DEBUG] Validation failed for handlerVersion: ${config.handlerVersion}`);
|
|
163
|
+
throw new Error(`ReactiveTask [${taskName}]: evolution.handlerVersion must be a non-negative integer.`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (config.onHandlerVersionChange) {
|
|
167
|
+
if (!['none', 'reprocess_failed', 'reprocess_all'].includes(config.onHandlerVersionChange)) {
|
|
168
|
+
throw new Error(`ReactiveTask [${taskName}]: evolution.onHandlerVersionChange must be one of ['none', 'reprocess_failed', 'reprocess_all'].`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (typeof config.reconcileOnTriggerChange !== 'undefined') {
|
|
172
|
+
if (typeof config.reconcileOnTriggerChange !== 'boolean') {
|
|
173
|
+
throw new Error(`ReactiveTask [${taskName}]: evolution.reconcileOnTriggerChange must be a boolean.`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const knownKeys = ['handlerVersion', 'onHandlerVersionChange', 'reconcileOnTriggerChange'];
|
|
177
|
+
const unknownKeys = Object.keys(config).filter((k) => !knownKeys.includes(k));
|
|
178
|
+
if (unknownKeys.length > 0) {
|
|
179
|
+
throw new Error(`ReactiveTask [${taskName}]: Unknown keys in evolution config: ${unknownKeys.join(', ')}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.ReactiveTaskRegistry = ReactiveTaskRegistry;
|
|
184
|
+
//# sourceMappingURL=ReactiveTaskRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReactiveTaskRegistry.js","sourceRoot":"","sources":["../../../../src/reactiveTasks/ReactiveTaskRegistry.ts"],"names":[],"mappings":"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"}
|