Skip to content

Commit 2c1ba75

Browse files
committed
fix(run-engine,webapp): flag-consistent gate overrides; generation-safe cap cleanup
Gate admission reads a per-key override only when concurrency limit enforcement is enabled, matching the primary admit paths (the flag now threads through the unkeyed enqueue and dequeue scripts too). The cap-rejection cleanup deletes only the exact row generation the rejected request wrote, so a concurrent request that succeeded after capacity freed keeps its durable record.
1 parent 3924308 commit 2c1ba75

2 files changed

Lines changed: 26 additions & 12 deletions

File tree

‎apps/webapp/app/v3/services/concurrencySystem.server.ts‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ function overrideQueueConcurrencyKeyLimit(
537537
}),
538538
(error) => ({ type: "other" as const, cause: error })
539539
)
540-
.andThen((existing) =>
540+
.andThen(() =>
541541
fromPromise(
542542
db.taskQueueConcurrencyKeyOverride.upsert({
543543
where: { taskQueueId_concurrencyKey: { taskQueueId: queue.id, concurrencyKey } },
@@ -557,9 +557,9 @@ function overrideQueueConcurrencyKeyLimit(
557557
type: "queue_update_failed" as const,
558558
cause: error,
559559
})
560-
).map(() => existing)
560+
)
561561
)
562-
.andThen((existing) =>
562+
.andThen((written) =>
563563
fromPromise(
564564
engine.runQueue.updateQueueConcurrencyKeyLimit(
565565
environment,
@@ -576,8 +576,13 @@ function overrideQueueConcurrencyKeyLimit(
576576
).orElse((error) => {
577577
if (error.type === "too_many_key_overrides") {
578578
return fromPromise(
579+
/**
580+
* Deletes only the exact row generation this request wrote, so a
581+
* concurrent request that succeeded after capacity freed keeps its
582+
* durable record.
583+
*/
579584
db.taskQueueConcurrencyKeyOverride.deleteMany({
580-
where: { taskQueueId: queue.id, concurrencyKey },
585+
where: { id: written.id, overriddenAt: written.overriddenAt },
581586
}),
582587
() => error
583588
).andThen(() => errAsync(error));

‎internal-packages/run-engine/src/run-queue/index.ts‎

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,13 @@ local function __gateReconcile(setKey, msgKeyPrefix, reconcileKeyPrefix)
116116
end
117117
end
118118
119-
local function __gatesHaveCapacity(gatesKeyPrefix, msg, messageId, envLimit, msgKeyPrefix)
119+
local function __gatesHaveCapacity(gatesKeyPrefix, msg, messageId, envLimit, msgKeyPrefix, ckOverridesEnabled)
120120
if not msg.gates then return true end
121121
for _, gate in ipairs(msg.gates) do
122122
local base, variant, gateKey = __gateKeys(gatesKeyPrefix, msg, gate)
123123
local occupancy = tonumber(redis.call('SCARD', variant .. ':currentConcurrency') or '0')
124124
local perKeyLimit = math.min(tonumber(redis.call('GET', base .. ':concurrency') or '1000000'), envLimit)
125-
if gateKey and gateKey ~= '' then
125+
if ckOverridesEnabled and gateKey and gateKey ~= '' then
126126
local gateOverride = redis.call('HGET', base .. ':ckLimits', string.sub(variant, #gatesKeyPrefix + 1))
127127
if gateOverride then
128128
perKeyLimit = math.min(tonumber(gateOverride), envLimit)
@@ -2571,6 +2571,7 @@ export class RunQueue {
25712571
enableFastPathArg,
25722572
this.options.redis.keyPrefix ?? "",
25732573
this.options.gatesEnabled ? "1" : "0",
2574+
this.options.totalConcurrencyEnabled ? "1" : "0",
25742575
metricsGaugeArg
25752576
);
25762577
} else {
@@ -2600,6 +2601,7 @@ export class RunQueue {
26002601
enableFastPathArg,
26012602
this.options.redis.keyPrefix ?? "",
26022603
this.options.gatesEnabled ? "1" : "0",
2604+
this.options.totalConcurrencyEnabled ? "1" : "0",
26032605
metricsGaugeArg
26042606
);
26052607
}
@@ -2681,6 +2683,7 @@ export class RunQueue {
26812683
this.options.redis.keyPrefix ?? "",
26822684
String(maxCount),
26832685
this.options.gatesEnabled ? "1" : "0",
2686+
this.options.totalConcurrencyEnabled ? "1" : "0",
26842687
metricsGaugeArg
26852688
);
26862689

@@ -3678,6 +3681,7 @@ local currentTime = ARGV[8]
36783681
local enableFastPath = ARGV[9]
36793682
local keyPrefix = ARGV[10]
36803683
local gatesEnabled = ARGV[11] == '1'
3684+
local totalConcurrencyEnabled = ARGV[12] == '1'
36813685
36823686
${QUEUE_METRICS_GAUGE_PRELUDE}
36833687
${QUEUE_GATES_LUA_HELPERS}
@@ -3705,7 +3709,7 @@ if enableFastPath == '1' then
37053709
local okDecode, decoded = pcall(cjson.decode, messageData)
37063710
if okDecode and type(decoded) == 'table' and decoded.gates then
37073711
gateMsg = decoded
3708-
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil)
3712+
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil, totalConcurrencyEnabled)
37093713
end
37103714
end
37113715
@@ -3792,6 +3796,7 @@ local currentTime = ARGV[10]
37923796
local enableFastPath = ARGV[11]
37933797
local keyPrefix = ARGV[12]
37943798
local gatesEnabled = ARGV[13] == '1'
3799+
local totalConcurrencyEnabled = ARGV[14] == '1'
37953800
37963801
${QUEUE_METRICS_GAUGE_PRELUDE}
37973802
${QUEUE_GATES_LUA_HELPERS}
@@ -3819,7 +3824,7 @@ if enableFastPath == '1' then
38193824
local okDecode, decoded = pcall(cjson.decode, messageData)
38203825
if okDecode and type(decoded) == 'table' and decoded.gates then
38213826
gateMsg = decoded
3822-
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil)
3827+
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil, totalConcurrencyEnabled)
38233828
end
38243829
end
38253830
@@ -4186,7 +4191,7 @@ if enableFastPath == '1' then
41864191
local okDecode, decoded = pcall(cjson.decode, messageData)
41874192
if okDecode and type(decoded) == 'table' and decoded.gates then
41884193
gateMsg = decoded
4189-
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil)
4194+
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil, totalConcurrencyEnabled)
41904195
end
41914196
end
41924197
@@ -4364,7 +4369,7 @@ if enableFastPath == '1' then
43644369
local okDecode, decoded = pcall(cjson.decode, messageData)
43654370
if okDecode and type(decoded) == 'table' and decoded.gates then
43664371
gateMsg = decoded
4367-
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil)
4372+
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil, totalConcurrencyEnabled)
43684373
end
43694374
end
43704375
@@ -4694,6 +4699,7 @@ local defaultEnvConcurrencyBurstFactor = ARGV[4]
46944699
local keyPrefix = ARGV[5]
46954700
local maxCount = tonumber(ARGV[6] or '1')
46964701
local gatesEnabled = ARGV[7] == '1'
4702+
local totalConcurrencyEnabled = ARGV[8] == '1'
46974703
${QUEUE_METRICS_GAUGE_PRELUDE}
46984704
${QUEUE_GATES_LUA_HELPERS}
46994705
${QUEUE_METRICS_GAUGE_LUA}
@@ -4766,7 +4772,7 @@ for i = 1, #messages, 2 do
47664772
else
47674773
local gatesAllow = true
47684774
if gatesEnabled then
4769-
gatesAllow = __gatesHaveCapacity(keyPrefix, messageData, messageId, envConcurrencyLimit, messageKeyPrefix)
4775+
gatesAllow = __gatesHaveCapacity(keyPrefix, messageData, messageId, envConcurrencyLimit, messageKeyPrefix, totalConcurrencyEnabled)
47704776
end
47714777
47724778
if gatesAllow then
@@ -5115,7 +5121,7 @@ for _, ckQueueName in ipairs(ckQueues) do
51155121
else
51165122
local gatesAllow = true
51175123
if gatesEnabled then
5118-
gatesAllow = __gatesHaveCapacity(keyPrefix, messageData, messageId, envConcurrencyLimit, messageKeyPrefix)
5124+
gatesAllow = __gatesHaveCapacity(keyPrefix, messageData, messageId, envConcurrencyLimit, messageKeyPrefix, totalConcurrencyEnabled)
51195125
end
51205126
if not gatesAllow then
51215127
blockedByGates = true
@@ -6223,6 +6229,7 @@ declare module "@internal/redis" {
62236229
enableFastPath: string,
62246230
keyPrefix: string,
62256231
gatesEnabled: string,
6232+
totalConcurrencyEnabled: string,
62266233
metricsEnabled: string,
62276234
callback?: Callback<[number, number[] | null]>
62286235
): Result<[number, number[] | null], Context>;
@@ -6256,6 +6263,7 @@ declare module "@internal/redis" {
62566263
enableFastPath: string,
62576264
keyPrefix: string,
62586265
gatesEnabled: string,
6266+
totalConcurrencyEnabled: string,
62596267
metricsEnabled: string,
62606268
callback?: Callback<[number, number[] | null]>
62616269
): Result<[number, number[] | null], Context>;
@@ -6294,6 +6302,7 @@ declare module "@internal/redis" {
62946302
keyPrefix: string,
62956303
maxCount: string,
62966304
gatesEnabled: string,
6305+
totalConcurrencyEnabled: string,
62976306
metricsEnabled: string,
62986307
callback?: Callback<[string[] | null, number[] | null]>
62996308
): Result<[string[] | null, number[] | null], Context>;

0 commit comments

Comments
 (0)