Skip to content

Reduce memory utilization in Envoy egress gateway by deduplicating policy object - #2651

Merged
Lior Lieberman (LiorLieberman) merged 26 commits into
agent-substrate:mainfrom
yanavlasov:mem-opt-1
Oct 15, 2026
Merged

Lior Lieberman (LiorLieberman) merged 26 commits into
agent-substrate:mainfrom
yanavlasov:mem-opt-1

Conversation

@yanavlasov

Copy link
Copy Markdown
Collaborator

This PR reduces memory usage by SNI based egress policies, surface by scalability tests with 1M actors and policies and 1K SNI rules per policy.

The cause of memory consumption was two fold:

  1. policy proto sent from ext_proc to Envoy was kept in memory, even though it was no longer used after it was converted into internal policy representation.
  2. Each CONNECT request kept its own copy of the egress policy, duplicating policy in memory by factor of Actors*Connections_per_actor.

This PR makes two high level changes:

  1. policy proto sent from the ext_proc is erased after it is parsed into the Envoy internal policy object.
  2. policy object is hashed using high quality Rapidhash and stored in thread local store using computed hash key. The policy hash is then communicated to the inner filter chain that fetches policy from the store and enforces SNI rules.

With this change, if all actors have exactly the same policy, Envoy will only have 1 copy of the policy object per worker thread.

  • Tests pass
  • Appropriate changes to documentation are included in the PR

Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
Signed-off-by: Yan Avlasov <[email protected]>
@yanavlasov

Copy link
Copy Markdown
Collaborator Author

Benchmark results of single core dataplane with 1000QPS of H/1 requests did not change from baseline. As expected.

@LiorLieberman

Copy link
Copy Markdown
Collaborator

is this ready for review?

@yufan-su

Copy link
Copy Markdown
Collaborator

I ran a small egress test with this change and see some big improvements on memory per tunnel.

Setup: 100 actors × 1,000 endpoints = 100,000 HTTPS tunnels. Every actor has the same 1,000-host policy. One gateway replica (16 Envoy threads).

Metric Before PR With PR 2651 Change
Envoy memory, 100k HTTPS tunnels 51.5 GiB 6.9 GiB −87%
Memory per HTTPS tunnel 520 KiB 56 KiB −89% (9.3× less)
Memory per HTTP tunnel (control) 49 KiB 50 KiB unchanged

@yanavlasov

Copy link
Copy Markdown
Collaborator Author

is this ready for review?

Yes it is. Test failed due to unrelated flake. I will try to re-start it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice improvement in memory!! thanks

};
envoy_log_trace!("egress policy store: policy={:?}", policy);
let mut local_store = GlobalEgressPolicyStore::get_local_store();
if let Some(existing_policy) = local_store.lookup(policy.hash) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a chance of collision here? I read that its small, but how expensive is it for us to compare the full policy on a store hit and fail closed if it doesn't match?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. Could be a lot of string comparisons for policies with a lot of SNIs. I have added a sanity check for now. We could see if it shows up in CPU profiles for load tests.


## What it decides
To minimize memory usage egress policies are hashed using [Rapidhash](https://docs.rs/rapidhash/latest/rapidhash/)
which produces collision resistant 64 bit hashes. Policies are then stored in the worker local

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this is not really collision resistant since its non cryto.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded

Comment on lines 62 to 64
@@ -63,6 +63,12 @@ const (
// the egress-policy module: {"rules": [{"pattern": ..., "mode": ...}]},
// most specific first.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think with your change, now only the hash is copied, right? added a suggestion but need to update

the same for the mitm_internal comments (1 and 2) in atenet-egress.yaml and router README.md

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggesting this for line 61-68

	// EgressPolicyMetadataNamespace holds the SNI rules returned on CONNECT, as
	// JSON under EgressPolicyJSONKey: {"rules": [{"pattern": ..., "mode": ...}]},
	// most specific first.
	EgressPolicyMetadataNamespace = "dev.ate.policy.egress"
	// EgressPolicyHashFilterStateKey holds the hash the inner listener uses to
	// find those rules in the egress-policy module's per-thread store.
	EgressPolicyHashFilterStateKey = "dev.ate.policy.egress.hash"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed comments and fixed the doc.

Signed-off-by: Yan Avlasov <[email protected]>
Merged via the queue into agent-substrate:main with commit 1738112 Oct 15, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/network kind/bug Something isn't working / bugfixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants