Repository navigation
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #4089 +/- ##
=======================================
Coverage 87.83% 87.83%
=======================================
Files 270 270
Lines 16058 16058
Branches 2626 2626
=======================================
Hits 14105 14105
Misses 1284 1284
Partials 669 669 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Base automatically changed from
telemetry-fingerprint-function-names
to
master
October 27, 2026 21:25
…ded copy When a simulation worker fails, the simulation service logs the original exception, then `record_failure_cause` records a redacted copy: a handled `exception.recorded` event of class `Stage12FailureCause` with the same stack, often with the same timestamp to the microsecond. `primary_records` ranks both as exceptions, so on a tied timestamp it picks whichever error type sorts first, and one cause splits across two error groups. These tests describe ranking the copy after the original exception and before messages and failed requests: - a failure with the original, the copy and the worker's later RuntimeError is represented by the original, in all six input orders; - a failure with only the copy, a message and a failed request is represented by the copy, in all six orders (this passes already); - four worker failures, two with copies at the same timestamp and two with copies logged later, form one ValueError group of four. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a simulation worker fails, the simulation service logs the original exception, then `failure_causes.record_failure_cause` records a redacted copy through `record_exception(handled=True)`: an `exception.recorded` event of class `Stage12FailureCause` with the original's stack, often with the same timestamp to the microsecond. `_primary_preference` ranked both as exceptions, so when the timestamps tied, the record whose fingerprint inputs sorted first represented the failure, and one cause split across two groups and two issues. The copy sets no event name or attribute of its own: the event name and handled flag are what the runtime writes for any handled exception, and the only attribute is the `observability_id` it adds to every record. So `RECORDED_CAUSE_COPY_TYPES` lists the copy's class, and `_primary_preference` ranks such a record after other exceptions and before messages and failed requests. A failure with only the copy still uses it. This changes which record represents those failures but not how a record is fingerprinted, so it belongs with fingerprint algorithm 2 and needs no further bump as long as the algorithm 2 change merges first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comment, README and changelog said the copy has the same stack as the original exception. The logs from 2026-10-20 to 2026-10-28 show that for the UK worker (all 28 copies had the original's frames, 10 with the same timestamp to the microsecond), but a coordinator's or US worker's copy is recorded seconds later from another point in the call stack, so its frames differ. They now say the copy carries the original's traceback, and that a worker's copy often has the original's frames and timestamp. No code changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
force-pushed
the
telemetry-rank-cause-copies
branch
from
October 27, 2026 21:25
ec9221d to
a24113d
Compare
This branch was previously 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.
Fixes #4088
Stacked on #4086 (fingerprint algorithm 2), so the base is
telemetry-fingerprint-function-names.What was wrong
When a simulation worker or coordinator fails on the simulation service's newer report path, the service logs the original exception, then
failure_causes.record_failure_causerecords a redacted copy that carries the original's traceback._primary_preferenceranked both as exceptions, so when they had the same timestamp, the record whose fingerprint inputs sorted first represented the failure: the copy, for aValueError. One cause then split across two groups and two issues, asa8e6ee4984a289c0and88e59fcf916bc9ffdid on 2026-10-26.What tells the copy apart
Only its exception class,
Stage12FailureCause.policyengine-simulation-executor,stage12_runtime/failure_causes.pyonmain):record_failure_causewraps the redacted summary inStage12FailureCause, attaches the original's traceback, and callsruntime.record_exception(cause, handled=True). Inpolicyengine-observability,record_exceptionwritesevent.nameexception.recordedanderror.handledtruefor every handled exception, anderror.typeistype(error).__name__. The copy passes no attributes; its only attribute is theobservability_idthe runtime adds to every record. The original comes fromfailure_recording.report_original_failure, which logs a record with the error, no event name and astage12_failure_scopeattribute._AllLogs, 2026-10-20 to 2026-10-28): everyStage12FailureCauserecord (28 frompolicyengine-stage12-uk-worker, 23 frompolicyengine-stage12-coordinator, 4 frompolicyengine-stage12-us-worker) is anexception.recordedevent witherror.handledtrueand nostage12_failure_scope, and each service logged the same number of originals withstage12_failure_scopeand no event name. Each of the 28 UK worker copies has the same frames as an original of the same request logged no later, and 10 have its timestamp to the microsecond; those ties are what split the groups. A coordinator's or US worker's copy is recorded seconds after its original, from another point in the call stack, so its frames differ and the original was already chosen as the earlier record. After a worker's copy, its operation logs anoperation.completedrecord with aRuntimeErrorabout a second later.exception.recordedwitherror.handledtruedoesn't identify a copy: the simulation gateway logs its handledValueErrorthe same way. The copy's writer sets no event name or attribute of its own, so the class name is the only field it sets on purpose.What changed
records.py:RECORDED_CAUSE_COPY_TYPESholdsStage12FailureCause, and_primary_preferenceranks a record of that class after other exceptions and before messages and failed requests. A failure with only the copy still uses it. The tie-breaker and the fingerprint are unchanged.gcp/observability/README.mddescribes the ranking, andchangelog.d/telemetry-rank-cause-copies.fixed.mdthe fix.Fingerprints
How a record is fingerprinted doesn't change, only which record represents these failures. Failures that were counted in a
Stage12FailureCausegroup move to their original exception's group, so aStage12FailureCausegroup's issue would stop being updated. This belongs with fingerprint algorithm 2 and needs no further algorithm change, as long as #4086 merges first and this follows before the report opens issues forStage12FailureCausegroups under algorithm 2. No issue exists for one today.Grouping the 1,394 ERROR records that the report's query returns for 2026-10-26, with a throwaway fingerprint key:
mastergivespolicyengine-stage12-uk-workeraValueErrorgroup of 10 production failures and aStage12FailureCausegroup of 4; this branch gives oneValueErrorgroup of 14. Every other group is the same.Testing
The first commit adds the tests and they fail; the second commit is the fix. The third commit corrects the comment, README and changelog, which had said the copy has the same stack as the original; it changes no code.
test_records.py: a worker failure logged as the originalValueError, its copy with the same timestamp, and the worker's laterRuntimeErroris represented by the original in all six input orders. Before the fix the copy was chosen in all six. A failure with only the copy, a message and a failed request is represented by the copy in all six orders; this passes before and after.test_groups.py: four worker failures, two with copies at the same timestamp and two with copies 100 ms later, form oneValueErrorgroup of four. Before the fix they formed aValueErrorgroup of two and aStage12FailureCausegroup of two.report_support.pygainsoriginal_worker_failureandrecorded_cause_copy, which build the two records.pytest tests/unit/telemetry_error_issues: 262 passed on Fingerprint telemetry exceptions by function names, not module paths #4086; 7 failed and 268 passed after the first commit; 275 passed after the second.uv lock --checkpass. No inline suppressions were added (94noqa, 9 type or pyright ignores, as onmaster).🤖 Generated with Claude Code