AlinsRan opened a new pull request, #14141:
URL: https://github.com/apache/apisix/pull/14141
### Description
`limit-count` with templated keys or counts (for example `key:
"${host}:${uri}"`, `count: "${http_count ?? 3}"`) did per-request work that
only depends on the configuration.
`core.utils.resolve_var` ran `ngx.re.gsub` with a function replacement on
every call. With a function replacement, lua-resty-core allocates a fresh 4 KB
output buffer per call and a captures table per match. The callback then split
every variable on `??` with `ngx.re.split`, which had no `o` flag, so that
regex was compiled again on every call.
`limit-count` created a new limiter object for every request whenever
`count` or `time_window` is a template or `rules` is used. It also rebuilt the
rate-limit header names (three `gsub` calls for `header_prefix`) and the
counter key prefix on every request.
Changes:
- `resolve_var` splits a template once into literal strings and variables
(name, whether `??` is present, the default) and caches the result in an LRU of
1024 templates. A call walks the segments into a buffer taken from `tablepool`.
Return values, the resolved-variable count, the escaper and the `??` cases
behave as before.
- With the `local` policy, a limiter resolved per request is cached by
plugin name, resolved count, time window and window type. A local limiter keeps
its counters in the shared dict under the request key, so sharing one between
requests and routes does not change any count. Redis policies are unchanged.
- Rate-limit header names are cached in a bounded LRU keyed by the prefix
and the base names, which come from the plugin configuration and plugin
metadata.
- The counter key prefix is cached per plugin conf table in a weak-keyed
table. `conf_version` is memoized on the conf and `_meta.parent` is only set
once, so the prefix of a conf never changes.
One bug is fixed along the way: a variable getter that itself calls
`resolve_var` used to cut the outer result short, because the old code kept the
context and the resolved count in module-level upvalues (`t/core/utils.t` TEST
17).
Microbenchmark of `resolve_var` (LuaJIT, results kept alive, GC stopped
while measuring allocation):
| template | before | after |
|---|---|---|
| `${host}:${uri}` | 1.2–2.0 µs, 4432 B | 0.06 µs, 0 B |
| `${dynamic_limit_value}` | 0.7–1.5 µs, 4288 B | 0.05 µs, 0 B |
| `${http_x ?? 10}` | 0.8–1.5 µs, 4368 B | 0.05 µs, 0 B |
Tests:
- `t/core/utils.t` TEST 14–17: resolved-variable counts and `??` cases, the
escaper, one template resolved with different values, and the re-entrant
getter. TEST 14–16 also pass on master, TEST 17 fails there with `in-x`. TEST
18: a getter that raises leaves no state behind.
- `t/plugin/limit-count-rules.t` TEST 23–29: one key limited under counts
that change per request, a count that fails to resolve rejecting only that
request, two routes with the same rule keeping separate counters, a
configuration change starting a new counter, header names following a plugin
metadata change, and a templated count without `rules`.
#### Which issue(s) this PR fixes:
None.
### Checklist
- [x] I have explained the need for this PR and the problem it solves
- [x] I have explained the changes or the new features added to this PR
- [x] I have added tests corresponding to this change
- [ ] I have updated the documentation to reflect this change
- [x] I have verified that this change is backward compatible (If not,
please discuss on the [APISIX mailing
list](https://github.com/apache/apisix/tree/master#community) first)
--
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]