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

// Note: "unknown" is syntactically a legal atespace/template name; like
// ate.sandbox.class="unknown", this trades potential collision with a real
// object named "unknown" for maintaining a bounded, non-empty metric dimension.
const TemplateUnknown = "unknown"

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 have a Normalize for this, like for the rest of unknowns?

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.

Added normalization in ateattr, following the pattern of NormalizeSandboxClass and NormalizeOperationName, and switched recordRouteDuration to use it. Added unit tests for it in ateattr_test.go as well.

// - ResumeOutcomeJoined ("joined"): Cold activation joiner (resumed == true or attempt errored, caller's reqID != leaderID).
outcome := ResumeOutcomeNone
if callRes.resumed {
if callRes.resumed || callRes.err != nil {

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.

A NotFound never triggered anything, but it now gets resume="triggered". Can we add a new value like unattempted for definitive errors, and keep triggered/joined for the capacity ones where a resume really was in flight?

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.

Great catch. Added RouterResumeUnattempted = "unattempted" to ateattr and registered it in metrics.yaml.

// - ResumeOutcomeNone ("none"): resumed == false, actor was already active/running.
// - ResumeOutcomeTriggered ("triggered"): Cold activation leader (resumed == true, caller's reqID == leaderID).
// - ResumeOutcomeJoined ("joined"): Cold activation joiner (resumed == true, caller's reqID != leaderID).
// - ResumeOutcomeNone ("none"): resumed == false and err == nil, actor was already active/running.

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.

Here, + the PR says it reserves none for warm hits, but we have multiple places it still returns none on a cancel.

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.

Updated. Set none only when actual warm hits where the actor was already running.

Comment thread docs/metrics/registry/metrics.yaml Outdated
Comment on lines +79 to +80
The atespace of the ActorTemplate of the actor, or "unknown" if the
request failed before template resolution.

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.

Two things:

  • "if the request failed" misses egress. Egress succeeds and still has no template, so it gets unknown too. Result's comment in extproc/handler.go phrases it right, can we mirror that?
  • 7 metrics share this brief but only the router sends unknown. And line 72 plus bounded-or-catalog-scoped in docs/metrics/substrate.yaml both say these labels only ever name something an operator made. unknown doesn't, so it needs writing down there.

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.

Updated both.

}
if outcome != ResumeOutcomeNone {
t.Errorf("expected outcome %q on error, got %q", ResumeOutcomeNone, outcome)
if outcome != ResumeOutcomeTriggered {

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.

Does this expect a nonexistent actor trigering a cold activation basically?

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.

Fixed. With unattempted introduced, ActorNotFound now expects ResumeOutcomeUnattempted (since no cold activation was ever attempted). Also added a test case for context cancellation verifying it returns unattempted.

…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.
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