@quave/agenda-mongodb 1.0.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/LICENSE.md +25 -0
- package/README.md +273 -0
- package/dist/MongoBackend.d.ts +58 -0
- package/dist/MongoBackend.js +82 -0
- package/dist/MongoBackend.js.map +1 -0
- package/dist/MongoChangeStreamNotificationChannel.d.ts +96 -0
- package/dist/MongoChangeStreamNotificationChannel.js +248 -0
- package/dist/MongoChangeStreamNotificationChannel.js.map +1 -0
- package/dist/MongoJobLogger.d.ts +46 -0
- package/dist/MongoJobLogger.js +142 -0
- package/dist/MongoJobLogger.js.map +1 -0
- package/dist/MongoJobRepository.d.ts +84 -0
- package/dist/MongoJobRepository.js +713 -0
- package/dist/MongoJobRepository.js.map +1 -0
- package/dist/cjs/MongoBackend.d.ts +58 -0
- package/dist/cjs/MongoBackend.js +86 -0
- package/dist/cjs/MongoBackend.js.map +1 -0
- package/dist/cjs/MongoChangeStreamNotificationChannel.d.ts +96 -0
- package/dist/cjs/MongoChangeStreamNotificationChannel.js +255 -0
- package/dist/cjs/MongoChangeStreamNotificationChannel.js.map +1 -0
- package/dist/cjs/MongoJobLogger.d.ts +46 -0
- package/dist/cjs/MongoJobLogger.js +149 -0
- package/dist/cjs/MongoJobLogger.js.map +1 -0
- package/dist/cjs/MongoJobRepository.d.ts +84 -0
- package/dist/cjs/MongoJobRepository.js +720 -0
- package/dist/cjs/MongoJobRepository.js.map +1 -0
- package/dist/cjs/hasMongoProtocol.d.ts +1 -0
- package/dist/cjs/hasMongoProtocol.js +6 -0
- package/dist/cjs/hasMongoProtocol.js.map +1 -0
- package/dist/cjs/index.d.ts +39 -0
- package/dist/cjs/index.js +46 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/testing.d.ts +25 -0
- package/dist/cjs/testing.js +55 -0
- package/dist/cjs/testing.js.map +1 -0
- package/dist/cjs/types.d.ts +52 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/hasMongoProtocol.d.ts +1 -0
- package/dist/hasMongoProtocol.js +2 -0
- package/dist/hasMongoProtocol.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/testing.d.ts +25 -0
- package/dist/testing.js +49 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +84 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
## License
|
|
2
|
+
(The MIT License)
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2013 Ryan Schmukler <ryan@slingingcode.com>
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2022-2026 Simon Tretter <s.tretter@gmail.com>
|
|
7
|
+
|
|
8
|
+
Contributors: Mikael Korpela, Alexis Tyler, Vasyl Boroviak, Haris Sulaiman, Loris Guignard
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
11
|
+
this software and associated documentation files (the 'Software'), to deal in
|
|
12
|
+
the Software without restriction, including without limitation the rights to
|
|
13
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
14
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
15
|
+
subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
22
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
23
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
24
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
25
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# @quave/agenda-mongodb
|
|
2
|
+
|
|
3
|
+
> Quave-managed fork of [@agendajs/mongo-backend](https://github.com/agenda/agenda) (forked at `4.0.2`, commit `b9c47688`). MongoDB backend for [`@quave/agenda`](../agenda).
|
|
4
|
+
|
|
5
|
+
## Runtime compatibility
|
|
6
|
+
|
|
7
|
+
- **mongodb driver**: `^4.17.0 || ^5.0.0 || ^6.0.0 || ^7.0.0` (peer dependency). Driver 4.17 is the line bundled with Meteor 2.x; the repository normalizes the `findOneAndUpdate` result-shape difference between driver 4/5 (ModifyResult) and 6/7 (document) automatically. Tests run against both driver 7.x and 4.17.
|
|
8
|
+
- **Node.js**: 14.13.1+ for the package itself (dual ESM + CommonJS builds, ES2020 target); your chosen mongodb driver sets its own floor (driver 6 needs Node 16.20+, driver 7 needs Node 20.19+).
|
|
9
|
+
- **Meteor 2.x**: supported — pass the app's existing driver-4 `Db` handle via `new MongoBackend({ mongo: db })`. CI runs a Meteor-style simulation (Node 14 + CommonJS + driver 4.17) on every push.
|
|
10
|
+
|
|
11
|
+
MongoDB backend for [Agenda](https://www.npmjs.com/package/agenda) job scheduler.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @quave/agenda @quave/agenda-mongodb
|
|
17
|
+
# or
|
|
18
|
+
pnpm add @quave/agenda @quave/agenda-mongodb
|
|
19
|
+
# or
|
|
20
|
+
yarn add @quave/agenda @quave/agenda-mongodb
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Requirements:**
|
|
24
|
+
- Node.js 18+
|
|
25
|
+
- MongoDB 4+
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { Agenda } from '@quave/agenda';
|
|
31
|
+
import { MongoBackend } from '@quave/agenda-mongodb';
|
|
32
|
+
|
|
33
|
+
// Via connection string
|
|
34
|
+
const agenda = new Agenda({
|
|
35
|
+
backend: new MongoBackend({
|
|
36
|
+
address: 'mongodb://localhost/agenda'
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Or via existing MongoDB connection
|
|
41
|
+
import { MongoClient } from 'mongodb';
|
|
42
|
+
|
|
43
|
+
const client = await MongoClient.connect('mongodb://localhost');
|
|
44
|
+
const db = client.db('agenda');
|
|
45
|
+
|
|
46
|
+
const agenda = new Agenda({
|
|
47
|
+
backend: new MongoBackend({ mongo: db })
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Define jobs
|
|
51
|
+
agenda.define('send-email', async (job) => {
|
|
52
|
+
const { to, subject } = job.attrs.data;
|
|
53
|
+
await sendEmail(to, subject);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Start processing
|
|
57
|
+
await agenda.start();
|
|
58
|
+
|
|
59
|
+
// Schedule jobs
|
|
60
|
+
await agenda.every('1 hour', 'send-email', { to: 'user@example.com', subject: 'Hello' });
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Configuration Options
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
interface IMongoBackendConfig {
|
|
67
|
+
/** MongoDB connection string */
|
|
68
|
+
address?: string;
|
|
69
|
+
|
|
70
|
+
/** Existing MongoDB database instance */
|
|
71
|
+
mongo?: Db;
|
|
72
|
+
|
|
73
|
+
/** Collection name for jobs (default: 'agendaJobs') */
|
|
74
|
+
collection?: string;
|
|
75
|
+
|
|
76
|
+
/** MongoDB client options */
|
|
77
|
+
options?: MongoClientOptions;
|
|
78
|
+
|
|
79
|
+
/** Name to identify this Agenda instance (stored as lastModifiedBy) */
|
|
80
|
+
name?: string;
|
|
81
|
+
|
|
82
|
+
/** Whether to create indexes on connect (default: true) */
|
|
83
|
+
ensureIndex?: boolean;
|
|
84
|
+
|
|
85
|
+
/** Sort order for job queries */
|
|
86
|
+
sort?: { [key: string]: SortDirection };
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Full Example with Options
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const agenda = new Agenda({
|
|
94
|
+
backend: new MongoBackend({
|
|
95
|
+
address: 'mongodb://localhost/agenda',
|
|
96
|
+
collection: 'jobs', // Custom collection name
|
|
97
|
+
sort: { nextRunAt: 'asc', priority: 'desc' }
|
|
98
|
+
}),
|
|
99
|
+
processEvery: '30 seconds', // Job polling interval
|
|
100
|
+
maxConcurrency: 20, // Max concurrent jobs
|
|
101
|
+
defaultConcurrency: 5 // Default per job type
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Real-Time Notifications
|
|
106
|
+
|
|
107
|
+
MongoBackend provides storage only and uses polling to check for new jobs. For real-time job processing across distributed systems, you have several options:
|
|
108
|
+
|
|
109
|
+
### Option 1: MongoDB Change Streams (Native)
|
|
110
|
+
|
|
111
|
+
If your MongoDB deployment is a replica set (even a single-node replica set works), you can use `MongoChangeStreamNotificationChannel` for native real-time notifications without any external dependencies:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { Agenda } from '@quave/agenda';
|
|
115
|
+
import { MongoBackend, MongoChangeStreamNotificationChannel } from '@quave/agenda-mongodb';
|
|
116
|
+
|
|
117
|
+
const agenda = new Agenda({
|
|
118
|
+
backend: new MongoBackend({ mongo: db }),
|
|
119
|
+
notificationChannel: new MongoChangeStreamNotificationChannel({ db })
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Jobs are processed immediately when created (no polling delay)
|
|
123
|
+
await agenda.start();
|
|
124
|
+
await agenda.now('myJob'); // Triggers instant processing
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Requirements:**
|
|
128
|
+
- MongoDB replica set (single-node replica sets work fine)
|
|
129
|
+
- WiredTiger storage engine (default since MongoDB 3.2)
|
|
130
|
+
|
|
131
|
+
**Configuration options:**
|
|
132
|
+
```typescript
|
|
133
|
+
const channel = new MongoChangeStreamNotificationChannel({
|
|
134
|
+
db: mongoDb, // Required: MongoDB database instance
|
|
135
|
+
collection: 'agendaJobs', // Optional: collection name (default: 'agendaJobs')
|
|
136
|
+
resumeToken: savedToken, // Optional: resume from specific point
|
|
137
|
+
fullDocument: true // Optional: include full document on updates (default: true)
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Option 2: In-Memory (Single Process)
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { Agenda, InMemoryNotificationChannel } from '@quave/agenda';
|
|
145
|
+
import { MongoBackend } from '@quave/agenda-mongodb';
|
|
146
|
+
|
|
147
|
+
// For single-process apps (testing/development)
|
|
148
|
+
const agenda = new Agenda({
|
|
149
|
+
backend: new MongoBackend({ mongo: db }),
|
|
150
|
+
notificationChannel: new InMemoryNotificationChannel()
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Option 3: Redis Pub/Sub (Multi-Process)
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { Agenda } from '@quave/agenda';
|
|
158
|
+
import { MongoBackend } from '@quave/agenda-mongodb';
|
|
159
|
+
import { RedisBackend } from '@agendajs/redis-backend';
|
|
160
|
+
|
|
161
|
+
// For production: use Redis for notifications
|
|
162
|
+
const redisBackend = new RedisBackend({ connectionString: 'redis://localhost:6379' });
|
|
163
|
+
const agenda = new Agenda({
|
|
164
|
+
backend: new MongoBackend({ mongo: db }),
|
|
165
|
+
notificationChannel: redisBackend.notificationChannel
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Database Index
|
|
170
|
+
|
|
171
|
+
By default, MongoBackend automatically creates the `findAndLockNextJobIndex` index on connect for optimal job query performance. The index includes:
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
{
|
|
175
|
+
"name": 1,
|
|
176
|
+
"nextRunAt": 1,
|
|
177
|
+
"priority": -1,
|
|
178
|
+
"lockedAt": 1,
|
|
179
|
+
"disabled": 1
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
To disable automatic index creation (e.g., if you manage indexes separately):
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const backend = new MongoBackend({
|
|
187
|
+
mongo: db,
|
|
188
|
+
ensureIndex: false // Skip automatic index creation
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Additional Indexes
|
|
193
|
+
|
|
194
|
+
If you use **Agendash**, add this index for better dashboard performance:
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
db.agendaJobs.createIndex({
|
|
198
|
+
"nextRunAt": -1,
|
|
199
|
+
"lastRunAt": -1,
|
|
200
|
+
"lastFinishedAt": -1
|
|
201
|
+
}, { name: "agendash" })
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
If you have job definitions with thousands of instances, this index can improve lock queries:
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
db.agendaJobs.createIndex({
|
|
208
|
+
"name": 1,
|
|
209
|
+
"disabled": 1,
|
|
210
|
+
"lockedAt": 1
|
|
211
|
+
}, { name: "findAndLockDeadJobs" })
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Job Logging
|
|
215
|
+
|
|
216
|
+
MongoBackend includes a built-in `MongoJobLogger` that stores structured job lifecycle events in a dedicated MongoDB collection (`agenda_logs` by default). The logger is lightweight and only creates its collection on first use.
|
|
217
|
+
|
|
218
|
+
### Automatic Usage
|
|
219
|
+
|
|
220
|
+
When you enable logging in Agenda, the backend's built-in logger is used automatically:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
const agenda = new Agenda({
|
|
224
|
+
backend: new MongoBackend({ mongo: db }),
|
|
225
|
+
logging: true // Uses MongoJobLogger automatically
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Standalone Usage
|
|
230
|
+
|
|
231
|
+
You can also use `MongoJobLogger` independently — for example, to log to MongoDB while using a different backend for storage:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { MongoJobLogger } from '@quave/agenda-mongodb';
|
|
235
|
+
|
|
236
|
+
const logger = new MongoJobLogger({
|
|
237
|
+
mongo: db, // Required: MongoDB database instance
|
|
238
|
+
logCollection: 'agenda_logs' // Optional: collection name (default: 'agenda_logs')
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Use with any backend
|
|
242
|
+
import { RedisBackend } from '@agendajs/redis-backend';
|
|
243
|
+
const agenda = new Agenda({
|
|
244
|
+
backend: new RedisBackend({ connectionString: 'redis://...' }),
|
|
245
|
+
logging: logger
|
|
246
|
+
});
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Querying Logs
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
const { entries, total } = await agenda.getLogs({
|
|
253
|
+
jobName: 'send-email',
|
|
254
|
+
level: 'error',
|
|
255
|
+
limit: 50,
|
|
256
|
+
sort: 'desc'
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Clear old logs
|
|
260
|
+
await agenda.clearLogs({ to: new Date(Date.now() - 30 * 86400000) });
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
See the [Agenda README](https://github.com/agenda/agenda#persistent-job-logging) for full logging documentation.
|
|
264
|
+
|
|
265
|
+
## Related Packages
|
|
266
|
+
|
|
267
|
+
- [agenda](https://www.npmjs.com/package/agenda) - Core scheduler
|
|
268
|
+
- [@agendajs/postgres-backend](https://www.npmjs.com/package/@agendajs/postgres-backend) - PostgreSQL backend with LISTEN/NOTIFY
|
|
269
|
+
- [@agendajs/redis-backend](https://www.npmjs.com/package/@agendajs/redis-backend) - Redis backend with Pub/Sub
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
MIT
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { AgendaBackend, JobRepository, NotificationChannel, JobLogger } from '@quave/agenda';
|
|
2
|
+
import type { MongoBackendConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* MongoDB backend for Agenda.
|
|
5
|
+
* Provides storage via MongoDB. Does not provide notifications (uses polling).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { Agenda } from '@quave/agenda';
|
|
10
|
+
* import { MongoBackend } from '@quave/agenda-mongodb';
|
|
11
|
+
*
|
|
12
|
+
* // Via connection string
|
|
13
|
+
* const backend = new MongoBackend({ address: 'mongodb://localhost/agenda' });
|
|
14
|
+
*
|
|
15
|
+
* // Via existing connection
|
|
16
|
+
* const backend = new MongoBackend({ mongo: existingDb });
|
|
17
|
+
*
|
|
18
|
+
* // With persistent job event logging
|
|
19
|
+
* const backend = new MongoBackend({ mongo: existingDb, logging: true });
|
|
20
|
+
*
|
|
21
|
+
* const agenda = new Agenda({ backend, logging: true });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare class MongoBackend implements AgendaBackend {
|
|
25
|
+
private config;
|
|
26
|
+
readonly name = "MongoDB";
|
|
27
|
+
private _repository;
|
|
28
|
+
private _ownsConnection;
|
|
29
|
+
private _logger;
|
|
30
|
+
/**
|
|
31
|
+
* MongoDB does not provide a notification channel.
|
|
32
|
+
* For real-time notifications with MongoDB, use a separate notification
|
|
33
|
+
* channel like Redis pub/sub.
|
|
34
|
+
*/
|
|
35
|
+
readonly notificationChannel: NotificationChannel | undefined;
|
|
36
|
+
constructor(config: MongoBackendConfig);
|
|
37
|
+
get repository(): JobRepository;
|
|
38
|
+
/**
|
|
39
|
+
* The job logger for persistent event logging.
|
|
40
|
+
* Always available; Agenda decides whether to activate it via its `logging` config.
|
|
41
|
+
*/
|
|
42
|
+
get logger(): JobLogger;
|
|
43
|
+
/**
|
|
44
|
+
* Whether this backend owns its database connection.
|
|
45
|
+
* True if created from address (connection string), false if mongo Db was passed in.
|
|
46
|
+
*/
|
|
47
|
+
get ownsConnection(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Connect to MongoDB and initialize the collection.
|
|
50
|
+
*/
|
|
51
|
+
connect(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Disconnect from MongoDB.
|
|
54
|
+
* Only closes the connection if we created it (i.e., from connection string).
|
|
55
|
+
* If an existing Db instance was passed in, this is a no-op.
|
|
56
|
+
*/
|
|
57
|
+
disconnect(): Promise<void>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { MongoJobRepository } from './MongoJobRepository.js';
|
|
2
|
+
import { MongoJobLogger } from './MongoJobLogger.js';
|
|
3
|
+
/**
|
|
4
|
+
* MongoDB backend for Agenda.
|
|
5
|
+
* Provides storage via MongoDB. Does not provide notifications (uses polling).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { Agenda } from '@quave/agenda';
|
|
10
|
+
* import { MongoBackend } from '@quave/agenda-mongodb';
|
|
11
|
+
*
|
|
12
|
+
* // Via connection string
|
|
13
|
+
* const backend = new MongoBackend({ address: 'mongodb://localhost/agenda' });
|
|
14
|
+
*
|
|
15
|
+
* // Via existing connection
|
|
16
|
+
* const backend = new MongoBackend({ mongo: existingDb });
|
|
17
|
+
*
|
|
18
|
+
* // With persistent job event logging
|
|
19
|
+
* const backend = new MongoBackend({ mongo: existingDb, logging: true });
|
|
20
|
+
*
|
|
21
|
+
* const agenda = new Agenda({ backend, logging: true });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export class MongoBackend {
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.name = 'MongoDB';
|
|
28
|
+
/**
|
|
29
|
+
* MongoDB does not provide a notification channel.
|
|
30
|
+
* For real-time notifications with MongoDB, use a separate notification
|
|
31
|
+
* channel like Redis pub/sub.
|
|
32
|
+
*/
|
|
33
|
+
this.notificationChannel = undefined;
|
|
34
|
+
// Determine if we own the connection (not passed in by user)
|
|
35
|
+
this._ownsConnection = !('mongo' in config);
|
|
36
|
+
this._repository = new MongoJobRepository({
|
|
37
|
+
...('mongo' in config
|
|
38
|
+
? { mongo: config.mongo, db: { collection: config.collection } }
|
|
39
|
+
: {
|
|
40
|
+
db: { address: config.address, collection: config.collection, options: config.options }
|
|
41
|
+
}),
|
|
42
|
+
ensureIndex: config.ensureIndex,
|
|
43
|
+
sort: config.sort
|
|
44
|
+
});
|
|
45
|
+
// Always create the logger (lightweight; only initializes on first use when Agenda activates it)
|
|
46
|
+
this._logger = new MongoJobLogger(config.logCollection);
|
|
47
|
+
}
|
|
48
|
+
get repository() {
|
|
49
|
+
return this._repository;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The job logger for persistent event logging.
|
|
53
|
+
* Always available; Agenda decides whether to activate it via its `logging` config.
|
|
54
|
+
*/
|
|
55
|
+
get logger() {
|
|
56
|
+
return this._logger;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Whether this backend owns its database connection.
|
|
60
|
+
* True if created from address (connection string), false if mongo Db was passed in.
|
|
61
|
+
*/
|
|
62
|
+
get ownsConnection() {
|
|
63
|
+
return this._ownsConnection;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Connect to MongoDB and initialize the collection.
|
|
67
|
+
*/
|
|
68
|
+
async connect() {
|
|
69
|
+
await this._repository.connect();
|
|
70
|
+
// Initialize the job logger with the shared database connection
|
|
71
|
+
await this._logger.setDb(this._repository.getDb());
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Disconnect from MongoDB.
|
|
75
|
+
* Only closes the connection if we created it (i.e., from connection string).
|
|
76
|
+
* If an existing Db instance was passed in, this is a no-op.
|
|
77
|
+
*/
|
|
78
|
+
async disconnect() {
|
|
79
|
+
await this._repository.disconnect();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=MongoBackend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoBackend.js","sourceRoot":"","sources":["../src/MongoBackend.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,YAAY;IAcxB,YAAoB,MAA0B;QAA1B,WAAM,GAAN,MAAM,CAAoB;QAbrC,SAAI,GAAG,SAAS,CAAC;QAM1B;;;;WAIG;QACM,wBAAmB,GAAoC,SAAS,CAAC;QAGzE,6DAA6D;QAC7D,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,WAAW,GAAG,IAAI,kBAAkB,CAAC;YACzC,GAAG,CAAC,OAAO,IAAI,MAAM;gBACpB,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE;gBAChE,CAAC,CAAC;oBACA,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE;iBACvF,CAAC;YACJ,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;SACjB,CAAC,CAAC;QAEH,iGAAiG;QACjG,IAAI,CAAC,OAAO,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,UAAU;QACb,OAAO,IAAI,CAAC,WAAW,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,eAAe,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACZ,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QAEjC,gEAAgE;QAChE,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU;QACf,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC;CACD"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Db, ResumeToken } from 'mongodb';
|
|
2
|
+
import type { JobNotification, NotificationChannelConfig } from '@quave/agenda';
|
|
3
|
+
import { BaseNotificationChannel } from '@quave/agenda';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for MongoChangeStreamNotificationChannel
|
|
6
|
+
*/
|
|
7
|
+
export interface MongoChangeStreamNotificationChannelConfig extends NotificationChannelConfig {
|
|
8
|
+
/**
|
|
9
|
+
* MongoDB database instance.
|
|
10
|
+
* Required - must be connected to a replica set.
|
|
11
|
+
*/
|
|
12
|
+
db?: Db;
|
|
13
|
+
/**
|
|
14
|
+
* Collection name to watch (default: 'agendaJobs')
|
|
15
|
+
*/
|
|
16
|
+
collection?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Resume token to resume watching from a specific point.
|
|
19
|
+
* Useful for recovering from disconnections.
|
|
20
|
+
*/
|
|
21
|
+
resumeToken?: ResumeToken;
|
|
22
|
+
/**
|
|
23
|
+
* Whether to use fullDocument: 'updateLookup' for update events.
|
|
24
|
+
* This fetches the full document on updates but adds latency.
|
|
25
|
+
* Default: true (required for proper notification data)
|
|
26
|
+
*/
|
|
27
|
+
fullDocument?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* MongoDB Change Stream notification channel.
|
|
31
|
+
*
|
|
32
|
+
* Uses MongoDB Change Streams to watch for job changes in real-time.
|
|
33
|
+
* This eliminates the need for polling and provides instant notifications
|
|
34
|
+
* when jobs are created or updated.
|
|
35
|
+
*
|
|
36
|
+
* **Requirements:**
|
|
37
|
+
* - MongoDB must be deployed as a replica set (even single-node replica sets work)
|
|
38
|
+
* - WiredTiger storage engine (default since MongoDB 3.2)
|
|
39
|
+
*
|
|
40
|
+
* **How it works:**
|
|
41
|
+
* - Watches the jobs collection for insert/update/replace operations
|
|
42
|
+
* - When a job with `nextRunAt` is modified, handlers are notified
|
|
43
|
+
* - The `publish()` method is a no-op since Change Streams automatically
|
|
44
|
+
* detect job changes from any source
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* import { Agenda } from '@quave/agenda';
|
|
49
|
+
* import { MongoBackend, MongoChangeStreamNotificationChannel } from '@quave/agenda-mongodb';
|
|
50
|
+
*
|
|
51
|
+
* const backend = new MongoBackend({ mongo: db });
|
|
52
|
+
* const channel = new MongoChangeStreamNotificationChannel({ db });
|
|
53
|
+
*
|
|
54
|
+
* const agenda = new Agenda({
|
|
55
|
+
* backend,
|
|
56
|
+
* notificationChannel: channel
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare class MongoChangeStreamNotificationChannel extends BaseNotificationChannel {
|
|
61
|
+
private db?;
|
|
62
|
+
private collectionName;
|
|
63
|
+
private collection?;
|
|
64
|
+
private changeStream?;
|
|
65
|
+
private resumeToken?;
|
|
66
|
+
private useFullDocument;
|
|
67
|
+
constructor(config?: MongoChangeStreamNotificationChannelConfig);
|
|
68
|
+
/**
|
|
69
|
+
* Set the MongoDB database instance.
|
|
70
|
+
* Used when the channel is created before the database connection.
|
|
71
|
+
*/
|
|
72
|
+
setDb(db: Db): void;
|
|
73
|
+
/**
|
|
74
|
+
* Get the last resume token for reconnection purposes.
|
|
75
|
+
* Store this token to resume watching from the same point after restart.
|
|
76
|
+
*/
|
|
77
|
+
getResumeToken(): ResumeToken | undefined;
|
|
78
|
+
connect(): Promise<void>;
|
|
79
|
+
private handleChange;
|
|
80
|
+
private extractFullDocument;
|
|
81
|
+
private buildNotification;
|
|
82
|
+
private handleConnectionLoss;
|
|
83
|
+
disconnect(): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Publish a job notification.
|
|
86
|
+
*
|
|
87
|
+
* **Note:** This method is a no-op for Change Stream notifications.
|
|
88
|
+
* Change Streams automatically detect job changes in the database,
|
|
89
|
+
* so explicit publishing is not required. This method exists only
|
|
90
|
+
* to satisfy the NotificationChannel interface.
|
|
91
|
+
*
|
|
92
|
+
* All job saves to the watched collection will automatically trigger
|
|
93
|
+
* notifications to all subscribers.
|
|
94
|
+
*/
|
|
95
|
+
publish(_notification: JobNotification): Promise<void>;
|
|
96
|
+
}
|