@agendajs/postgres-backend 1.0.0-alpha.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 +215 -0
- package/dist/PostgresBackend.d.ts +75 -0
- package/dist/PostgresBackend.js +123 -0
- package/dist/PostgresBackend.js.map +1 -0
- package/dist/PostgresJobRepository.d.ts +53 -0
- package/dist/PostgresJobRepository.js +559 -0
- package/dist/PostgresJobRepository.js.map +1 -0
- package/dist/PostgresNotificationChannel.d.ts +41 -0
- package/dist/PostgresNotificationChannel.js +170 -0
- package/dist/PostgresNotificationChannel.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/schema.d.ts +10 -0
- package/dist/schema.js +72 -0
- package/dist/schema.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 +71 -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,215 @@
|
|
|
1
|
+
# @agendajs/postgres-backend
|
|
2
|
+
|
|
3
|
+
PostgreSQL backend for [Agenda](https://github.com/agenda/agenda) job scheduler with LISTEN/NOTIFY support for real-time job processing.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Full PostgreSQL storage backend for Agenda jobs
|
|
8
|
+
- Real-time job notifications using PostgreSQL LISTEN/NOTIFY
|
|
9
|
+
- Automatic schema creation with optimized indexes
|
|
10
|
+
- Connection pooling via `pg` library
|
|
11
|
+
- TypeScript support with full type definitions
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @agendajs/postgres-backend
|
|
17
|
+
# or
|
|
18
|
+
pnpm add @agendajs/postgres-backend
|
|
19
|
+
# or
|
|
20
|
+
yarn add @agendajs/postgres-backend
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Basic Usage
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { Agenda } from 'agenda';
|
|
29
|
+
import { PostgresBackend } from '@agendajs/postgres-backend';
|
|
30
|
+
|
|
31
|
+
// Create agenda with PostgreSQL backend
|
|
32
|
+
const agenda = new Agenda({
|
|
33
|
+
backend: new PostgresBackend({
|
|
34
|
+
connectionString: 'postgresql://user:pass@localhost:5432/mydb'
|
|
35
|
+
})
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Define jobs
|
|
39
|
+
agenda.define('send-email', async (job) => {
|
|
40
|
+
const { to, subject, body } = job.attrs.data;
|
|
41
|
+
await sendEmail(to, subject, body);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Start processing
|
|
45
|
+
await agenda.start();
|
|
46
|
+
|
|
47
|
+
// Schedule jobs
|
|
48
|
+
await agenda.every('5 minutes', 'send-email', { to: 'user@example.com', subject: 'Hello', body: 'World' });
|
|
49
|
+
await agenda.schedule('in 10 minutes', 'send-email', { to: 'other@example.com', subject: 'Hi', body: 'There' });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Configuration Options
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { PostgresBackend } from '@agendajs/postgres-backend';
|
|
56
|
+
|
|
57
|
+
const backend = new PostgresBackend({
|
|
58
|
+
// PostgreSQL connection string (required unless pool/poolConfig is provided)
|
|
59
|
+
connectionString: 'postgresql://user:pass@localhost:5432/mydb',
|
|
60
|
+
|
|
61
|
+
// Or use pool configuration (creates a new pool)
|
|
62
|
+
poolConfig: {
|
|
63
|
+
host: 'localhost',
|
|
64
|
+
port: 5432,
|
|
65
|
+
database: 'mydb',
|
|
66
|
+
user: 'user',
|
|
67
|
+
password: 'pass',
|
|
68
|
+
max: 20 // max pool size
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Table name for jobs (default: 'agenda_jobs')
|
|
72
|
+
tableName: 'agenda_jobs',
|
|
73
|
+
|
|
74
|
+
// Channel name for LISTEN/NOTIFY (default: 'agenda_jobs')
|
|
75
|
+
channelName: 'agenda_jobs',
|
|
76
|
+
|
|
77
|
+
// Name to identify this Agenda instance (stored as lastModifiedBy)
|
|
78
|
+
name: 'worker-1',
|
|
79
|
+
|
|
80
|
+
// Whether to create the table and indexes on connect (default: true)
|
|
81
|
+
ensureSchema: true,
|
|
82
|
+
|
|
83
|
+
// Sort order for job queries
|
|
84
|
+
sort: {
|
|
85
|
+
nextRunAt: 1, // 1 for ASC, -1 for DESC
|
|
86
|
+
priority: -1
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Using an Existing Pool
|
|
92
|
+
|
|
93
|
+
If your application already has a PostgreSQL connection pool, you can pass it directly. The pool will **not** be closed when Agenda disconnects:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { Pool } from 'pg';
|
|
97
|
+
import { PostgresBackend } from '@agendajs/postgres-backend';
|
|
98
|
+
|
|
99
|
+
// Your app's existing pool
|
|
100
|
+
const pool = new Pool({ connectionString: 'postgresql://...' });
|
|
101
|
+
|
|
102
|
+
const backend = new PostgresBackend({ pool });
|
|
103
|
+
|
|
104
|
+
// After agenda.stop(), your pool is still usable
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## How It Works
|
|
108
|
+
|
|
109
|
+
### Storage
|
|
110
|
+
|
|
111
|
+
The backend stores jobs in a PostgreSQL table with the following structure:
|
|
112
|
+
|
|
113
|
+
- `id` - UUID primary key
|
|
114
|
+
- `name` - Job name
|
|
115
|
+
- `priority` - Job priority (higher = more urgent)
|
|
116
|
+
- `next_run_at` - When the job should run next
|
|
117
|
+
- `type` - Job type ('normal' or 'single')
|
|
118
|
+
- `locked_at` - Lock timestamp for distributed execution
|
|
119
|
+
- `data` - JSONB payload for job data
|
|
120
|
+
- And other metadata fields...
|
|
121
|
+
|
|
122
|
+
### Real-Time Notifications
|
|
123
|
+
|
|
124
|
+
Unlike MongoDB, PostgreSQL has built-in pub/sub via LISTEN/NOTIFY. When a job is saved, a notification is published to all listening Agenda instances, triggering immediate job processing without waiting for the next poll interval.
|
|
125
|
+
|
|
126
|
+
This means:
|
|
127
|
+
- Lower latency for job execution
|
|
128
|
+
- Reduced database polling overhead
|
|
129
|
+
- Efficient cross-process coordination
|
|
130
|
+
|
|
131
|
+
### Distributed Locking
|
|
132
|
+
|
|
133
|
+
The backend uses PostgreSQL's `FOR UPDATE SKIP LOCKED` to atomically lock jobs for processing, preventing duplicate execution across multiple Agenda instances.
|
|
134
|
+
|
|
135
|
+
## Database Indexes
|
|
136
|
+
|
|
137
|
+
When `ensureSchema: true` (default), the following indexes are created:
|
|
138
|
+
|
|
139
|
+
```sql
|
|
140
|
+
-- Main index for finding and locking next job
|
|
141
|
+
CREATE INDEX agenda_jobs_find_and_lock_idx
|
|
142
|
+
ON agenda_jobs (name, next_run_at, priority DESC, locked_at, disabled)
|
|
143
|
+
WHERE disabled = FALSE;
|
|
144
|
+
|
|
145
|
+
-- Index for single jobs (upsert operations)
|
|
146
|
+
CREATE UNIQUE INDEX agenda_jobs_single_job_idx
|
|
147
|
+
ON agenda_jobs (name) WHERE type = 'single';
|
|
148
|
+
|
|
149
|
+
-- Index for locked_at (stale lock detection)
|
|
150
|
+
CREATE INDEX agenda_jobs_locked_at_idx
|
|
151
|
+
ON agenda_jobs (locked_at) WHERE locked_at IS NOT NULL;
|
|
152
|
+
|
|
153
|
+
-- Index for next_run_at queries
|
|
154
|
+
CREATE INDEX agenda_jobs_next_run_at_idx
|
|
155
|
+
ON agenda_jobs (next_run_at) WHERE next_run_at IS NOT NULL;
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Manual Schema Management
|
|
159
|
+
|
|
160
|
+
If you prefer to manage the schema yourself (set `ensureSchema: false`):
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { getCreateTableSQL, getCreateIndexesSQL } from '@agendajs/postgres-backend';
|
|
164
|
+
|
|
165
|
+
const tableName = 'agenda_jobs';
|
|
166
|
+
|
|
167
|
+
// Create table
|
|
168
|
+
await pool.query(getCreateTableSQL(tableName));
|
|
169
|
+
|
|
170
|
+
// Create indexes
|
|
171
|
+
for (const sql of getCreateIndexesSQL(tableName)) {
|
|
172
|
+
await pool.query(sql);
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Testing
|
|
177
|
+
|
|
178
|
+
Tests require a PostgreSQL database. The easiest way is to use Docker:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Start PostgreSQL container and run tests
|
|
182
|
+
pnpm test:docker
|
|
183
|
+
|
|
184
|
+
# Or manually:
|
|
185
|
+
pnpm docker:up # Start PostgreSQL container
|
|
186
|
+
pnpm test:postgres # Run tests
|
|
187
|
+
pnpm docker:down # Stop container
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
You can also use an existing PostgreSQL database:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
POSTGRES_TEST_URL=postgresql://user:pass@localhost:5432/agenda_test pnpm test
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Available Scripts
|
|
197
|
+
|
|
198
|
+
| Script | Description |
|
|
199
|
+
|--------|-------------|
|
|
200
|
+
| `pnpm test` | Run tests (requires PostgreSQL or Docker) |
|
|
201
|
+
| `pnpm test:postgres` | Run tests with local Docker PostgreSQL |
|
|
202
|
+
| `pnpm test:docker` | Start container, run tests, stop container |
|
|
203
|
+
| `pnpm docker:up` | Start PostgreSQL container |
|
|
204
|
+
| `pnpm docker:down` | Stop PostgreSQL container |
|
|
205
|
+
| `pnpm docker:logs` | View container logs |
|
|
206
|
+
|
|
207
|
+
## Requirements
|
|
208
|
+
|
|
209
|
+
- Node.js >= 18.0.0
|
|
210
|
+
- PostgreSQL >= 12 (for `gen_random_uuid()` support)
|
|
211
|
+
- Agenda >= 6.0.0
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
MIT
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { AgendaBackend, JobRepository, NotificationChannel } from 'agenda';
|
|
2
|
+
import type { PostgresBackendConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* PostgreSQL backend for Agenda
|
|
5
|
+
*
|
|
6
|
+
* Provides both storage (via PostgresJobRepository) and real-time notifications
|
|
7
|
+
* (via PostgresNotificationChannel using LISTEN/NOTIFY).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Agenda } from 'agenda';
|
|
12
|
+
* import { PostgresBackend } from '@agendajs/postgres-backend';
|
|
13
|
+
*
|
|
14
|
+
* // Using connection string
|
|
15
|
+
* const agenda = new Agenda({
|
|
16
|
+
* backend: new PostgresBackend({
|
|
17
|
+
* connectionString: 'postgresql://user:pass@localhost:5432/mydb'
|
|
18
|
+
* })
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Or using an existing pool (e.g., shared with your app)
|
|
22
|
+
* // The pool will NOT be closed when Agenda disconnects
|
|
23
|
+
* import { Pool } from 'pg';
|
|
24
|
+
* const pool = new Pool({ connectionString: '...' });
|
|
25
|
+
*
|
|
26
|
+
* const agenda = new Agenda({
|
|
27
|
+
* backend: new PostgresBackend({ pool })
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Your app can continue using the pool after agenda.stop()
|
|
31
|
+
*
|
|
32
|
+
* agenda.define('myJob', async (job) => {
|
|
33
|
+
* console.log('Running job:', job.attrs.name);
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* await agenda.start();
|
|
37
|
+
* await agenda.every('5 minutes', 'myJob');
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare class PostgresBackend implements AgendaBackend {
|
|
41
|
+
private _repository;
|
|
42
|
+
private _notificationChannel?;
|
|
43
|
+
private config;
|
|
44
|
+
private _ownsConnection;
|
|
45
|
+
constructor(config: PostgresBackendConfig);
|
|
46
|
+
/**
|
|
47
|
+
* The job repository for storage operations
|
|
48
|
+
*/
|
|
49
|
+
get repository(): JobRepository;
|
|
50
|
+
/**
|
|
51
|
+
* Whether this backend owns its database connection.
|
|
52
|
+
* True if created from connectionString/poolConfig, false if pool was passed in.
|
|
53
|
+
*/
|
|
54
|
+
get ownsConnection(): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* The notification channel for real-time notifications via LISTEN/NOTIFY
|
|
57
|
+
* Returns undefined if notifications are disabled
|
|
58
|
+
*/
|
|
59
|
+
get notificationChannel(): NotificationChannel | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Connect to PostgreSQL
|
|
62
|
+
*
|
|
63
|
+
* - Establishes database connection pool
|
|
64
|
+
* - Creates table and indexes if ensureSchema is true
|
|
65
|
+
* - Sets up LISTEN for real-time notifications
|
|
66
|
+
*/
|
|
67
|
+
connect(): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Disconnect from PostgreSQL
|
|
70
|
+
*
|
|
71
|
+
* - Stops LISTEN on notification channel
|
|
72
|
+
* - Closes database connection pool
|
|
73
|
+
*/
|
|
74
|
+
disconnect(): Promise<void>;
|
|
75
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import { PostgresJobRepository } from './PostgresJobRepository.js';
|
|
3
|
+
import { PostgresNotificationChannel } from './PostgresNotificationChannel.js';
|
|
4
|
+
const log = debug('agenda:postgres:backend');
|
|
5
|
+
/**
|
|
6
|
+
* PostgreSQL backend for Agenda
|
|
7
|
+
*
|
|
8
|
+
* Provides both storage (via PostgresJobRepository) and real-time notifications
|
|
9
|
+
* (via PostgresNotificationChannel using LISTEN/NOTIFY).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { Agenda } from 'agenda';
|
|
14
|
+
* import { PostgresBackend } from '@agendajs/postgres-backend';
|
|
15
|
+
*
|
|
16
|
+
* // Using connection string
|
|
17
|
+
* const agenda = new Agenda({
|
|
18
|
+
* backend: new PostgresBackend({
|
|
19
|
+
* connectionString: 'postgresql://user:pass@localhost:5432/mydb'
|
|
20
|
+
* })
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Or using an existing pool (e.g., shared with your app)
|
|
24
|
+
* // The pool will NOT be closed when Agenda disconnects
|
|
25
|
+
* import { Pool } from 'pg';
|
|
26
|
+
* const pool = new Pool({ connectionString: '...' });
|
|
27
|
+
*
|
|
28
|
+
* const agenda = new Agenda({
|
|
29
|
+
* backend: new PostgresBackend({ pool })
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* // Your app can continue using the pool after agenda.stop()
|
|
33
|
+
*
|
|
34
|
+
* agenda.define('myJob', async (job) => {
|
|
35
|
+
* console.log('Running job:', job.attrs.name);
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* await agenda.start();
|
|
39
|
+
* await agenda.every('5 minutes', 'myJob');
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export class PostgresBackend {
|
|
43
|
+
_repository;
|
|
44
|
+
_notificationChannel;
|
|
45
|
+
config;
|
|
46
|
+
_ownsConnection;
|
|
47
|
+
constructor(config) {
|
|
48
|
+
this.config = config;
|
|
49
|
+
// Determine if we own the connection (not passed in by user)
|
|
50
|
+
this._ownsConnection = !config.pool;
|
|
51
|
+
// Create repository
|
|
52
|
+
this._repository = new PostgresJobRepository(config);
|
|
53
|
+
// Create notification channel (unless disabled)
|
|
54
|
+
if (!config.disableNotifications) {
|
|
55
|
+
this._notificationChannel = new PostgresNotificationChannel({
|
|
56
|
+
channelName: config.channelName,
|
|
57
|
+
reconnect: {
|
|
58
|
+
enabled: true
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
log('PostgresBackend created with config: %O', {
|
|
63
|
+
tableName: config.tableName || 'agenda_jobs',
|
|
64
|
+
channelName: config.channelName || 'agenda_jobs',
|
|
65
|
+
ensureSchema: config.ensureSchema ?? true,
|
|
66
|
+
disableNotifications: config.disableNotifications ?? false,
|
|
67
|
+
ownsConnection: this._ownsConnection
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The job repository for storage operations
|
|
72
|
+
*/
|
|
73
|
+
get repository() {
|
|
74
|
+
return this._repository;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Whether this backend owns its database connection.
|
|
78
|
+
* True if created from connectionString/poolConfig, false if pool was passed in.
|
|
79
|
+
*/
|
|
80
|
+
get ownsConnection() {
|
|
81
|
+
return this._ownsConnection;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The notification channel for real-time notifications via LISTEN/NOTIFY
|
|
85
|
+
* Returns undefined if notifications are disabled
|
|
86
|
+
*/
|
|
87
|
+
get notificationChannel() {
|
|
88
|
+
return this._notificationChannel;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Connect to PostgreSQL
|
|
92
|
+
*
|
|
93
|
+
* - Establishes database connection pool
|
|
94
|
+
* - Creates table and indexes if ensureSchema is true
|
|
95
|
+
* - Sets up LISTEN for real-time notifications
|
|
96
|
+
*/
|
|
97
|
+
async connect() {
|
|
98
|
+
log('connecting PostgresBackend');
|
|
99
|
+
// Connect repository first (creates pool and schema)
|
|
100
|
+
await this._repository.connect();
|
|
101
|
+
// Share the pool with notification channel (if enabled)
|
|
102
|
+
if (this._notificationChannel) {
|
|
103
|
+
this._notificationChannel.setPool(this._repository.getPool());
|
|
104
|
+
}
|
|
105
|
+
// Note: notification channel is connected by Agenda.start()
|
|
106
|
+
// when it's needed, so we don't connect it here
|
|
107
|
+
log('PostgresBackend connected');
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Disconnect from PostgreSQL
|
|
111
|
+
*
|
|
112
|
+
* - Stops LISTEN on notification channel
|
|
113
|
+
* - Closes database connection pool
|
|
114
|
+
*/
|
|
115
|
+
async disconnect() {
|
|
116
|
+
log('disconnecting PostgresBackend');
|
|
117
|
+
// Notification channel is disconnected by Agenda.stop()
|
|
118
|
+
// Repository disconnect will close the shared pool
|
|
119
|
+
await this._repository.disconnect();
|
|
120
|
+
log('PostgresBackend disconnected');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=PostgresBackend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresBackend.js","sourceRoot":"","sources":["../src/PostgresBackend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAG/E,MAAM,GAAG,GAAG,KAAK,CAAC,yBAAyB,CAAC,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,OAAO,eAAe;IACnB,WAAW,CAAwB;IACnC,oBAAoB,CAA+B;IACnD,MAAM,CAAwB;IAC9B,eAAe,CAAU;IAEjC,YAAY,MAA6B;QACxC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,6DAA6D;QAC7D,IAAI,CAAC,eAAe,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAEpC,oBAAoB;QACpB,IAAI,CAAC,WAAW,GAAG,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAErD,gDAAgD;QAChD,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAClC,IAAI,CAAC,oBAAoB,GAAG,IAAI,2BAA2B,CAAC;gBAC3D,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,SAAS,EAAE;oBACV,OAAO,EAAE,IAAI;iBACb;aACD,CAAC,CAAC;QACJ,CAAC;QAED,GAAG,CAAC,yCAAyC,EAAE;YAC9C,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,aAAa;YAC5C,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,aAAa;YAChD,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,IAAI;YACzC,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,IAAI,KAAK;YAC1D,cAAc,EAAE,IAAI,CAAC,eAAe;SACpC,CAAC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACb,OAAO,IAAI,CAAC,WAAW,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,IAAI,cAAc;QACjB,OAAO,IAAI,CAAC,eAAe,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,IAAI,mBAAmB;QACtB,OAAO,IAAI,CAAC,oBAAoB,CAAC;IAClC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO;QACZ,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAElC,qDAAqD;QACrD,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QAEjC,wDAAwD;QACxD,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC/B,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,4DAA4D;QAC5D,gDAAgD;QAEhD,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU;QACf,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAErC,wDAAwD;QACxD,mDAAmD;QACnD,MAAM,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;QAEpC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IACrC,CAAC;CACD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Pool } from 'pg';
|
|
2
|
+
import type { JobRepository, JobRepositoryOptions, RemoveJobsOptions, JobParameters, JobId, JobsQueryOptions, JobsResult, JobsOverview } from 'agenda';
|
|
3
|
+
import type { PostgresBackendConfig } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* PostgreSQL implementation of JobRepository
|
|
6
|
+
*/
|
|
7
|
+
export declare class PostgresJobRepository implements JobRepository {
|
|
8
|
+
private config;
|
|
9
|
+
private pool;
|
|
10
|
+
private ownPool;
|
|
11
|
+
private tableName;
|
|
12
|
+
private ensureSchema;
|
|
13
|
+
private sort;
|
|
14
|
+
private disconnected;
|
|
15
|
+
constructor(config: PostgresBackendConfig);
|
|
16
|
+
private createPool;
|
|
17
|
+
/**
|
|
18
|
+
* Get the underlying pool (for use by notification channel)
|
|
19
|
+
*/
|
|
20
|
+
getPool(): Pool;
|
|
21
|
+
connect(): Promise<void>;
|
|
22
|
+
private createSchema;
|
|
23
|
+
disconnect(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Force close the pool - should only be called on application shutdown
|
|
26
|
+
* or when you're sure the backend won't be reused
|
|
27
|
+
*/
|
|
28
|
+
close(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Convert PostgreSQL row to JobParameters
|
|
31
|
+
*/
|
|
32
|
+
private rowToJob;
|
|
33
|
+
/**
|
|
34
|
+
* Convert SortDirection to SQL ASC/DESC
|
|
35
|
+
*/
|
|
36
|
+
private toSqlDirection;
|
|
37
|
+
/**
|
|
38
|
+
* Convert sort options to PostgreSQL ORDER BY clause
|
|
39
|
+
*/
|
|
40
|
+
private toOrderByClause;
|
|
41
|
+
getJobById(id: string): Promise<JobParameters | null>;
|
|
42
|
+
queryJobs(options?: JobsQueryOptions): Promise<JobsResult>;
|
|
43
|
+
getJobsOverview(): Promise<JobsOverview[]>;
|
|
44
|
+
getDistinctJobNames(): Promise<string[]>;
|
|
45
|
+
getQueueSize(): Promise<number>;
|
|
46
|
+
removeJobs(options: RemoveJobsOptions): Promise<number>;
|
|
47
|
+
unlockJob(job: JobParameters): Promise<void>;
|
|
48
|
+
unlockJobs(jobIds: (JobId | string)[]): Promise<void>;
|
|
49
|
+
lockJob(job: JobParameters, options: JobRepositoryOptions | undefined): Promise<JobParameters | undefined>;
|
|
50
|
+
getNextJobToRun(jobName: string, nextScanAt: Date, lockDeadline: Date, now: Date | undefined, options: JobRepositoryOptions | undefined): Promise<JobParameters | undefined>;
|
|
51
|
+
saveJobState(job: JobParameters, options: JobRepositoryOptions | undefined): Promise<void>;
|
|
52
|
+
saveJob<DATA = unknown>(job: JobParameters<DATA>, options: JobRepositoryOptions | undefined): Promise<JobParameters<DATA>>;
|
|
53
|
+
}
|