Skip to content

atenet: preserve cold resume outcome and fallback empty template dimension - #1482

Open
Jeff Luo (JeffLuoo) wants to merge 1 commit into
agent-substrate:mainfrom
JeffLuoo:fix/issue-1474-router-route-duration-labels
Open

atenet: preserve cold resume outcome and fallback empty template dimension#1482
Jeff Luo (JeffLuoo) wants to merge 1 commit into
agent-substrate:mainfrom
JeffLuoo:fix/issue-1474-router-route-duration-labels

Conversation

@JeffLuoo

@JeffLuoo Jeff Luo (JeffLuoo) commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1474

When atenet-router routes requests:

  • Failed cold resumes were mislabeled as ate.router.resume="none" due to an unconditional overwrite in ResumeActor. Preserves 'triggered' (for singleflight leader) and 'joined' (for joiners) on resume error, strictly reserving 'none' for warm hits on already-running actors.
  • Route duration metrics emitted empty strings for ate.template.atespace and ate.template.name when routing/resuming failed, violating the required metric registry contract. Normalizes empty template namespace and name to "unknown", avoiding request-derived/caller-controlled metric labels on failure paths.
  • Updated metric registry documentation for ate.template.atespace and ate.template.name to include "unknown" fallback.

It's a good idea to open an issue first for discussion.

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

Comment thread internal/ateattr/ateattr.go
Comment thread cmd/atenet/internal/router/ingress/resumer.go Outdated
Comment thread cmd/atenet/internal/router/ingress/resumer.go Outdated
Comment thread docs/metrics/registry/metrics.yaml Outdated
Comment thread cmd/atenet/internal/router/ingress/resumer_test.go Outdated
…nsions (agent-substrate#1474)

Fixes agent-substrate#1474

When atenet-router routes requests:
- Mislabeled cold resume failures: Previously, failed resumes were unconditionally
  overwritten with ate.router.resume="none", distorting cold activation rates and
  durations. In-flight cold activation attempts (and joiners) now preserve
  "triggered" and "joined" respectively on capacity and transient errors.
  Definitive non-activation errors (e.g. NotFound, InvalidArgument), context
  cancellations, and non-resuming directions (egress) now report "unattempted",
  strictly reserving "none" for warm hits on running actors.
- Missing template labels: Empty template namespace and name dimensions are normalized
  to "unknown" using ateattr.NormalizeTemplateDimension, satisfying metric registry
  invariants without using request-derived labels on failure paths.
- Documentation: Updated registry metrics.yaml and substrate.yaml to reflect
  the "unattempted" resume value, mirror handler Result documentation for "unknown"
  template fallback (including egress), and account for platform-injected unknown dimensions.

// NormalizeTemplateDimension ensures a template dimension (atespace or name) is
// non-empty, falling back to TemplateUnknown if unset.
func NormalizeTemplateDimension(dim string) string {

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, but it's still only called from the router though, and the group brief now promises unknown for all 7 metrics that ref these attrs.

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.

Thanks for catching it. This change would touch 7 metrics. I would prefer to do it in a follow-up PR for a cleaner chagne.

Comment on lines +164 to +174
// isDefinitiveResumeError reports whether err represents a failure where no cold
// activation could be attempted (e.g. the actor does not exist, bad request, or
// permission denied), as opposed to in-flight capacity or transient failures.
func isDefinitiveResumeError(err error) bool {
switch status.Code(err) {
case codes.NotFound, codes.InvalidArgument, codes.PermissionDenied, codes.Unauthenticated:
return true
default:
return false
}
}

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.

Can we flip the default? Right now Internal, Unimplemented, Unknown etc. fall through to triggered, so an unrecognized code is as an activation in the latency series.
Unknown means we don't know, so unattempted seems safer.

Also wondering about FailedPrecondition, its registry brief is "the state of the actor did not permit a route" and with parking off it fails immediately, which sounds definitive. And retryable's comment just above already groups DeadlineExceeded with NotFound and PermissionDenied, so it's a bit weird to have two classifiers for the same codes I think.

Wdyt?

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.

Agreed on it. But I want to discuss more on the flipping bit.

If I flip it, I still need a list of codes that mean "an activation was in flight". This is not quite maintainable by looking at the code now.

My proposed change will be:

  1. Delete isDefinitiveResumeError. This removes the classifier.
  2. Make err == nil a condition for triggered and joined. A resume that fails reports the fourth value (added in this PR). FailedPrecondition and ResourceExhausted both go there. It also corrects the words "initiated cold activation" that you found.
  3. Change the name unattempted to unknown. The value includes requests where an activation did operate. One example is a canceled leader. Its flight is separate from the request context, and it continues to restore the actor. A second example is DeadlineExceeded during a restore. Thus unattempted is not true for these requests, and new words in the brief cannot correct this.

WDYT?

Comment on lines 211 to 212
// RouterResumeTriggered indicates this request won the singleflight lock and initiated cold activation.
RouterResumeTriggered = "triggered"

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.

initiated cold activation

a request that is ResourceExhausted gets triggered without activating anything. Same wording in the registry brief.

Comment on lines +434 to +436
No cold activation was in flight. The request was invalid, the
actor did not exist, the direction does not resume, or the
request canceled before activation was attempted.

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.

"No cold activation was in flight" and "request canceled" contradict each other, a canceled joiner was waiting on someone else's activation. Maybe "this request neither attempted nor observed an activation" is more accurate?

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.

[Bug]: atenet.router.route.duration mislabels failed cold resumes as "none" and emits empty template attributes on routing failures

2 participants