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]