fix(xds): carry Lua scripts on the routes with a shared VM per script - #10056
Open
zhaohuabing wants to merge 12 commits into
Open
zhaohuabing wants to merge 12 commits into
zhaohuabing wants to merge 12 commits into
Conversation
✅ Deploy Preview for cerulean-figolla-1f9435 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #10056 +/- ##
==========================================
+ Coverage 81.41% 81.43% +0.01%
==========================================
Files 266 266
Lines 41300 41302 +2
==========================================
+ Hits 33626 33635 +9
+ Misses 7673 7666 -7
Partials 1 1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
4 times, most recently
from
September 21, 2026 03:15
f277143 to
fb0cf88
Compare
This comment was marked as resolved.
This comment was marked as resolved.
Member
Author
|
@codex review |
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
4 times, most recently
from
September 21, 2026 05:13
44e2595 to
01c6d30
Compare
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
2 times, most recently
from
September 21, 2026 06:11
316c22e to
19fbfac
Compare
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
4 times, most recently
from
September 21, 2026 09:14
2433302 to
3df7503
Compare
zhaohuabing
marked this pull request as ready for review
September 22, 2026 02:48
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
from
September 22, 2026 02:50
0d19f38 to
2fd3fbe
Compare
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
4 times, most recently
from
September 28, 2026 10:10
1650031 to
a9ee2f4
Compare
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
2 times, most recently
from
September 28, 2026 10:49
13f5731 to
9430c68
Compare
zhaohuabing
marked this pull request as ready for review
September 28, 2026 10:50
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
2 times, most recently
from
September 28, 2026 12:20
5c710be to
c720783
Compare
A listener now carries one Lua filter per execution slot, holding every script that can run in that slot in its sourceCodes map, and routes select one with LuaPerRoute.name instead of carrying the source. Envoy builds one Lua VM set per distinct script rather than one per route config that embeds source text, and the filter chain no longer grows with the number of Lua policies. Each filter's configuration is served as its own ECDS resource, so adding, editing or removing a script is an extension config update that Envoy applies in place instead of a listener update that drains the listener. Slot counts are rounded up to a multiple of ten to keep the filter list still as policies come and go; an unused slot holds an empty config and builds no VM. Fixes: envoyproxy#9355 Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
Non-TLS Gateway listeners sharing an address and port share one xDS listener, one filter chain and one HTTP connection manager. Naming the Lua slots after the IR listener meant each of them appended a bucket of its own, so three listeners on a port produced thirty Lua filters in a single manager, and thirty ECDS resources, twenty seven of them empty. Scope the slots to the RouteConfiguration the manager serves instead. That is what the listeners sharing a manager have in common, and what routes key their per-filter config against, so they now share one set of slots and one set of Lua VMs. A manager patched by a second listener merges its scripts into the slots already there rather than adding more. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
The extraction pass decided what it owned by parsing the filter name, which a filter added by an EnvoyPatchPolicy or an extension server can match: anything called envoy.filters.http.lua/<something>/<digits> was moved to ECDS, taking its configuration out from under its author. Two such filters in different listeners would also have claimed one ECDS resource name. Record the eligible filters as Envoy Gateway builds each connection manager instead, which happens before the patches and the extension hook run, and have the pass lift only those. Names are no longer parsed. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
Filter types that can appear several times on a listener got their order by adding the index within the type to the type's base order. Lua starts at 13 and filters the ordering does not recognize sit at 50, so a listener with 38 Lua filters sorted its last ones after those, and at 88 they reached ext_proc at 100. The same applies to ext_proc, wasm and dynamic modules. Keep the index as a separate sort key instead, compared after the order and before the name, so a type's filters can never reach the next type's position. With the index out of the order, the gaps at 100, 200, 250 and 300 are no longer headroom for anything, so collapse them and put every type one apart. The order for an unrecognized type and the one for the router are the two that carry meaning, so give them names; an unrecognized filter now runs after everything placed deliberately rather than between Lua and ext_proc, still before the terminal router. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
The new ecds command declared "e" as its alias, which endpoint already had. Cobra sorts subcommands by name before matching, so ecds won and egctl config envoy-proxy e silently changed from endpoints to ECDS. Give ecds "ec" instead and fix its example, which advertised the alias it had taken. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
The ECDS filters were set to apply their default config without warming, so a listener started serving as soon as it was created. Envoy subscribes to ECDS only after it has processed the listener, which left a window on every listener update where the slots were present but empty and the scripts did not run. A script that checks a header or rejects a request was simply absent for that window, and nothing in the config dump showed it. Warm instead, and drop the default config with it. A delivery that fails or is rejected now leaves the slot without a config and Envoy answers with a 500, rather than running the route as if the script had never been configured. Envoy skips a slot disabled by default before it looks for a config, so this reaches only a request whose route enabled the slot: a route with no Lua on it, and the slots no route uses, are unaffected. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
The fixture looks like a near copy of lua.yaml, so record that it covers two listeners sharing one HCM and the slot merging that follows from it. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
Every golden carried an empty EcdsConfigDump, so the loop that fills it from the extension configs had never run under test. A typo in the type URL or a validation failure there would have gone unnoticed, and the symptom is egctl quietly omitting the Lua scripts somebody is trying to debug. Attach a Lua EnvoyExtensionPolicy to the route of the xds fixture, which gives the dump a TypedExtensionConfig with a populated sourceCodes map, and covers the listener and route sides of the same change while it is there. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
…missing script A route names a Lua script by a key inside an ECDS resource, and the two are delivered as separate xDS responses in an order the control plane does not choose. A key added in the same push as the route naming it can arrive after the route, and a key removed in the same push can leave before it. In that window Envoy finds no script under the name and skips the filter silently, which for a denying script is a bypass. Sequence the publish instead of relying on the order. When keys are added, first publish the previous snapshot with the ECDS resources holding both old and new keys, so the scripts exist before anything names them. When keys are removed, publish the new snapshot with the old keys still present and prune them in a further snapshot, so nothing names a script after it is gone. A push that changes no key, which is every script edit, stays a single snapshot as before. The snapshot cache already keeps the last snapshot per IR key, so the previous resources come from there. Snapshots published under one trace share a version, which the cache takes as nothing to send, so a repeated version now gets a suffix. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
from
September 29, 2026 02:07
c720783 to
0fcd8bf
Compare
Picks up the mirror of envoyproxy/envoy@c5f7278f, which adds shared_vm_id to the Lua filter and its per-route config. A route can then carry its script inline while Envoy shares one VM across every route with the same script and id, which is what lets Lua policies move out of the listener without the per-route VM duplication from envoyproxy#9355. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
2 times, most recently
from
September 29, 2026 03:10
7a2d92a to
cbebc5b
Compare
Member
Author
|
@codex review |
Serving the scripts over ECDS left a route able to name a script its slot did not hold yet, or no longer held, because the route and the script travel as separate xDS responses in an order the control plane cannot guarantee without tracking each proxy's acknowledgements. Envoy skips the script silently in that window. Put the script back on the route instead, as LuaPerRoute source with a shared_vm_id equal to the script's name. Envoy then builds one set of Lua VMs per script and shares it across every route carrying that script, and across the TCP and QUIC managers of a listener, so the per-route VM duplication from envoyproxy#9355 cannot return. The listener keeps one empty, disabled placeholder filter per slot, so a policy change never rewrites it. Nothing crosses resources any more, so the ECDS extraction, the ordered publish, the snapshot version suffix, the egctl ECDS dump and the per-route route config plumbing all go. Filter names shrink to their slot, since they only have to be unique within a manager. This needs an Envoy build that includes envoyproxy/envoy#47562. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
zhaohuabing
force-pushed
the
lua-shared-source-codes
branch
from
September 29, 2026 03:31
cbebc5b to
5a5fe7e
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lua in an
EnvoyExtensionPolicyhas been stuck between two bad options: keeping the script in the listener means every edit drains the listener, and moving it onto the routes meant one Lua VM set per route config, which is what took a proxy from 88 MiB to 2.8 GiB in #9355 and led to the revert in #9426.envoyproxy/envoy#47562 removes the second problem. With
shared_vm_idset onLuaPerRoute, Envoy builds one set of Lua VMs per script and shares it across every route carrying that script. The listener keeps one empty, disabled placeholder Lua filter per slot, rounded up to a multiple of ten, so adding, editing or removing a policy is a route configuration update and never rewrites the listener.The shared VM id is the script's own name, so scripts from different policies never share a VM or its globals; only routes running the same script do.
Compared to the earlier ECDS design, no ordering between RDS and ECDS to get right. The cost is that a policy spanning many routes puts its script text in each route entry, so the
RouteConfigurationis larger for that case.Compatibilitie:
Filter names change from policy-derived to
envoy.filters.http.lua/<slot>,EnvoyPatchPolicysand extension servers need to be adjusted accordingly.A script's VM is now shared across every listener that runs it, where previously each listener had its own. Global variables that a script uses as shared state, such as a cache or a counter, were already shared by all routes of the policy on one listener and are now also shared across listeners. This should rarely matter, but scripts that rely on per-listener globals should be aware of it and use a separate policy per listener if needed, since each policy script gets its own VM.
Fixes: #9355