Skip to content

Commit 73618d4

Browse files
authored
refactor(improvement): use secret URI as key for cache and refactor lrucache (#12682)
1 parent 8722d99 commit 73618d4

File tree

12 files changed

+379
-73
lines changed

12 files changed

+379
-73
lines changed

apisix/cli/config.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ local _M = {
8383
lru = {
8484
secret = {
8585
ttl = 300,
86-
count = 512
86+
count = 512,
87+
neg_ttl = 60,
88+
neg_count = 512
8789
}
8890
}
8991
},

apisix/core/lrucache.lua

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,23 @@ local function new_lru_fun(opts)
9898
local refresh_stale = opts and opts.refresh_stale
9999
local serial_creating = opts and opts.serial_creating
100100
local lru_obj = lru_new(item_count)
101+
102+
local neg_lru_obj
103+
if opts and opts.neg_ttl and opts.neg_count then
104+
neg_lru_obj = lru_new(opts.neg_count)
105+
end
106+
101107
stale_obj_pool[lru_obj] = {}
102108

103109
return function (key, version, create_obj_fun, ...)
110+
-- check negative cache first
111+
if neg_lru_obj then
112+
local neg_obj = neg_lru_obj:get(key)
113+
if neg_obj and neg_obj.ver == version then
114+
return nil, neg_obj.err
115+
end
116+
end
117+
104118
if not serial_creating or not can_yield_phases[get_phase()] then
105119
local cache_obj = fetch_valid_cache(lru_obj, invalid_stale, refresh_stale,
106120
item_ttl, key, version, create_obj_fun, ...)
@@ -111,6 +125,9 @@ local function new_lru_fun(opts)
111125
local obj, err = create_obj_fun(...)
112126
if obj ~= nil then
113127
lru_obj:set(key, {val = obj, ver = version}, item_ttl)
128+
elseif neg_lru_obj then
129+
-- cache the failure in negative cache
130+
neg_lru_obj:set(key, {err = err, ver = version}, opts.neg_ttl)
114131
end
115132

116133
return obj, err
@@ -146,6 +163,9 @@ local function new_lru_fun(opts)
146163
local obj, err = create_obj_fun(...)
147164
if obj ~= nil then
148165
lru_obj:set(key, {val = obj, ver = version}, item_ttl)
166+
elseif neg_lru_obj then
167+
-- cache the failure in negative cache
168+
neg_lru_obj:set(key, {err = err, ver = version}, opts.neg_ttl)
149169
end
150170
lock:unlock()
151171
log.info("unlock with key ", key_s)

apisix/plugins/ai-aws-content-moderation.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ end
8888

8989

9090
function _M.rewrite(conf, ctx)
91-
conf = fetch_secrets(conf, true, conf, "")
91+
conf = fetch_secrets(conf, true)
9292
if not conf then
9393
return HTTP_INTERNAL_SERVER_ERROR, "failed to retrieve secrets from conf"
9494
end

apisix/plugins/authz-keycloak.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ end
764764

765765
function _M.access(conf, ctx)
766766
-- resolve secrets
767-
conf = fetch_secrets(conf, true, conf, "")
767+
conf = fetch_secrets(conf, true)
768768
local headers = core.request.headers(ctx)
769769
local need_grant_token = conf.password_grant_token_generation_incoming_uri and
770770
ctx.var.request_uri == conf.password_grant_token_generation_incoming_uri and

apisix/plugins/limit-count.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ end
3434

3535

3636
function _M.access(conf, ctx)
37-
conf = fetch_secrets(conf, true, conf, "")
37+
conf = fetch_secrets(conf, true)
3838
return limit_count.rate_limit(conf, ctx, plugin_name, 1)
3939
end
4040

apisix/plugins/openid-connect.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ end
550550

551551
function _M.rewrite(plugin_conf, ctx)
552552
local conf_clone = core.table.clone(plugin_conf)
553-
local conf = fetch_secrets(conf_clone, true, plugin_conf, "")
553+
local conf = fetch_secrets(conf_clone, true)
554554

555555
-- Previously, we multiply conf.timeout before storing it in etcd.
556556
-- If the timeout is too large, we should not multiply it again.

apisix/secret.lua

Lines changed: 66 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ local function check_secret(conf)
5151
end
5252

5353

54-
local function secret_kv(manager, confid)
54+
local function secret_kv(manager, confid)
5555
local secret_values
5656
secret_values = core.config.fetch_created_obj("/secrets")
5757
if not secret_values or not secret_values.values then
@@ -136,7 +136,7 @@ local function parse_secret_uri(secret_uri)
136136
end
137137

138138

139-
local function fetch_by_uri(secret_uri)
139+
local function fetch_by_uri_secret(secret_uri)
140140
core.log.info("fetching data from secret uri: ", secret_uri)
141141
local opts, err = parse_secret_uri(secret_uri)
142142
if not opts then
@@ -162,81 +162,93 @@ local function fetch_by_uri(secret_uri)
162162
end
163163

164164
-- for test
165-
_M.fetch_by_uri = fetch_by_uri
166-
167-
168-
local function fetch(uri)
169-
-- do a quick filter to improve retrieval speed
170-
if byte(uri, 1, 1) ~= byte('$') then
171-
return nil
172-
end
173-
174-
local val, err
175-
if string.has_prefix(upper(uri), core.env.PREFIX) then
176-
val, err = core.env.fetch_by_uri(uri)
177-
elseif string.has_prefix(uri, PREFIX) then
178-
val, err = fetch_by_uri(uri)
179-
end
180-
181-
if err then
182-
core.log.error("failed to fetch secret value: ", err)
183-
return
184-
end
185-
186-
return val
187-
end
165+
_M.fetch_by_uri = fetch_by_uri_secret
188166

189167

190168
local function new_lrucache()
191169
local ttl = core.table.try_read_attr(local_conf, "apisix", "lru", "secret", "ttl")
192170
if not ttl then
193171
ttl = 300
194172
end
173+
195174
local count = core.table.try_read_attr(local_conf, "apisix", "lru", "secret", "count")
196175
if not count then
197176
count = 512
198177
end
199-
core.log.info("secret lrucache ttl: ", ttl, ", count: ", count)
178+
179+
local neg_ttl = core.table.try_read_attr(local_conf, "apisix", "lru", "secret", "neg_ttl")
180+
if not neg_ttl then
181+
neg_ttl = 60 -- 1 minute default for failures
182+
end
183+
184+
local neg_count = core.table.try_read_attr(local_conf, "apisix", "lru", "secret", "neg_count")
185+
if not neg_count then
186+
neg_count = 512
187+
end
188+
189+
core.log.info("secret lrucache ttl: ", ttl, ", count: ", count,
190+
", neg_ttl: ", neg_ttl, ", neg_count: ", neg_count)
191+
200192
return core.lrucache.new({
201-
ttl = ttl, count = count, invalid_stale = true, refresh_stale = true
193+
ttl = ttl,
194+
count = count,
195+
neg_ttl = neg_ttl,
196+
neg_count = neg_count,
197+
invalid_stale = true,
198+
refresh_stale = true
202199
})
203200
end
204-
local secrets_lrucache = new_lrucache()
205-
206-
207-
local fetch_secrets
208-
do
209-
local retrieve_refs
210-
function retrieve_refs(refs)
211-
for k, v in pairs(refs) do
212-
local typ = type(v)
213-
if typ == "string" then
214-
refs[k] = fetch(v) or v
215-
elseif typ == "table" then
216-
retrieve_refs(v)
217-
end
218-
end
219-
return refs
220-
end
221201

222-
local function retrieve(refs)
223-
core.log.info("retrieve secrets refs")
202+
local secrets_cache = new_lrucache()
224203

225-
local new_refs = core.table.deepcopy(refs)
226-
return retrieve_refs(new_refs)
204+
205+
206+
local function fetch(uri, use_cache)
207+
-- do a quick filter to improve retrieval speed
208+
if byte(uri, 1, 1) ~= byte('$') then
209+
return nil
227210
end
228211

229-
function fetch_secrets(refs, cache, key, version)
230-
if not refs or type(refs) ~= "table" then
212+
local fetch_by_uri
213+
if string.has_prefix(upper(uri), core.env.PREFIX) then
214+
fetch_by_uri = core.env.fetch_by_uri
215+
elseif string.has_prefix(uri, PREFIX) then
216+
fetch_by_uri = fetch_by_uri_secret
217+
else
218+
return nil
219+
end
220+
221+
if not use_cache then
222+
local val, err = fetch_by_uri(uri)
223+
if err then
224+
core.log.error("failed to fetch secret value: ", err)
231225
return nil
232226
end
233-
if not cache then
234-
return retrieve(refs)
227+
return val
228+
end
229+
230+
return secrets_cache(uri, "", fetch_by_uri, uri)
231+
end
232+
233+
local function retrieve_refs(refs, use_cache)
234+
for k, v in pairs(refs) do
235+
local typ = type(v)
236+
if typ == "string" then
237+
refs[k] = fetch(v, use_cache) or v
238+
elseif typ == "table" then
239+
retrieve_refs(v, use_cache)
235240
end
236-
return secrets_lrucache(key, version, retrieve, refs)
237241
end
242+
return refs
238243
end
239244

240-
_M.fetch_secrets = fetch_secrets
245+
function _M.fetch_secrets(refs, use_cache)
246+
if not refs or type(refs) ~= "table" then
247+
return nil
248+
end
249+
250+
local new_refs = core.table.deepcopy(refs)
251+
return retrieve_refs(new_refs, use_cache)
252+
end
241253

242254
return _M

apisix/ssl/router/radixtree_sni.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ function _M.set(matched_ssl, sni)
241241
end
242242
ngx_ssl.clear_certs()
243243

244-
local new_ssl_value = secret.fetch_secrets(matched_ssl.value, true, matched_ssl.value, "")
244+
local new_ssl_value = secret.fetch_secrets(matched_ssl.value, true)
245245
or matched_ssl.value
246246

247247
ok, err = _M.set_cert_and_key(sni, new_ssl_value)

conf/config.yaml.example

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,10 @@ apisix:
148148
# fine tune the parameters of LRU cache for some features like secret
149149
lru:
150150
secret:
151-
ttl: 300 # seconds
152-
count: 512
151+
ttl: 300 # Global TTL fallback
152+
count: 512 # Cache size
153+
neg_ttl: 60 # Negative cache TTL
154+
neg_count: 512 # Negative cache size
153155
nginx_config: # Config for render the template to generate nginx.conf
154156
# user: root # Set the execution user of the worker process. This is only
155157
# effective if the master process runs with super-user privileges.

t/config-center-json/secret.t

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ qr/retrieve secrets refs/
385385
386386
387387
388-
=== TEST 14: fetch_secrets env: cache
388+
=== TEST 14: fetch_secrets env: cache (fetch data should be only called once and next call return from cache)
389389
--- main_config
390390
env secret=apisix;
391391
--- config
@@ -396,9 +396,8 @@ env secret=apisix;
396396
key = "jack",
397397
secret = "$env://secret"
398398
}
399-
local refs_1 = secret.fetch_secrets(refs, true, "key", 1)
400-
local refs_2 = secret.fetch_secrets(refs, true, "key", 1)
401-
assert(refs_1 == refs_2)
399+
local refs_1 = secret.fetch_secrets(refs, true)
400+
local refs_2 = secret.fetch_secrets(refs, true)
402401
ngx.say(refs_1.secret)
403402
ngx.say(refs_2.secret)
404403
}
@@ -409,9 +408,9 @@ GET /t
409408
apisix
410409
apisix
411410
--- grep_error_log eval
412-
qr/retrieve secrets refs/
411+
qr/fetching data from env uri/
413412
--- grep_error_log_out
414-
retrieve secrets refs
413+
fetching data from env uri
415414
416415
417416

0 commit comments

Comments
 (0)