@dbos-inc/dbos-sdk 4.26.10 → 4.27.3-preview
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/dist/schemas/system_db_schema.d.ts +4 -0
- package/dist/schemas/system_db_schema.d.ts.map +1 -1
- package/dist/schemas/system_db_schema.js.map +1 -1
- package/dist/src/adminserver.d.ts.map +1 -1
- package/dist/src/adminserver.js +6 -0
- package/dist/src/adminserver.js.map +1 -1
- package/dist/src/conductor/conductor.d.ts.map +1 -1
- package/dist/src/conductor/conductor.js +8 -0
- package/dist/src/conductor/conductor.js.map +1 -1
- package/dist/src/conductor/protocol.d.ts +4 -0
- package/dist/src/conductor/protocol.d.ts.map +1 -1
- package/dist/src/conductor/protocol.js.map +1 -1
- package/dist/src/dbos.d.ts.map +1 -1
- package/dist/src/dbos.js +3 -7
- package/dist/src/dbos.js.map +1 -1
- package/dist/src/sysdb_migrations/internal/migrations.js +10 -0
- package/dist/src/sysdb_migrations/internal/migrations.js.map +1 -1
- package/dist/src/system_database.d.ts +12 -5
- package/dist/src/system_database.d.ts.map +1 -1
- package/dist/src/system_database.js +138 -79
- package/dist/src/system_database.js.map +1 -1
- package/dist/src/wfqueue.d.ts +45 -6
- package/dist/src/wfqueue.d.ts.map +1 -1
- package/dist/src/wfqueue.js +336 -52
- package/dist/src/wfqueue.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ const pg_1 = require("pg");
|
|
|
15
15
|
const error_1 = require("./error");
|
|
16
16
|
const workflow_1 = require("./workflow");
|
|
17
17
|
const utils_1 = require("./utils");
|
|
18
|
+
const wfqueue_1 = require("./wfqueue");
|
|
18
19
|
const crypto_1 = require("crypto");
|
|
19
20
|
const utils_2 = require("./utils");
|
|
20
21
|
const database_utils_1 = require("./database_utils");
|
|
@@ -47,9 +48,15 @@ const QUEUE_COLUMN_BY_FIELD = {
|
|
|
47
48
|
rateLimitPeriodSec: 'rate_limit_period_sec',
|
|
48
49
|
priorityEnabled: 'priority_enabled',
|
|
49
50
|
partitionQueue: 'partition_queue',
|
|
51
|
+
partitionConcurrency: 'partition_concurrency',
|
|
52
|
+
partitionWorkerConcurrency: 'partition_worker_concurrency',
|
|
53
|
+
partitionRateLimitMax: 'partition_rate_limit_max',
|
|
54
|
+
partitionRateLimitPeriodSec: 'partition_rate_limit_period_sec',
|
|
50
55
|
pollingIntervalSec: 'polling_interval_sec',
|
|
51
56
|
};
|
|
52
|
-
const QUEUE_COLUMNS = 'name, concurrency, worker_concurrency, rate_limit_max, rate_limit_period_sec, priority_enabled, partition_queue,
|
|
57
|
+
const QUEUE_COLUMNS = 'name, concurrency, worker_concurrency, rate_limit_max, rate_limit_period_sec, priority_enabled, partition_queue, ' +
|
|
58
|
+
'partition_concurrency, partition_worker_concurrency, partition_rate_limit_max, partition_rate_limit_period_sec, ' +
|
|
59
|
+
'polling_interval_sec, application_name';
|
|
53
60
|
function queueRecordFromRow(row) {
|
|
54
61
|
return {
|
|
55
62
|
name: row.name,
|
|
@@ -59,6 +66,10 @@ function queueRecordFromRow(row) {
|
|
|
59
66
|
rateLimitPeriodSec: row.rate_limit_period_sec,
|
|
60
67
|
priorityEnabled: row.priority_enabled,
|
|
61
68
|
partitionQueue: row.partition_queue,
|
|
69
|
+
partitionConcurrency: row.partition_concurrency,
|
|
70
|
+
partitionWorkerConcurrency: row.partition_worker_concurrency,
|
|
71
|
+
partitionRateLimitMax: row.partition_rate_limit_max,
|
|
72
|
+
partitionRateLimitPeriodSec: row.partition_rate_limit_period_sec,
|
|
62
73
|
pollingIntervalSec: row.polling_interval_sec,
|
|
63
74
|
applicationName: row.application_name ?? undefined,
|
|
64
75
|
};
|
|
@@ -1635,7 +1646,17 @@ class SystemDatabase {
|
|
|
1635
1646
|
clearRunningWorkflow(workflowID) {
|
|
1636
1647
|
this.runningWorkflowMap.delete(workflowID);
|
|
1637
1648
|
}
|
|
1638
|
-
|
|
1649
|
+
/** Workflows this worker is running for a queue, across every partition of it. */
|
|
1650
|
+
countRunningWorkflowsForQueue(queueName) {
|
|
1651
|
+
let count = 0;
|
|
1652
|
+
for (const entry of this.runningWorkflowMap.values()) {
|
|
1653
|
+
if (entry.queueName === queueName)
|
|
1654
|
+
count++;
|
|
1655
|
+
}
|
|
1656
|
+
return count;
|
|
1657
|
+
}
|
|
1658
|
+
/** Workflows this worker is running for one partition of a queue. */
|
|
1659
|
+
countRunningWorkflowsForPartition(queueName, queuePartitionKey) {
|
|
1639
1660
|
let count = 0;
|
|
1640
1661
|
for (const entry of this.runningWorkflowMap.values()) {
|
|
1641
1662
|
if (entry.queueName === queueName && entry.queuePartitionKey === queuePartitionKey)
|
|
@@ -2334,78 +2355,93 @@ class SystemDatabase {
|
|
|
2334
2355
|
SELECT pk FROM partitions WHERE pk IS NOT NULL`, params);
|
|
2335
2356
|
return rows.map((row) => row.pk);
|
|
2336
2357
|
}
|
|
2337
|
-
async findAndMarkStartableWorkflows(queue, executorID, appVersion, queuePartitionKey) {
|
|
2338
|
-
const limiterPeriodMS = queue.rateLimit ? queue.rateLimit.periodSec * 1000 : 0;
|
|
2358
|
+
async findAndMarkStartableWorkflows(queue, executorID, appVersion, queuePartitionKey, localRunningCount = 0, partitionLocalRunningCount = 0) {
|
|
2339
2359
|
const claimedIDs = [];
|
|
2340
|
-
const
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
const
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2360
|
+
const limits = (0, wfqueue_1.resolveQueueLimits)(queue);
|
|
2361
|
+
const partitionParams = queuePartitionKey !== undefined ? [queuePartitionKey] : [];
|
|
2362
|
+
// Shares a concurrency or rate limit budget with other executors.
|
|
2363
|
+
const hasSharedBudget = limits.globalConcurrency !== undefined ||
|
|
2364
|
+
limits.partitionConcurrency !== undefined ||
|
|
2365
|
+
limits.rateLimit !== undefined ||
|
|
2366
|
+
limits.partitionRateLimit !== undefined;
|
|
2367
|
+
// Shares that budget across partitions too, so sweeps of different partitions read disjoint rows and could each spend it.
|
|
2368
|
+
const hasWriteSkew = queuePartitionKey !== undefined && (limits.globalConcurrency !== undefined || limits.rateLimit !== undefined);
|
|
2348
2369
|
const client = await this.#connect();
|
|
2349
2370
|
try {
|
|
2350
|
-
// Default to READ COMMITTED except with
|
|
2351
|
-
if (
|
|
2352
|
-
await client.query(
|
|
2371
|
+
// Default to READ COMMITTED except with a budget shared across executors
|
|
2372
|
+
if (hasSharedBudget) {
|
|
2373
|
+
await client.query(`BEGIN ISOLATION LEVEL ${hasWriteSkew ? 'SERIALIZABLE' : 'REPEATABLE READ'}`);
|
|
2353
2374
|
}
|
|
2354
2375
|
else {
|
|
2355
2376
|
await client.query('BEGIN');
|
|
2356
2377
|
}
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
const params = [
|
|
2361
|
-
queue.name,
|
|
2362
|
-
workflow_1.StatusString.ENQUEUED,
|
|
2363
|
-
workflow_1.StatusString.DELAYED,
|
|
2364
|
-
limiterPeriodMS,
|
|
2365
|
-
...partitionParams,
|
|
2366
|
-
];
|
|
2378
|
+
/** Slots left in a rate limit's rolling window, at the scope that limit applies to. */
|
|
2379
|
+
const rateLimitRemaining = async (rateLimit, partitionScoped) => {
|
|
2380
|
+
const params = [queue.name, workflow_1.StatusString.ENQUEUED, workflow_1.StatusString.DELAYED, rateLimit.periodSec * 1000];
|
|
2367
2381
|
// Count only what this application would dequeue, matching the select below.
|
|
2368
2382
|
const scope = this.#appNameFilter('application_name', this.appName, params);
|
|
2369
|
-
const
|
|
2383
|
+
const partitionFilter = partitionScoped ? `AND queue_partition_key = $${params.push(queuePartitionKey)}` : '';
|
|
2384
|
+
const { rows } = await client.query(`SELECT COUNT(*) FROM "${this.schemaName}".workflow_status
|
|
2370
2385
|
WHERE queue_name = $1
|
|
2371
2386
|
AND rate_limited = TRUE
|
|
2372
2387
|
AND status NOT IN ($2, $3)
|
|
2373
2388
|
-- Database clock on both sides, as the claim stamps started_at_epoch_ms with it.
|
|
2374
2389
|
AND started_at_epoch_ms > (EXTRACT(epoch FROM now()) * 1000)::bigint - $4
|
|
2375
2390
|
AND ${scope}
|
|
2376
|
-
${partitionFilter
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2391
|
+
${partitionFilter}`, params);
|
|
2392
|
+
return rateLimit.limitPerPeriod - Number(rows[0].count);
|
|
2393
|
+
};
|
|
2394
|
+
/**
|
|
2395
|
+
* Workflows already running, which peer workers count against too. Kept as its own query per
|
|
2396
|
+
* scope: the partition-scoped predicate rides idx_workflow_status_partition_dequeue_v2, which
|
|
2397
|
+
* a queue-wide scan loses.
|
|
2398
|
+
*/
|
|
2399
|
+
const pendingCount = async (partitionScoped) => {
|
|
2400
|
+
const params = [queue.name, workflow_1.StatusString.PENDING];
|
|
2401
|
+
const scope = this.#appNameFilter('application_name', this.appName, params);
|
|
2402
|
+
const partitionFilter = partitionScoped ? `AND queue_partition_key = $${params.push(queuePartitionKey)}` : '';
|
|
2403
|
+
const { rows } = await client.query(`SELECT COUNT(*) FROM "${this.schemaName}".workflow_status
|
|
2404
|
+
WHERE queue_name = $1 AND status = $2 AND ${scope} ${partitionFilter}`, params);
|
|
2405
|
+
return Number(rows[0]?.count ?? 0);
|
|
2406
|
+
};
|
|
2407
|
+
// Compute maxTasks, the number of workflows startable under every flow control limit on this queue.
|
|
2386
2408
|
let maxTasks = Infinity;
|
|
2387
|
-
if (
|
|
2409
|
+
if (limits.workerConcurrency !== undefined) {
|
|
2410
|
+
// Use the in-memory registry for this worker's running count — avoids a DB round trip.
|
|
2411
|
+
maxTasks = Math.min(maxTasks, Math.max(0, limits.workerConcurrency - localRunningCount));
|
|
2412
|
+
}
|
|
2413
|
+
if (limits.partitionWorkerConcurrency !== undefined) {
|
|
2414
|
+
maxTasks = Math.min(maxTasks, Math.max(0, limits.partitionWorkerConcurrency - partitionLocalRunningCount));
|
|
2415
|
+
}
|
|
2416
|
+
if (maxTasks <= 0) {
|
|
2417
|
+
await client.query('COMMIT');
|
|
2418
|
+
return claimedIDs;
|
|
2419
|
+
}
|
|
2420
|
+
if (limits.rateLimit !== undefined) {
|
|
2388
2421
|
// Bound the claim by the limiter's remaining slots so a backlogged queue locks only what it can start.
|
|
2389
|
-
maxTasks = Math.
|
|
2422
|
+
maxTasks = Math.min(maxTasks, await rateLimitRemaining(limits.rateLimit, false));
|
|
2390
2423
|
}
|
|
2391
|
-
if (
|
|
2392
|
-
|
|
2393
|
-
maxTasks = Math.min(maxTasks, Math.max(0, queue.workerConcurrency - localRunningForQueue));
|
|
2424
|
+
if (limits.partitionRateLimit !== undefined) {
|
|
2425
|
+
maxTasks = Math.min(maxTasks, await rateLimitRemaining(limits.partitionRateLimit, true));
|
|
2394
2426
|
}
|
|
2395
|
-
if (
|
|
2427
|
+
if (maxTasks <= 0) {
|
|
2428
|
+
await client.query('COMMIT');
|
|
2429
|
+
return claimedIDs;
|
|
2430
|
+
}
|
|
2431
|
+
if (limits.globalConcurrency !== undefined) {
|
|
2396
2432
|
// Global concurrency still requires a DB query since other workers may be running workflows too.
|
|
2397
|
-
const
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2433
|
+
const totalRunningTasks = await pendingCount(false);
|
|
2434
|
+
if (totalRunningTasks > limits.globalConcurrency) {
|
|
2435
|
+
this.logger.warn(`Total running tasks (${totalRunningTasks}) exceeds the global concurrency limit (${limits.globalConcurrency})`);
|
|
2436
|
+
}
|
|
2437
|
+
maxTasks = Math.min(maxTasks, Math.max(0, limits.globalConcurrency - totalRunningTasks));
|
|
2438
|
+
}
|
|
2439
|
+
if (limits.partitionConcurrency !== undefined) {
|
|
2440
|
+
const partitionRunningTasks = await pendingCount(true);
|
|
2441
|
+
if (partitionRunningTasks > limits.partitionConcurrency) {
|
|
2442
|
+
this.logger.warn(`Total running tasks (${partitionRunningTasks}) on partition ${queuePartitionKey} of queue ${queue.name} exceeds the partition concurrency limit (${limits.partitionConcurrency})`);
|
|
2406
2443
|
}
|
|
2407
|
-
|
|
2408
|
-
maxTasks = Math.min(maxTasks, availableTasks);
|
|
2444
|
+
maxTasks = Math.min(maxTasks, Math.max(0, limits.partitionConcurrency - partitionRunningTasks));
|
|
2409
2445
|
}
|
|
2410
2446
|
// Return immediately if there are no available tasks due to flow control limits
|
|
2411
2447
|
if (maxTasks <= 0) {
|
|
@@ -2420,8 +2456,7 @@ class SystemDatabase {
|
|
|
2420
2456
|
: 'application_version = $3';
|
|
2421
2457
|
// A limit shared across processes needs a consistent view of the table: NOWAIT makes an
|
|
2422
2458
|
// overlapping dequeuer abort rather than claim the next rows and spend the same budget twice.
|
|
2423
|
-
const
|
|
2424
|
-
const lockMode = sharedBudget ? 'FOR UPDATE NOWAIT' : 'FOR UPDATE SKIP LOCKED';
|
|
2459
|
+
const lockMode = hasSharedBudget ? 'FOR UPDATE NOWAIT' : 'FOR UPDATE SKIP LOCKED';
|
|
2425
2460
|
const limitClause = maxTasks !== Infinity ? `LIMIT ${maxTasks}` : '';
|
|
2426
2461
|
const selectParams = [workflow_1.StatusString.ENQUEUED, queue.name, appVersion, ...partitionParams];
|
|
2427
2462
|
const selectScope = this.#appNameFilter('application_name', this.appName, selectParams);
|
|
@@ -2432,7 +2467,7 @@ class SystemDatabase {
|
|
|
2432
2467
|
AND queue_name = $2
|
|
2433
2468
|
AND ${versionClause}
|
|
2434
2469
|
AND ${selectScope}
|
|
2435
|
-
${
|
|
2470
|
+
${queuePartitionKey !== undefined ? 'AND queue_partition_key = $4' : ''}
|
|
2436
2471
|
ORDER BY priority ASC, created_at ASC
|
|
2437
2472
|
${limitClause}
|
|
2438
2473
|
${lockMode}
|
|
@@ -2449,7 +2484,7 @@ class SystemDatabase {
|
|
|
2449
2484
|
workflow_1.StatusString.PENDING,
|
|
2450
2485
|
executorID,
|
|
2451
2486
|
appVersion,
|
|
2452
|
-
|
|
2487
|
+
limits.rateLimit !== undefined || limits.partitionRateLimit !== undefined,
|
|
2453
2488
|
workflowIDs,
|
|
2454
2489
|
workflow_1.StatusString.ENQUEUED,
|
|
2455
2490
|
// Claim an unclaimed row for this application; a nameless dequeuer leaves ownership untouched.
|
|
@@ -2492,14 +2527,18 @@ class SystemDatabase {
|
|
|
2492
2527
|
// Return the IDs of all functions we marked started
|
|
2493
2528
|
return claimedIDs;
|
|
2494
2529
|
}
|
|
2495
|
-
/** Max heads admitted per sweep: bounds dispatch, not the partition walk; lowest keys
|
|
2530
|
+
/** Max heads admitted per sweep: bounds dispatch, not the partition walk; an unconstrained sweep takes the lowest keys, so higher keys can wait under sustained load. */
|
|
2496
2531
|
partitionedDequeueSweepCap = 8192;
|
|
2497
|
-
/** Dequeue each partition's head-of-line workflow in one transaction, at most {@link partitionedDequeueSweepCap} per sweep; only valid for concurrency
|
|
2498
|
-
async findAndMarkStartablePartitionedWorkflows(queue, executorID, appVersion) {
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2532
|
+
/** Dequeue each partition's head-of-line workflow in one transaction, at most {@link partitionedDequeueSweepCap} per sweep; only valid for partition-concurrency-1 queues with no queue-wide limit. */
|
|
2533
|
+
async findAndMarkStartablePartitionedWorkflows(queue, executorID, appVersion, maxTasks = Infinity) {
|
|
2534
|
+
const limits = (0, wfqueue_1.resolveQueueLimits)(queue);
|
|
2535
|
+
if (limits.partitionConcurrency !== 1 ||
|
|
2536
|
+
limits.globalConcurrency !== undefined ||
|
|
2537
|
+
limits.rateLimit !== undefined ||
|
|
2538
|
+
limits.partitionRateLimit !== undefined) {
|
|
2539
|
+
throw new error_1.DBOSError(`Batched partitioned dequeue requires a queue with partition concurrency 1 and no queue-wide concurrency or rate limit: ${queue.name}`);
|
|
2540
|
+
}
|
|
2541
|
+
// partitionWorkerConcurrency needs no handling here: any value above 0 is capped at partition concurrency 1, which the PENDING gate already enforces globally, and 0 makes the caller's maxTasks 0.
|
|
2503
2542
|
const client = await this.#connect();
|
|
2504
2543
|
try {
|
|
2505
2544
|
await client.query('BEGIN');
|
|
@@ -2508,12 +2547,16 @@ class SystemDatabase {
|
|
|
2508
2547
|
const versionClause = (n) => isLatestVersion
|
|
2509
2548
|
? `(application_version = $${n} OR application_version IS NULL)`
|
|
2510
2549
|
: `application_version = $${n}`;
|
|
2550
|
+
// This worker's own budget bounds the sweep alongside the cap.
|
|
2551
|
+
const sweepLimit = Math.min(this.partitionedDequeueSweepCap, maxTasks);
|
|
2552
|
+
// When the worker's budget is the binding constraint, probe partitions in random order to prevent starvation.
|
|
2553
|
+
const sweepOrder = sweepLimit < this.partitionedDequeueSweepCap ? 'random()' : 'partitions.pk ASC';
|
|
2511
2554
|
const candidateParams = [
|
|
2512
2555
|
queue.name,
|
|
2513
2556
|
workflow_1.StatusString.ENQUEUED,
|
|
2514
2557
|
appVersion,
|
|
2515
2558
|
workflow_1.StatusString.PENDING,
|
|
2516
|
-
|
|
2559
|
+
sweepLimit,
|
|
2517
2560
|
];
|
|
2518
2561
|
const candidateScope = this.#appNameFilter('application_name', this.appName, candidateParams);
|
|
2519
2562
|
// Walk distinct partition keys with a recursive-CTE loose index scan (one seek per key, mirroring getQueuePartitions) so sweep cost scales with partition count, not backlog depth.
|
|
@@ -2528,30 +2571,36 @@ class SystemDatabase {
|
|
|
2528
2571
|
FROM partitions
|
|
2529
2572
|
WHERE partitions.pk IS NOT NULL)
|
|
2530
2573
|
)
|
|
2574
|
+
, chosen AS (
|
|
2575
|
+
SELECT partitions.pk
|
|
2576
|
+
FROM partitions
|
|
2577
|
+
WHERE partitions.pk IS NOT NULL
|
|
2578
|
+
-- Unscoped by design: a mutual-exclusion probe must block on any owner's row.
|
|
2579
|
+
AND NOT EXISTS (
|
|
2580
|
+
SELECT 1
|
|
2581
|
+
FROM "${this.schemaName}".workflow_status
|
|
2582
|
+
WHERE queue_name = $1 AND status = $4
|
|
2583
|
+
AND queue_partition_key IS NOT NULL AND queue_partition_key = partitions.pk
|
|
2584
|
+
)
|
|
2585
|
+
ORDER BY ${sweepOrder}
|
|
2586
|
+
LIMIT $5
|
|
2587
|
+
)
|
|
2531
2588
|
SELECT head.workflow_uuid
|
|
2532
|
-
FROM
|
|
2589
|
+
FROM chosen
|
|
2533
2590
|
-- LATERAL plans as a tight nested loop; a correlated scalar subquery runs as a slower per-row SubPlan.
|
|
2534
2591
|
JOIN LATERAL (
|
|
2535
2592
|
SELECT workflow_uuid
|
|
2536
2593
|
FROM "${this.schemaName}".workflow_status
|
|
2537
2594
|
WHERE queue_name = $1 AND status = $2
|
|
2538
|
-
AND queue_partition_key =
|
|
2595
|
+
AND queue_partition_key = chosen.pk
|
|
2539
2596
|
AND ${versionClause(3)}
|
|
2540
2597
|
AND ${candidateScope}
|
|
2541
2598
|
-- workflow_uuid totalizes the head order (same head for every worker under created_at ties) and the index's trailing workflow_uuid keeps this a pure top-1 probe.
|
|
2542
2599
|
ORDER BY priority ASC, created_at ASC, workflow_uuid ASC
|
|
2543
2600
|
LIMIT 1
|
|
2544
2601
|
) head ON TRUE
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
AND NOT EXISTS (
|
|
2548
|
-
SELECT 1
|
|
2549
|
-
FROM "${this.schemaName}".workflow_status
|
|
2550
|
-
WHERE queue_name = $1 AND status = $4
|
|
2551
|
-
AND queue_partition_key IS NOT NULL AND queue_partition_key = partitions.pk
|
|
2552
|
-
)
|
|
2553
|
-
ORDER BY partitions.pk ASC
|
|
2554
|
-
LIMIT $5`, candidateParams);
|
|
2602
|
+
-- Which partitions were chosen is settled above; order the result so the claim, and the dispatch it feeds, are deterministic.
|
|
2603
|
+
ORDER BY chosen.pk ASC`, candidateParams);
|
|
2555
2604
|
const candidateIDs = candidateResult.rows.map((row) => row.workflow_uuid);
|
|
2556
2605
|
if (candidateIDs.length === 0) {
|
|
2557
2606
|
await client.query('COMMIT');
|
|
@@ -3483,6 +3532,10 @@ class SystemDatabase {
|
|
|
3483
3532
|
rate_limit_period_sec = EXCLUDED.rate_limit_period_sec,
|
|
3484
3533
|
priority_enabled = EXCLUDED.priority_enabled,
|
|
3485
3534
|
partition_queue = EXCLUDED.partition_queue,
|
|
3535
|
+
partition_concurrency = EXCLUDED.partition_concurrency,
|
|
3536
|
+
partition_worker_concurrency = EXCLUDED.partition_worker_concurrency,
|
|
3537
|
+
partition_rate_limit_max = EXCLUDED.partition_rate_limit_max,
|
|
3538
|
+
partition_rate_limit_period_sec = EXCLUDED.partition_rate_limit_period_sec,
|
|
3486
3539
|
polling_interval_sec = EXCLUDED.polling_interval_sec,
|
|
3487
3540
|
updated_at = EXCLUDED.updated_at,
|
|
3488
3541
|
-- Claim only an unclaimed row, so a registration landing between the check above and this write keeps the name it just took.
|
|
@@ -3497,8 +3550,10 @@ class SystemDatabase {
|
|
|
3497
3550
|
const resolvedOwner = await this.#resolveRowOwner(client, 'queues', 'name', record.name, owner, 'Queue');
|
|
3498
3551
|
await client.query(`INSERT INTO "${this.schemaName}".queues
|
|
3499
3552
|
(name, concurrency, worker_concurrency, rate_limit_max, rate_limit_period_sec,
|
|
3500
|
-
priority_enabled, partition_queue,
|
|
3501
|
-
|
|
3553
|
+
priority_enabled, partition_queue, partition_concurrency, partition_worker_concurrency,
|
|
3554
|
+
partition_rate_limit_max, partition_rate_limit_period_sec,
|
|
3555
|
+
polling_interval_sec, updated_at, application_name)
|
|
3556
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
|
3502
3557
|
${onConflict}`, [
|
|
3503
3558
|
record.name,
|
|
3504
3559
|
record.concurrency,
|
|
@@ -3507,6 +3562,10 @@ class SystemDatabase {
|
|
|
3507
3562
|
record.rateLimitPeriodSec,
|
|
3508
3563
|
record.priorityEnabled,
|
|
3509
3564
|
record.partitionQueue,
|
|
3565
|
+
record.partitionConcurrency,
|
|
3566
|
+
record.partitionWorkerConcurrency,
|
|
3567
|
+
record.partitionRateLimitMax,
|
|
3568
|
+
record.partitionRateLimitPeriodSec,
|
|
3510
3569
|
record.pollingIntervalSec,
|
|
3511
3570
|
now,
|
|
3512
3571
|
resolvedOwner ?? null,
|