nic-6443 commented on code in PR #14047:
URL: https://github.com/apache/apisix/pull/14047#discussion_r4291341265


##########
apisix/secret.lua:
##########
@@ -171,6 +176,55 @@ end
 _M.fetch_by_uri = fetch_by_uri_secret
 
 
+-- A provider resolves one field per call but reads the whole container (a
+-- vault path, a JSON secret, ...) to do it, so a certificate and its key are
+-- two reads that a rotation can fall between. This cache keeps a container 
read
+-- for a moment and makes concurrent readers in the worker share the read in
+-- flight, so fields resolved together come from the same read.
+local CONTAINER_TTL = 1
+local INFLIGHT_WAIT = 10
+
+function _M.new_container_cache()
+    local lru = lru_new(64)
+    local inflight = {}
+
+    return function (key, ver, read, ...)
+        local hit = lru:get(key)
+        if hit and hit.ver == ver then
+            return hit.val
+        end
+
+        local pending = inflight[key]
+        if pending and pending.ver == ver then
+            if pending.sema:wait(INFLIGHT_WAIT) then
+                return pending.val, pending.err, pending.extra
+            end
+            -- the reader may have been aborted along with its request
+            if inflight[key] == pending then
+                inflight[key] = nil
+            end
+        end
+
+        pending = {ver = ver, sema = semaphore.new()}
+        inflight[key] = pending
+        local val, err, extra = read(...)
+        pending.val, pending.err, pending.extra = val, err, extra
+        if inflight[key] == pending then
+            inflight[key] = nil
+        end
+        if val ~= nil then
+            lru:set(key, {val = val, ver = ver}, CONTAINER_TTL)
+        end
+
+        local waiting = -pending.sema:count()
+        if waiting > 0 then
+            pending.sema:post(waiting)
+        end
+        return val, err, extra
+    end

Review Comment:
   I don't think this race can happen. Everything after the read returns runs 
without a yield: store the result on `pending`, clear `inflight[key]`, 
`lru:set`, then `count()` and `post()`. None of those can yield, so no other 
coroutine runs between the `count()` snapshot and the `post()`, and nobody can 
start waiting in that window.
   
   A caller can only get hold of `pending` through `inflight[key]`. That entry 
is cleared in the same non-yielding step, before the count, so anyone arriving 
later either hits the lru entry or creates a fresh `pending` with its own read. 
It never waits on the finished one. The head of this PR also moved the read 
into a timer under `pcall`, so every exit of the read goes through this same 
release path.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to