pg-boss 12.5.4 → 12.7.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 +102 -0
- package/dist/attorney.d.ts.map +1 -1
- package/dist/attorney.js +58 -1
- package/dist/bam.d.ts +14 -0
- package/dist/bam.d.ts.map +1 -0
- package/dist/bam.js +114 -0
- package/dist/boss.d.ts.map +1 -1
- package/dist/boss.js +18 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +333 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/manager.d.ts +6 -2
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +244 -51
- package/dist/migrationStore.d.ts.map +1 -1
- package/dist/migrationStore.js +302 -3
- package/dist/plans.d.ts +22 -2
- package/dist/plans.d.ts.map +1 -1
- package/dist/plans.js +377 -64
- package/dist/types.d.ts +74 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.d.ts +2 -0
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +6 -0
- package/package.json +17 -15
package/README.md
CHANGED
|
@@ -52,6 +52,108 @@ This will likely cater the most to teams already familiar with the simplicity of
|
|
|
52
52
|
* Serverless function compatible
|
|
53
53
|
* Multi-master compatible (for example, in a Kubernetes ReplicaSet)
|
|
54
54
|
|
|
55
|
+
## CLI
|
|
56
|
+
|
|
57
|
+
pg-boss includes a command-line interface for managing database migrations without writing code. This is useful for CI/CD pipelines, database setup scripts, or manual schema management.
|
|
58
|
+
|
|
59
|
+
### Installation
|
|
60
|
+
|
|
61
|
+
When installed globally, the CLI is available as `pg-boss`:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install -g pg-boss
|
|
65
|
+
pg-boss --help
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or run directly with npx:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx pg-boss --help
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Commands
|
|
75
|
+
|
|
76
|
+
| Command | Description |
|
|
77
|
+
|---------|-------------|
|
|
78
|
+
| `migrate` | Run pending migrations (creates schema if not exists) |
|
|
79
|
+
| `create` | Create initial pg-boss schema |
|
|
80
|
+
| `version` | Show current schema version |
|
|
81
|
+
| `rollback` | Rollback the last migration |
|
|
82
|
+
| `plans <subcommand>` | Output SQL without executing (subcommands: `create`, `migrate`, `rollback`) |
|
|
83
|
+
|
|
84
|
+
### Connection Configuration
|
|
85
|
+
|
|
86
|
+
The CLI supports multiple ways to configure the database connection, in order of precedence:
|
|
87
|
+
|
|
88
|
+
1. **Command-line arguments**
|
|
89
|
+
```bash
|
|
90
|
+
pg-boss migrate --connection-string postgres://user:pass@host/database
|
|
91
|
+
# or individual options
|
|
92
|
+
pg-boss migrate --host localhost --port 5432 --database mydb --user postgres --password secret
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
2. **Environment variables**
|
|
96
|
+
```bash
|
|
97
|
+
PGBOSS_DATABASE_URL=postgres://user:pass@host/database pg-boss migrate
|
|
98
|
+
# or individual variables
|
|
99
|
+
PGBOSS_HOST=localhost PGBOSS_PORT=5432 PGBOSS_DATABASE=mydb PGBOSS_USER=postgres PGBOSS_PASSWORD=secret pg-boss migrate
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This allows admin credentials for migrations to coexist with regular application database credentials (e.g., `DATABASE_URL` for the app, `PGBOSS_DATABASE_URL` for migrations).
|
|
103
|
+
|
|
104
|
+
3. **Config file** (pgboss.json or .pgbossrc in current directory, or specify with `--config`)
|
|
105
|
+
```bash
|
|
106
|
+
pg-boss migrate --config ./config/pgboss.json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Config file format:
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"host": "localhost",
|
|
113
|
+
"port": 5432,
|
|
114
|
+
"database": "mydb",
|
|
115
|
+
"user": "postgres",
|
|
116
|
+
"password": "secret",
|
|
117
|
+
"schema": "pgboss"
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Options
|
|
122
|
+
|
|
123
|
+
| Option | Short | Description |
|
|
124
|
+
|--------|-------|-------------|
|
|
125
|
+
| `--connection-string` | `-c` | PostgreSQL connection string |
|
|
126
|
+
| `--host` | | Database host |
|
|
127
|
+
| `--port` | | Database port |
|
|
128
|
+
| `--database` | `-d` | Database name |
|
|
129
|
+
| `--user` | `-u` | Database user |
|
|
130
|
+
| `--password` | `-p` | Database password |
|
|
131
|
+
| `--schema` | `-s` | pg-boss schema name (default: pgboss) |
|
|
132
|
+
| `--config` | | Path to config file |
|
|
133
|
+
| `--dry-run` | | Show SQL without executing (for migrate, create, rollback) |
|
|
134
|
+
|
|
135
|
+
### Examples
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Create schema in a new database
|
|
139
|
+
pg-boss create --connection-string postgres://localhost/myapp
|
|
140
|
+
|
|
141
|
+
# Run migrations in CI/CD pipeline
|
|
142
|
+
PGBOSS_DATABASE_URL=$PGBOSS_DATABASE_URL pg-boss migrate
|
|
143
|
+
|
|
144
|
+
# Preview migration SQL before running
|
|
145
|
+
pg-boss migrate --connection-string postgres://localhost/myapp --dry-run
|
|
146
|
+
|
|
147
|
+
# Check current schema version
|
|
148
|
+
pg-boss version -c postgres://localhost/myapp
|
|
149
|
+
|
|
150
|
+
# Use a custom schema name
|
|
151
|
+
pg-boss migrate -c postgres://localhost/myapp --schema myapp_jobs
|
|
152
|
+
|
|
153
|
+
# Output SQL for creating schema (useful for review or manual execution)
|
|
154
|
+
pg-boss plans create --schema myapp_jobs
|
|
155
|
+
```
|
|
156
|
+
|
|
55
157
|
## Requirements
|
|
56
158
|
* Node 22.12 or higher for CommonJS's require(esm)
|
|
57
159
|
* PostgreSQL 13 or higher
|
package/dist/attorney.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attorney.d.ts","sourceRoot":"","sources":["../src/attorney.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAA;AAExC,QAAA,MAAM,MAAM;;;;CAIX,CAAA;AAMD,iBAAS,iBAAiB,CAAE,MAAM,GAAE,GAAQ,QAW3C;AAED,iBAAS,aAAa,CAAE,IAAI,EAAE,GAAG,GAAG,KAAK,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"attorney.d.ts","sourceRoot":"","sources":["../src/attorney.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAA;AAExC,QAAA,MAAM,MAAM;;;;CAIX,CAAA;AAMD,iBAAS,iBAAiB,CAAE,MAAM,GAAE,GAAQ,QAW3C;AAED,iBAAS,aAAa,CAAE,IAAI,EAAE,GAAG,GAAG,KAAK,CAAC,OAAO,CA+ChD;AAmED,iBAAS,aAAa,CAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG;IAClD,OAAO,EAAE,KAAK,CAAC,mBAAmB,CAAA;IAClC,QAAQ,EAAE,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;CACjC,CA2BA;AAED,iBAAS,cAAc,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,QAOlD;AAED,iBAAS,SAAS,CAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,kBAAkB,GAAG,KAAK,CAAC,0BAA0B,CAoB9F;AAkBD,iBAAS,wBAAwB,CAAE,IAAI,EAAE,MAAM,QAK9C;AAED,iBAAS,eAAe,CAAE,IAAI,EAAE,MAAM,QAIrC;AAED,iBAAS,SAAS,CAAE,GAAG,EAAE,MAAM,QAI9B;AA+FD,OAAO,EACL,SAAS,EACT,wBAAwB,EACxB,eAAe,EACf,cAAc,EACd,aAAa,EACb,aAAa,EACb,SAAS,EACT,MAAM,EACN,iBAAiB,EAClB,CAAA"}
|
package/dist/attorney.js
CHANGED
|
@@ -51,8 +51,57 @@ function checkSendArgs(args) {
|
|
|
51
51
|
validateExpirationConfig(options);
|
|
52
52
|
validateRetentionConfig(options);
|
|
53
53
|
validateDeletionConfig(options);
|
|
54
|
+
validateGroupConfig(options);
|
|
54
55
|
return { name, data, options };
|
|
55
56
|
}
|
|
57
|
+
function validateGroupConfig(config) {
|
|
58
|
+
if (!('group' in config) || config.group === undefined || config.group === null) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
assert(typeof config.group === 'object', 'group must be an object');
|
|
62
|
+
assert(typeof config.group.id === 'string' && config.group.id.length > 0, 'group.id must be a non-empty string');
|
|
63
|
+
assert(!('tier' in config.group) || (typeof config.group.tier === 'string' && config.group.tier.length > 0), 'group.tier must be a non-empty string if provided');
|
|
64
|
+
}
|
|
65
|
+
function validateGroupConcurrencyValue(value, optionName) {
|
|
66
|
+
if (typeof value === 'number') {
|
|
67
|
+
assert(Number.isInteger(value) && value >= 1, `${optionName} must be an integer >= 1`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
assert(typeof value === 'object', `${optionName} must be a number or an object with { default, tiers? }`);
|
|
71
|
+
assert(Number.isInteger(value.default) && value.default >= 1, `${optionName}.default must be an integer >= 1`);
|
|
72
|
+
if ('tiers' in value && value.tiers) {
|
|
73
|
+
assert(typeof value.tiers === 'object', `${optionName}.tiers must be an object`);
|
|
74
|
+
for (const [tier, limit] of Object.entries(value.tiers)) {
|
|
75
|
+
assert(typeof tier === 'string' && tier.length > 0, `${optionName} tier keys must be non-empty strings`);
|
|
76
|
+
assert(Number.isInteger(limit) && limit >= 1, `${optionName}.tiers["${tier}"] must be an integer >= 1`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function validateGroupConcurrencyConfig(config) {
|
|
81
|
+
const hasGlobal = config.groupConcurrency != null;
|
|
82
|
+
const hasLocal = config.localGroupConcurrency != null;
|
|
83
|
+
assert(!(hasGlobal && hasLocal), 'cannot specify both groupConcurrency and localGroupConcurrency - choose one');
|
|
84
|
+
if (hasGlobal)
|
|
85
|
+
validateGroupConcurrencyValue(config.groupConcurrency, 'groupConcurrency');
|
|
86
|
+
if (hasLocal) {
|
|
87
|
+
validateGroupConcurrencyValue(config.localGroupConcurrency, 'localGroupConcurrency');
|
|
88
|
+
validateLocalGroupConcurrencyLimit(config.localGroupConcurrency, config.localConcurrency);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function validateLocalGroupConcurrencyLimit(localGroupConcurrency, localConcurrency) {
|
|
92
|
+
const effectiveLocalConcurrency = localConcurrency ?? 1;
|
|
93
|
+
if (typeof localGroupConcurrency === 'number') {
|
|
94
|
+
assert(localGroupConcurrency <= effectiveLocalConcurrency, `localGroupConcurrency (${localGroupConcurrency}) cannot exceed localConcurrency (${effectiveLocalConcurrency})`);
|
|
95
|
+
}
|
|
96
|
+
else if (typeof localGroupConcurrency === 'object') {
|
|
97
|
+
assert(localGroupConcurrency.default <= effectiveLocalConcurrency, `localGroupConcurrency.default (${localGroupConcurrency.default}) cannot exceed localConcurrency (${effectiveLocalConcurrency})`);
|
|
98
|
+
if (localGroupConcurrency.tiers) {
|
|
99
|
+
for (const [tier, limit] of Object.entries(localGroupConcurrency.tiers)) {
|
|
100
|
+
assert(limit <= effectiveLocalConcurrency, `localGroupConcurrency.tiers["${tier}"] (${limit}) cannot exceed localConcurrency (${effectiveLocalConcurrency})`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
56
105
|
function checkWorkArgs(name, args) {
|
|
57
106
|
let options, callback;
|
|
58
107
|
assert(name, 'queue name is required');
|
|
@@ -71,6 +120,8 @@ function checkWorkArgs(name, args) {
|
|
|
71
120
|
assert(!('batchSize' in options) || (Number.isInteger(options.batchSize) && options.batchSize >= 1), 'batchSize must be an integer > 0');
|
|
72
121
|
assert(!('includeMetadata' in options) || typeof options.includeMetadata === 'boolean', 'includeMetadata must be a boolean');
|
|
73
122
|
assert(!('priority' in options) || typeof options.priority === 'boolean', 'priority must be a boolean');
|
|
123
|
+
assert(!('localConcurrency' in options) || (Number.isInteger(options.localConcurrency) && options.localConcurrency >= 1), 'localConcurrency must be an integer >= 1');
|
|
124
|
+
validateGroupConcurrencyConfig(options);
|
|
74
125
|
return { options, callback };
|
|
75
126
|
}
|
|
76
127
|
function checkFetchArgs(name, options) {
|
|
@@ -92,6 +143,7 @@ function getConfig(value) {
|
|
|
92
143
|
applySchemaConfig(config);
|
|
93
144
|
applyOpsConfig(config);
|
|
94
145
|
applyScheduleConfig(config);
|
|
146
|
+
applyBamConfig(config);
|
|
95
147
|
validateWarningConfig(config);
|
|
96
148
|
return config;
|
|
97
149
|
}
|
|
@@ -157,7 +209,7 @@ function applyOpsConfig(config) {
|
|
|
157
209
|
assert(config.queueCacheIntervalSeconds / 60 / 60 <= POLICY.MAX_EXPIRATION_HOURS, `configuration assert: queueCacheIntervalSeconds cannot exceed ${POLICY.MAX_EXPIRATION_HOURS} hours`);
|
|
158
210
|
}
|
|
159
211
|
function validateDeletionConfig(config) {
|
|
160
|
-
assert(!('deleteAfterSeconds' in config) || config.deleteAfterSeconds >=
|
|
212
|
+
assert(!('deleteAfterSeconds' in config) || config.deleteAfterSeconds >= 0, 'configuration assert: deleteAfterSeconds must be at least 0 (0 disables deletion)');
|
|
161
213
|
}
|
|
162
214
|
function applyScheduleConfig(config) {
|
|
163
215
|
assert(!('clockMonitorIntervalSeconds' in config) || (config.clockMonitorIntervalSeconds >= 1 && config.clockMonitorIntervalSeconds <= 600), 'configuration assert: clockMonitorIntervalSeconds must be between 1 second and 10 minutes');
|
|
@@ -167,4 +219,9 @@ function applyScheduleConfig(config) {
|
|
|
167
219
|
assert(!('cronWorkerIntervalSeconds' in config) || (config.cronWorkerIntervalSeconds >= 1 && config.cronWorkerIntervalSeconds <= 45), 'configuration assert: cronWorkerIntervalSeconds must be between 1 and 45 seconds');
|
|
168
220
|
config.cronWorkerIntervalSeconds = config.cronWorkerIntervalSeconds || 5;
|
|
169
221
|
}
|
|
222
|
+
function applyBamConfig(config) {
|
|
223
|
+
const minInterval = config.__test__bypass_bam_interval_check ? 1 : 10;
|
|
224
|
+
assert(!('bamIntervalSeconds' in config) || config.bamIntervalSeconds >= minInterval, `configuration assert: bamIntervalSeconds must be at least ${minInterval} seconds`);
|
|
225
|
+
config.bamIntervalSeconds = config.bamIntervalSeconds || 60;
|
|
226
|
+
}
|
|
170
227
|
export { assertKey, assertPostgresObjectName, assertQueueName, checkFetchArgs, checkSendArgs, checkWorkArgs, getConfig, POLICY, validateQueueArgs };
|
package/dist/bam.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import * as types from './types.js';
|
|
3
|
+
declare class Bam extends EventEmitter implements types.EventsMixin {
|
|
4
|
+
#private;
|
|
5
|
+
events: {
|
|
6
|
+
error: string;
|
|
7
|
+
bam: string;
|
|
8
|
+
};
|
|
9
|
+
constructor(db: types.IDatabase, config: types.ResolvedConstructorOptions);
|
|
10
|
+
start(): Promise<void>;
|
|
11
|
+
stop(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export default Bam;
|
|
14
|
+
//# sourceMappingURL=bam.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bam.d.ts","sourceRoot":"","sources":["../src/bam.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,aAAa,CAAA;AAEtC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAOnC,cAAM,GAAI,SAAQ,YAAa,YAAW,KAAK,CAAC,WAAW;;IAOzD,MAAM;;;MAAS;gBAGb,EAAE,EAAE,KAAK,CAAC,SAAS,EACnB,MAAM,EAAE,KAAK,CAAC,0BAA0B;IAUpC,KAAK;IAWL,IAAI;CAgGX;AAED,eAAe,GAAG,CAAA"}
|
package/dist/bam.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import * as plans from './plans.js';
|
|
3
|
+
import * as types from './types.js';
|
|
4
|
+
const events = {
|
|
5
|
+
error: 'error',
|
|
6
|
+
bam: 'bam'
|
|
7
|
+
};
|
|
8
|
+
class Bam extends EventEmitter {
|
|
9
|
+
#stopped;
|
|
10
|
+
#working;
|
|
11
|
+
#pollInterval;
|
|
12
|
+
#db;
|
|
13
|
+
#config;
|
|
14
|
+
events = events;
|
|
15
|
+
constructor(db, config) {
|
|
16
|
+
super();
|
|
17
|
+
this.#db = db;
|
|
18
|
+
this.#config = config;
|
|
19
|
+
this.#stopped = true;
|
|
20
|
+
this.#working = false;
|
|
21
|
+
}
|
|
22
|
+
async start() {
|
|
23
|
+
if (!this.#stopped)
|
|
24
|
+
return;
|
|
25
|
+
this.#stopped = false;
|
|
26
|
+
setImmediate(() => this.#onPoll());
|
|
27
|
+
this.#pollInterval = setInterval(() => this.#onPoll(), this.#config.bamIntervalSeconds * 1000);
|
|
28
|
+
}
|
|
29
|
+
async stop() {
|
|
30
|
+
if (this.#stopped)
|
|
31
|
+
return;
|
|
32
|
+
this.#stopped = true;
|
|
33
|
+
if (this.#pollInterval) {
|
|
34
|
+
clearInterval(this.#pollInterval);
|
|
35
|
+
this.#pollInterval = undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async #onPoll() {
|
|
39
|
+
if (this.#stopped || this.#working || !this.#config.migrate)
|
|
40
|
+
return;
|
|
41
|
+
this.#working = true;
|
|
42
|
+
try {
|
|
43
|
+
if (this.#config.__test__throw_bam) {
|
|
44
|
+
throw new Error(this.#config.__test__throw_bam);
|
|
45
|
+
}
|
|
46
|
+
const sql = plans.trySetBamTime(this.#config.schema, this.#config.bamIntervalSeconds);
|
|
47
|
+
const { rows } = await this.#db.executeSql(sql);
|
|
48
|
+
if (rows.length === 1) {
|
|
49
|
+
await this.#processCommands();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
this.emit(events.error, err);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
this.#working = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async #processCommands() {
|
|
60
|
+
if (this.#stopped)
|
|
61
|
+
return;
|
|
62
|
+
const entry = await this.#getNextCommand();
|
|
63
|
+
if (!entry || this.#stopped)
|
|
64
|
+
return;
|
|
65
|
+
this.emit(events.bam, {
|
|
66
|
+
id: entry.id,
|
|
67
|
+
name: entry.name,
|
|
68
|
+
status: 'in_progress',
|
|
69
|
+
queue: entry.queue,
|
|
70
|
+
table: entry.table
|
|
71
|
+
});
|
|
72
|
+
try {
|
|
73
|
+
await this.#db.executeSql(entry.command);
|
|
74
|
+
if (this.#stopped)
|
|
75
|
+
return;
|
|
76
|
+
await this.#markCompleted(entry.id);
|
|
77
|
+
this.emit(events.bam, {
|
|
78
|
+
id: entry.id,
|
|
79
|
+
name: entry.name,
|
|
80
|
+
status: 'completed',
|
|
81
|
+
queue: entry.queue,
|
|
82
|
+
table: entry.table
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (this.#stopped)
|
|
87
|
+
return;
|
|
88
|
+
await this.#markFailed(entry.id, err);
|
|
89
|
+
this.emit(events.error, err);
|
|
90
|
+
this.emit(events.bam, {
|
|
91
|
+
id: entry.id,
|
|
92
|
+
name: entry.name,
|
|
93
|
+
status: 'failed',
|
|
94
|
+
queue: entry.queue,
|
|
95
|
+
table: entry.table,
|
|
96
|
+
error: String(err)
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async #getNextCommand() {
|
|
101
|
+
const sql = plans.getNextBamCommand(this.#config.schema);
|
|
102
|
+
const { rows } = await this.#db.executeSql(sql);
|
|
103
|
+
return rows[0] || null;
|
|
104
|
+
}
|
|
105
|
+
async #markCompleted(id) {
|
|
106
|
+
const sql = plans.setBamCompleted(this.#config.schema, id);
|
|
107
|
+
await this.#db.executeSql(sql);
|
|
108
|
+
}
|
|
109
|
+
async #markFailed(id, error) {
|
|
110
|
+
const sql = plans.setBamFailed(this.#config.schema, id, String(error));
|
|
111
|
+
await this.#db.executeSql(sql);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export default Bam;
|
package/dist/boss.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boss.d.ts","sourceRoot":"","sources":["../src/boss.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,aAAa,CAAA;AACtC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAGvC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAYnC,cAAM,IAAK,SAAQ,YAAa,YAAW,KAAK,CAAC,WAAW;;
|
|
1
|
+
{"version":3,"file":"boss.d.ts","sourceRoot":"","sources":["../src/boss.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,aAAa,CAAA;AACtC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAGvC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAYnC,cAAM,IAAK,SAAQ,YAAa,YAAW,KAAK,CAAC,WAAW;;IAS1D,MAAM;;;MAAS;gBAGb,EAAE,EAAE,KAAK,CAAC,SAAS,EACnB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,KAAK,CAAC,0BAA0B;IAmBpC,KAAK;IAWL,IAAI;IAkEJ,SAAS,CAAE,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,WAAW,EAAE;CAuFtD;AAED,eAAe,IAAI,CAAA"}
|
package/dist/boss.js
CHANGED
|
@@ -12,6 +12,7 @@ const WARNINGS = {
|
|
|
12
12
|
};
|
|
13
13
|
class Boss extends EventEmitter {
|
|
14
14
|
#stopped;
|
|
15
|
+
#stopping;
|
|
15
16
|
#maintaining;
|
|
16
17
|
#superviseInterval;
|
|
17
18
|
#db;
|
|
@@ -24,6 +25,7 @@ class Boss extends EventEmitter {
|
|
|
24
25
|
this.#config = config;
|
|
25
26
|
this.#manager = manager;
|
|
26
27
|
this.#stopped = true;
|
|
28
|
+
this.#stopping = false;
|
|
27
29
|
if (config.warningSlowQuerySeconds) {
|
|
28
30
|
WARNINGS.SLOW_QUERY.seconds = config.warningSlowQuerySeconds;
|
|
29
31
|
}
|
|
@@ -33,12 +35,14 @@ class Boss extends EventEmitter {
|
|
|
33
35
|
}
|
|
34
36
|
async start() {
|
|
35
37
|
if (this.#stopped) {
|
|
38
|
+
this.#stopping = false;
|
|
36
39
|
this.#superviseInterval = setInterval(() => this.#onSupervise(), this.#config.superviseIntervalSeconds * 1000);
|
|
37
40
|
this.#stopped = false;
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
async stop() {
|
|
41
44
|
if (!this.#stopped) {
|
|
45
|
+
this.#stopping = true;
|
|
42
46
|
if (this.#superviseInterval)
|
|
43
47
|
clearInterval(this.#superviseInterval);
|
|
44
48
|
this.#stopped = true;
|
|
@@ -105,9 +109,13 @@ class Boss extends EventEmitter {
|
|
|
105
109
|
return acc;
|
|
106
110
|
}, {});
|
|
107
111
|
for (const queueGroup of Object.values(queueGroups)) {
|
|
112
|
+
if (this.#stopping)
|
|
113
|
+
return;
|
|
108
114
|
const { table, queues } = queueGroup;
|
|
109
115
|
const names = queues.map((i) => i.name);
|
|
110
116
|
while (names.length) {
|
|
117
|
+
if (this.#stopping)
|
|
118
|
+
return;
|
|
111
119
|
const chunk = names.splice(0, 100);
|
|
112
120
|
await this.#monitor(table, chunk);
|
|
113
121
|
await this.#maintain(table, chunk);
|
|
@@ -115,12 +123,18 @@ class Boss extends EventEmitter {
|
|
|
115
123
|
}
|
|
116
124
|
}
|
|
117
125
|
async #monitor(table, names) {
|
|
126
|
+
if (this.#stopping)
|
|
127
|
+
return;
|
|
118
128
|
const command = plans.trySetQueueMonitorTime(this.#config.schema, names, this.#config.monitorIntervalSeconds);
|
|
119
129
|
const { rows } = await this.#executeQuery(command);
|
|
130
|
+
if (this.#stopping)
|
|
131
|
+
return;
|
|
120
132
|
if (rows.length) {
|
|
121
133
|
const queues = rows.map((q) => q.name);
|
|
122
134
|
const cacheStatsSql = plans.cacheQueueStats(this.#config.schema, table, queues);
|
|
123
135
|
const { rows: rowsCacheStats } = await this.#executeSql(cacheStatsSql);
|
|
136
|
+
if (this.#stopping)
|
|
137
|
+
return;
|
|
124
138
|
const warnings = rowsCacheStats.filter(i => i.queuedCount > (i.warningQueueSize || WARNINGS.LARGE_QUEUE.size));
|
|
125
139
|
for (const warning of warnings) {
|
|
126
140
|
this.emit(events.warning, {
|
|
@@ -133,8 +147,12 @@ class Boss extends EventEmitter {
|
|
|
133
147
|
}
|
|
134
148
|
}
|
|
135
149
|
async #maintain(table, names) {
|
|
150
|
+
if (this.#stopping)
|
|
151
|
+
return;
|
|
136
152
|
const command = plans.trySetQueueDeletionTime(this.#config.schema, names, this.#config.maintenanceIntervalSeconds);
|
|
137
153
|
const { rows } = await this.#executeQuery(command);
|
|
154
|
+
if (this.#stopping)
|
|
155
|
+
return;
|
|
138
156
|
if (rows.length) {
|
|
139
157
|
const queues = rows.map((q) => q.name);
|
|
140
158
|
const sql = plans.deletion(this.#config.schema, table, queues);
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|