Skip to content

Commit 16bf6f5

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 28314fb commit 16bf6f5

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
@@ -114,13 +114,13 @@ local function __gateReconcile(setKey, msgKeyPrefix, reconcileKeyPrefix)
114114
end
115115
end
116116
117-
local function __gatesHaveCapacity(gatesKeyPrefix, msg, messageId, envLimit, msgKeyPrefix)
117+
local function __gatesHaveCapacity(gatesKeyPrefix, msg, messageId, envLimit, msgKeyPrefix, ckOverridesEnabled)
118118
if not msg.gates then return true end
119119
for _, gate in ipairs(msg.gates) do
120120
local base, variant, gateKey = __gateKeys(gatesKeyPrefix, msg, gate)
121121
local occupancy = tonumber(redis.call('SCARD', variant .. ':currentConcurrency') or '0')
122122
local perKeyLimit = math.min(tonumber(redis.call('GET', base .. ':concurrency') or '1000000'), envLimit)
123-
if gateKey and gateKey ~= '' then
123+
if ckOverridesEnabled and gateKey and gateKey ~= '' then
124124
local gateOverride = redis.call('HGET', base .. ':ckLimits', string.sub(variant, #gatesKeyPrefix + 1))
125125
if gateOverride then
126126
perKeyLimit = math.min(tonumber(gateOverride), envLimit)
@@ -2573,6 +2573,7 @@ export class RunQueue {
25732573
enableFastPathArg,
25742574
this.options.redis.keyPrefix ?? "",
25752575
this.options.gatesEnabled ? "1" : "0",
2576+
this.options.totalConcurrencyEnabled ? "1" : "0",
25762577
metricsGaugeArg
25772578
);
25782579
} else {
@@ -2602,6 +2603,7 @@ export class RunQueue {
26022603
enableFastPathArg,
26032604
this.options.redis.keyPrefix ?? "",
26042605
this.options.gatesEnabled ? "1" : "0",
2606+
this.options.totalConcurrencyEnabled ? "1" : "0",
26052607
metricsGaugeArg
26062608
);
26072609
}
@@ -2683,6 +2685,7 @@ export class RunQueue {
26832685
this.options.redis.keyPrefix ?? "",
26842686
String(maxCount),
26852687
this.options.gatesEnabled ? "1" : "0",
2688+
this.options.totalConcurrencyEnabled ? "1" : "0",
26862689
metricsGaugeArg
26872690
);
26882691

@@ -3680,6 +3683,7 @@ local currentTime = ARGV[8]
36803683
local enableFastPath = ARGV[9]
36813684
local keyPrefix = ARGV[10]
36823685
local gatesEnabled = ARGV[11] == '1'
3686+
local totalConcurrencyEnabled = ARGV[12] == '1'
36833687
36843688
${QUEUE_METRICS_GAUGE_PRELUDE}
36853689
${QUEUE_GATES_LUA_HELPERS}
@@ -3707,7 +3711,7 @@ if enableFastPath == '1' then
37073711
local okDecode, decoded = pcall(cjson.decode, messageData)
37083712
if okDecode and type(decoded) == 'table' and decoded.gates then
37093713
gateMsg = decoded
3710-
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil)
3714+
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil, totalConcurrencyEnabled)
37113715
end
37123716
end
37133717
@@ -3794,6 +3798,7 @@ local currentTime = ARGV[10]
37943798
local enableFastPath = ARGV[11]
37953799
local keyPrefix = ARGV[12]
37963800
local gatesEnabled = ARGV[13] == '1'
3801+
local totalConcurrencyEnabled = ARGV[14] == '1'
37973802
37983803
${QUEUE_METRICS_GAUGE_PRELUDE}
37993804
${QUEUE_GATES_LUA_HELPERS}
@@ -3821,7 +3826,7 @@ if enableFastPath == '1' then
38213826
local okDecode, decoded = pcall(cjson.decode, messageData)
38223827
if okDecode and type(decoded) == 'table' and decoded.gates then
38233828
gateMsg = decoded
3824-
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil)
3829+
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil, totalConcurrencyEnabled)
38253830
end
38263831
end
38273832
@@ -4188,7 +4193,7 @@ if enableFastPath == '1' then
41884193
local okDecode, decoded = pcall(cjson.decode, messageData)
41894194
if okDecode and type(decoded) == 'table' and decoded.gates then
41904195
gateMsg = decoded
4191-
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil)
4196+
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil, totalConcurrencyEnabled)
41924197
end
41934198
end
41944199
@@ -4366,7 +4371,7 @@ if enableFastPath == '1' then
43664371
local okDecode, decoded = pcall(cjson.decode, messageData)
43674372
if okDecode and type(decoded) == 'table' and decoded.gates then
43684373
gateMsg = decoded
4369-
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil)
4374+
gatesAllowFastPath = __gatesHaveCapacity(keyPrefix, decoded, messageId, envLimit, nil, totalConcurrencyEnabled)
43704375
end
43714376
end
43724377
@@ -4696,6 +4701,7 @@ local defaultEnvConcurrencyBurstFactor = ARGV[4]
46964701
local keyPrefix = ARGV[5]
46974702
local maxCount = tonumber(ARGV[6] or '1')
46984703
local gatesEnabled = ARGV[7] == '1'
4704+
local totalConcurrencyEnabled = ARGV[8] == '1'
46994705
${QUEUE_METRICS_GAUGE_PRELUDE}
47004706
${QUEUE_GATES_LUA_HELPERS}
47014707
${QUEUE_METRICS_GAUGE_LUA}
@@ -4768,7 +4774,7 @@ for i = 1, #messages, 2 do
47684774
else
47694775
local gatesAllow = true
47704776
if gatesEnabled then
4771-
gatesAllow = __gatesHaveCapacity(keyPrefix, messageData, messageId, envConcurrencyLimit, messageKeyPrefix)
4777+
gatesAllow = __gatesHaveCapacity(keyPrefix, messageData, messageId, envConcurrencyLimit, messageKeyPrefix, totalConcurrencyEnabled)
47724778
end
47734779
47744780
if gatesAllow then
@@ -5117,7 +5123,7 @@ for _, ckQueueName in ipairs(ckQueues) do
51175123
else
51185124
local gatesAllow = true
51195125
if gatesEnabled then
5120-
gatesAllow = __gatesHaveCapacity(keyPrefix, messageData, messageId, envConcurrencyLimit, messageKeyPrefix)
5126+
gatesAllow = __gatesHaveCapacity(keyPrefix, messageData, messageId, envConcurrencyLimit, messageKeyPrefix, totalConcurrencyEnabled)
51215127
end
51225128
if not gatesAllow then
51235129
blockedByGates = true
@@ -6225,6 +6231,7 @@ declare module "@internal/redis" {
62256231
enableFastPath: string,
62266232
keyPrefix: string,
62276233
gatesEnabled: string,
6234+
totalConcurrencyEnabled: string,
62286235
metricsEnabled: string,
62296236
callback?: Callback<[number, number[] | null]>
62306237
): Result<[number, number[] | null], Context>;
@@ -6258,6 +6265,7 @@ declare module "@internal/redis" {
62586265
enableFastPath: string,
62596266
keyPrefix: string,
62606267
gatesEnabled: string,
6268+
totalConcurrencyEnabled: string,
62616269
metricsEnabled: string,
62626270
callback?: Callback<[number, number[] | null]>
62636271
): Result<[number, number[] | null], Context>;
@@ -6296,6 +6304,7 @@ declare module "@internal/redis" {
62966304
keyPrefix: string,
62976305
maxCount: string,
62986306
gatesEnabled: string,
6307+
totalConcurrencyEnabled: string,
62996308
metricsEnabled: string,
63006309
callback?: Callback<[string[] | null, number[] | null]>
63016310
): Result<[string[] | null, number[] | null], Context>;

0 commit comments

Comments
 (0)