@@ -67,8 +67,10 @@ const SemanticAttributes = {
6767 * __gatesHaveCapacity / __gatesAcquire run on admit paths behind the gatesEnabled
6868 * flag. __gatesRelease runs on every release path UNCONDITIONALLY and is payload-
6969 * driven (a cheap substring probe before decoding), so slots acquired while the flag
70- * was on always drain, and gateless messages pay near zero. __gateReconcile is the
71- * same bounded self-heal as the total-cap gate. Group and gate sets are strict
70+ * was on always drain, and gateless messages pay near zero. Every holder joins the
71+ * gate's groupConcurrency set — keyed or keyless — so a totalConcurrency limit caps
72+ * across everything, matching the SDK's documented "total" semantics. __gateReconcile
73+ * is the same bounded self-heal as the total-cap gate. Group and gate sets are strict
7274 * mirrors of the member's home-queue currentConcurrency set (admits populate both
7375 * in one script), so a member whose message key is gone, or who is absent from its
7476 * home currentConcurrency set, holds no legitimate slot and is pruned. The home-set
@@ -124,15 +126,13 @@ local function __gatesHaveCapacity(gatesKeyPrefix, msg, messageId, envLimit, msg
124126 __gateReconcile(variant .. ':currentConcurrency', msgKeyPrefix, gatesKeyPrefix)
125127 return false
126128 end
127- if gateKey and gateKey ~= '' then
128- local rawTotal = redis.call('GET', base .. ':totalConcurrency')
129- if rawTotal then
130- local totalLimit = math.min(tonumber(rawTotal), envLimit)
131- local groupKey = base .. ':groupConcurrency'
132- if tonumber(redis.call('SCARD', groupKey) or '0') >= totalLimit and redis.call('SISMEMBER', groupKey, messageId) == 0 then
133- __gateReconcile(groupKey, msgKeyPrefix, gatesKeyPrefix)
134- return false
135- end
129+ local rawTotal = redis.call('GET', base .. ':totalConcurrency')
130+ if rawTotal then
131+ local totalLimit = math.min(tonumber(rawTotal), envLimit)
132+ local groupKey = base .. ':groupConcurrency'
133+ if tonumber(redis.call('SCARD', groupKey) or '0') >= totalLimit and redis.call('SISMEMBER', groupKey, messageId) == 0 then
134+ __gateReconcile(groupKey, msgKeyPrefix, gatesKeyPrefix)
135+ return false
136136 end
137137 end
138138 end
142142local function __gatesAcquire(gatesKeyPrefix, msg, messageId)
143143 if not msg.gates then return end
144144 for _, gate in ipairs(msg.gates) do
145- local base, variant, gateKey = __gateKeys(gatesKeyPrefix, msg, gate)
145+ local base, variant = __gateKeys(gatesKeyPrefix, msg, gate)
146146 redis.call('SADD', variant .. ':currentConcurrency', messageId)
147- if gateKey and gateKey ~= '' then
148- redis.call('SADD', base .. ':groupConcurrency', messageId)
149- end
147+ redis.call('SADD', base .. ':groupConcurrency', messageId)
150148 end
151149end
152150
@@ -156,9 +154,9 @@ local function __gatesRelease(gatesKeyPrefix, rawPayload, messageId)
156154 local ok, msg = pcall(cjson.decode, rawPayload)
157155 if not ok or type(msg) ~= 'table' or not msg.gates then return end
158156 for _, gate in ipairs(msg.gates) do
159- local base, variant, gateKey = __gateKeys(gatesKeyPrefix, msg, gate)
157+ local base, variant = __gateKeys(gatesKeyPrefix, msg, gate)
160158 local removed = redis.call('SREM', variant .. ':currentConcurrency', messageId)
161- if removed == 1 and gateKey and gateKey ~= '' then
159+ if removed == 1 then
162160 redis.call('SREM', base .. ':groupConcurrency', messageId)
163161 end
164162 end
0 commit comments