Skip to content

Egress gateway reports actor JWT cache hits and mint latency - #2317

Closed
Max Thompson (thompsonmax) wants to merge 1 commit into
agent-substrate:mainfrom
thompsonmax:actor-jwt-metrics
Closed

Max Thompson (thompsonmax) wants to merge 1 commit into
agent-substrate:mainfrom
thompsonmax:actor-jwt-metrics

Conversation

@thompsonmax

@thompsonmax Max Thompson (thompsonmax) commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #1660.

  • ate.egress.actor_jwt.lookups counts each token lookup in the egress gateway's actor JWT cache by outcome (hit, miss, error, cancelled, timeout), so the hit ratio shows whether the cache is saving mints.
  • ate.egress.actor_jwt.mint.duration times each MintActorJwt call to ateapi, so a slow egress request can be traced to a slow mint. Its count is the mint rate.
  • Failures carry the gRPC code in error.type. No label names an actor or an audience.
  • The router builds the instruments and passes them through egress.New, as it does for the ingress parking metrics. The registry, ateattr, and the observability table gain the two instruments and the outcome attribute.

egress.New gains a parameter; the tests that call it pass nil.

Testing: ManualReader unit tests cover a miss then a hit, a failed mint, and callers that cancel or time out. hack/verify/metrics.sh passes with Weaver v0.25.1. Not run on a cluster, and no dashboard panel yet.

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

The egress gateway's actor JWT minter now records two instruments:
ate.egress.actor_jwt.lookups counts each token lookup by outcome (hit,
miss, error, cancelled, timeout), so an operator can see whether the
token cache is helping, and ate.egress.actor_jwt.mint.duration times
each MintActorJWT call, so a slow egress request can be traced to a
slow ateapi. Failures carry the gRPC code in error.type.

The router builds the instruments and passes them through egress.New,
as it does for the ingress parking metrics. Both instruments and the new
outcome attribute are in the metric registry.

@JeffLuoo Jeff Luo (JeffLuoo) left a comment

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.

Do we need either of these instruments?

  1. ate.egress.actor_jwt.mint.duration duplicates rpc.client.call.duration. Minter.mint times only m.client.MintActorJwt(ctx, req). The router builds s.apiClient with otelgrpc.NewClientHandler() in router.go, so each mint already records rpc.client.call.duration with rpc.method="ateapi.Control/MintActorJwt" and rpc.response.status_code. ateapi records rpc.server.call.duration for the same call. Both histograms measure the same RPC: the mint runs under context.WithoutCancel, and requests that join a singleflight make no call. The other egress RPCs (GetActor, GetEgressPolicy, FetchSecret) have no second histogram for each method.

  2. ate.egress.actor_jwt.lookups measures a cache that has no size limit. Minter uses cache.Expiring and reuses each token until one third of expiration_seconds remains. Thus a miss occurs only on the first request or after a token expires, and the miss count is almost equal to the MintActorJwt call count. The hit ratio shows only how many egress requests an actor sends during one token lifetime, and no setting in the gateway changes it. This is not like ate.imagecache.requests: there, a miss pulls OCI layers and disk GC removes entries. policyCache in the same package has no lookup counter.

Can we use atenet.router.route.duration to find slow egress callouts, and rpc.client.call.duration by rpc.method to find the slow call? If you want to keep one instrument, please drop ate.egress.actor_jwt.mint.duration at a minimum. If lookups goes, the ate.egress.actor_jwt.outcome attribute and its ateattr constants go too.

@thompsonmax

Copy link
Copy Markdown
Collaborator Author

Do we need either of these instruments?

  1. ate.egress.actor_jwt.mint.duration duplicates rpc.client.call.duration. Minter.mint times only m.client.MintActorJwt(ctx, req). The router builds s.apiClient with otelgrpc.NewClientHandler() in router.go, so each mint already records rpc.client.call.duration with rpc.method="ateapi.Control/MintActorJwt" and rpc.response.status_code. ateapi records rpc.server.call.duration for the same call. Both histograms measure the same RPC: the mint runs under context.WithoutCancel, and requests that join a singleflight make no call. The other egress RPCs (GetActor, GetEgressPolicy, FetchSecret) have no second histogram for each method.
  2. ate.egress.actor_jwt.lookups measures a cache that has no size limit. Minter uses cache.Expiring and reuses each token until one third of expiration_seconds remains. Thus a miss occurs only on the first request or after a token expires, and the miss count is almost equal to the MintActorJwt call count. The hit ratio shows only how many egress requests an actor sends during one token lifetime, and no setting in the gateway changes it. This is not like ate.imagecache.requests: there, a miss pulls OCI layers and disk GC removes entries. policyCache in the same package has no lookup counter.

Can we use atenet.router.route.duration to find slow egress callouts, and rpc.client.call.duration by rpc.method to find the slow call? If you want to keep one instrument, please drop ate.egress.actor_jwt.mint.duration at a minimum. If lookups goes, the ate.egress.actor_jwt.outcome attribute and its ateattr constants go too.

I see, #1 makes sense to me. I think for #2, the lookup counter isn't too helpful on its own (and currently we're missing it on the policyCache).

It may make sense to later instrument all the caches in egress gateway with hit/miss metrics, but I don't think we have a strong operational use case for this today. I think I'll close this one for now to free up some review bandwidth prior to GA. Thanks!

Jay (jay23606) pushed a commit to jay23606/substrate that referenced this pull request Oct 19, 2026
)

Part of agent-substrate#1660.

- `docs/egress-credential-injection.md` gains an Actor JWTs section: the
`actorJwt` policy fields and their limits, the claims a minted token
carries, and how long the gateway reuses a token before minting another.
- The outcome table gains rows for actor JWT entries: 403 when the actor
no longer exists, 503 when ateapi is unreachable, and 500 (Envoy) or 403
(agentgateway) for any other mint failure. The intro and "How it works"
mention the ateapi path.
- An operational note says tokens follow the actor's name, so a
recreated actor can get its predecessor's token until the refresh point.

The metrics from agent-substrate#2317 aren't mentioned, since they aren't on main yet.

Testing: docs only. The YAML example parses with `kubectl ate`'s policy
parser, and each documented limit matches ateapi's validation, both
checked with throwaway tests.

- [ ] Tests pass
- [x] Appropriate changes to documentation are included in the PR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants