AlinsRan opened a new pull request, #13779: URL: https://github.com/apache/apisix/pull/13779
### Description `run_global_rules()` rebuilds the same plugin set on every phase, and `_M.filter()` scans every loaded plugin to do it. With a `global_rule` enabling a single plugin and ~108 plugins loaded, one plain request walks the full plugin list 9 times. Two independent changes, one commit each so their contributions can be judged separately. **1. Iterate the configured plugins instead of every loaded plugin** `_M.filter()` loops `local_plugins` and asks whether the resource configured each one, discarding 107 of 108 lookups. The fast path loops `user_plugin_conf` instead and resolves names through `local_plugins_hash`. The slow path is kept for resources configuring a large fraction of the loaded set, where the added sort would outweigh the saved scan. Ordering is load-bearing and the two paths derive it differently: the slow path inherits it from the already-sorted `local_plugins`, while the fast path sees `pairs()` order and must sort it back. `sort_plugin` compares priority alone and `sort_tab` is not stable, so plugins sharing a priority could come out differently. `local_plugins_order` records each plugin's position and `sort_matched_plugin` falls back to it, so both paths agree. **2. Reuse the filtered global-rule set across phases** Route plugins are computed once and reused through `api_ctx.plugins`; global rules were not. The result is now cached on `api_ctx`, keyed by the merged rule's `modifiedIndex` and by the matched route. The route belongs in the key: plugins declaring `run_policy = "prefer_route"` are skipped when the route configures the same plugin, and `api_ctx.matched_route` is replaced when a consumer's configuration is merged in — after `rewrite` has run but before `access`. Since the table now outlives a phase, it moves to the `global_plugins` pool and is released in `http_log_phase` instead of at the end of every phase. ### Measurements Measured on an APISIX 3.2-based fork carrying the same two changes, single worker, CPU-pinned (worker and load generator on separate cores), wrk2, 5 runs each, median. `global_rule` enabling prometheus: | build | rps | |---|---| | baseline | 27,754 | | iterate configured plugins | 32,723 (+17.9%) | | + cross-phase reuse | measured separately at +12.2% on its own baseline | The two numbers come from different runs with different baselines and are **not** additive — the second change cuts the number of `filter()` calls per request from 9 to 1, which necessarily shrinks what the first one saves. I have not measured the 2x2 matrix that would separate them properly. Both are also unmeasured on master itself. ### Tests - `t/plugin/filter-fast-path.t` — the fast path yields the same order as `local_plugins` - `t/plugin/global-rule-phase-cache.t` — every phase still runs off the cached set; `body_filter` still runs per response buffer on a chunked response; the set is rebuilt when the global rule changes **Known gap:** the fork also carries a case proving the cached set is rebuilt when a consumer merge replaces `matched_route` (verified there by deleting the route key and watching it fail). It observes `prefer_route` through prometheus metrics, and I could not port it: `Test::Nginx` restarts nginx between blocks with differing `--- config`, and the metrics live in a per-process shared dict, so cross-block observation resets. Since `prometheus` and `skywalking` are the only `prefer_route` plugins, I did not find another observation point. The route cache key is therefore untested here — pointers welcome. Equal-priority ordering is likewise uncovered: all 108 built-in plugins have unique priorities, and constructing a collision needs a custom plugin. The tie-breaker guards custom plugins. ### Checklist - [x] I have explained why we need this PR and what problem it solves - [x] I have explained the changes in this PR - [x] I have added tests - [ ] I have updated the documentation — no user-facing behaviour change -- 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]
