Skip to content

Failure Detector Expansion 1/5: Read failure type from updated valkey-io/valkey artifact and group nameless failures - #79

Open
BChan-0 wants to merge 1 commit into
valkey-io:mainfrom
BChan-0:failure-detector-expansion-foundation-final
Open

Failure Detector Expansion 1/5: Read failure type from updated valkey-io/valkey artifact and group nameless failures#79
BChan-0 wants to merge 1 commit into
valkey-io:mainfrom
BChan-0:failure-detector-expansion-foundation-final

Conversation

@BChan-0

@BChan-0 BChan-0 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

This is the first of five PRs expanding the Failure Detector past assertion failures. It carries the schema and dedup plumbing the other four build on; each of those adds one failure category and can merge in any order once this lands. The producer side is valkey PR 4292, which adds the type field to the artifact.

Issue

The detector only ever files issues for assertion failures. Valkey's Daily also produces timeouts, sanitizer and valgrind reports, server startup failures, memory-leak reports, uncaught test-client exceptions, and gtest unit-test failures, and none of them reach an issue.

'parse_and_deduplicate` drops any entry that does not name both a test and a file

A valgrind or sanitizer report has no test name because the tool doesn't run within tests. The same is true of a startup failure and a leak report. Those entries are read, discarded, and the run reports no failure for a job that was red.

PR Summary

FailureType declares all eight types up front (assertion, sanitizer, valgrind, timeout, startup, exception, memory-leak, unittest), so the later PRs add handling rather than enum values and producer/consumer drift stays visible. The type field defaults to assertion when absent, which keeps the current artifact format working, and an unrecognized value falls back to exception with a warning in the run log rather than being dropped.

A nameless failure now groups on its normalized error identity instead of being skipped. Normalization removes what changes between runs of one bug: PIDs, hex addresses, temp paths, timestamps, byte and block counts, loss records, and the runner's workspace layout, which differs per platform and was splitting one root cause into an issue per OS. A nameless timeout is the exception and groups on its test file, since every timeout carries the same generic error text and would otherwise collapse into one issue for the whole run.

Where an error has a stack, the identity anchors on it, so two leaks with identical report lines but different allocation sites stay distinct. Only the frame that names the bug keeps its line number: the frames below it are callers whose lines move whenever unrelated code is edited, and carrying the whole chain refiled one leak on most commits. Allocation wrappers in zmalloc.c and sds.c are skipped by name, since whether they appear at all depends on the compiler's inlining rather than on the bug. They are matched by name rather than by file because those files also hold ordinary code that can itself be the faulting frame, and skipping the whole file made a bad write in sdscatlen and one in sdsrange reduce to the same anchor.

Each of the eight types gets its own marker namespace, so two types cannot collide on one issue. All of them share the [TEST-FAILURE] title prefix and name the type in the body instead; a per-type prefix would rewrite an issue's title whenever the same bug was re-attributed, which happens when valgrind and a sanitizer report it on different days. A trace is embedded in the body, so text in it that looks like the body's own structure is now defused.

ArtifactClient.list_run_artifacts paginates and takes a server-side name filter.

Testing

Added:

  • tests/test_testfailuredetector_failure_parser.py: TestParseWithTypes, TestNormalizeErrorIdentity, TestVolatileTestNameDemotion, TestValgrindLeakIdentity, TestAccessWidthScrubbing, TestParenthesizedCountScrubbing, TestAllocationWrappersOnlyAreSkipped
  • tests/test_testfailuredetector_issue_manager.py: TestTypeSpecificFingerprint, TestTypeSpecificRendering, TestTitleFollowsFingerprint, TestTraceTruncation, TestTraceTextCannotForgeBodyStructure, TestReportedTraceRecordIsNotAFingerprintClaim, TestSymbolicatedMacosLeaksReport
  • tests/test_issue_dedup.py: TestUpdateAgainstRealIssueObjects, TestClaimGuardSpansSiblingNamespaces, TestClosedIssueSuppressesOnlyOneFingerprint
  • tests/test_workflow_artifacts.py: pagination, server-side name filter, page cap, cross-page dedup

1455 total tests passed.

@BChan-0 BChan-0 changed the title Read failure type from the updated valkey-io/valkey artifact and group nameless failures Failure Detector Expansion 1: Read failure type from the updated valkey-io/valkey artifact and group nameless failures Aug 6, 2026
@BChan-0 BChan-0 changed the title Failure Detector Expansion 1: Read failure type from the updated valkey-io/valkey artifact and group nameless failures Failure Detector Expansion 1/5: Read failure type from the updated valkey-io/valkey artifact and group nameless failures Aug 6, 2026
@BChan-0
BChan-0 force-pushed the failure-detector-expansion-foundation-final branch from f6105bc to fc21793 Compare August 7, 2026 17:42
@BChan-0 BChan-0 changed the title Failure Detector Expansion 1/5: Read failure type from the updated valkey-io/valkey artifact and group nameless failures Failure Detector Expansion: 1/5 — Read failure type from the updated valkey-io/valkey artifact and group nameless failures Aug 7, 2026
@BChan-0 BChan-0 changed the title Failure Detector Expansion: 1/5 — Read failure type from the updated valkey-io/valkey artifact and group nameless failures Failure Detector Expansion 1/5: Read failure type from updated valkey-io/valkey artifact and group nameless failures Aug 7, 2026
@BChan-0
BChan-0 marked this pull request as ready for review August 7, 2026 19:27
@BChan-0
BChan-0 force-pushed the failure-detector-expansion-foundation-final branch from fc21793 to 8e011ca Compare August 7, 2026 19:36
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The failure detector now classifies and normalizes multiple failure types, merges same-run failures, renders stable issue content, and tracks recurrence traces. Issue deduplication uses cached bodies and claim guards. Artifact listing now supports filtered pagination with validation and limits.

Changes

Failure detection and issue publishing

Layer / File(s) Summary
Typed failure parsing and identity normalization
scripts/test_failure_detector/parse_failures.py, tests/test_testfailuredetector_failure_parser.py
Failures use typed classifications, stable error identities, nameless entries, and type-aware deduplication.
Failure titles, bodies, and recurrence traces
scripts/test_failure_detector/issue_renderer.py, tests/test_testfailuredetector_issue_manager.py
Rendering provides stable titles, structured bodies, protected traces, bounded output, and digest-based recurrence updates.
Same-run merging and namespace-specific publishing
scripts/test_failure_detector/manage_issues.py
Failures with matching fingerprints merge job references and traces before publishing through namespace-specific publishers.

Issue deduplication safeguards

Layer / File(s) Summary
Cached issue bodies and claim-aware matching
scripts/common/issue_dedup.py
The publisher caches written bodies, supports creation comments, detects claims across namespaces, and limits closed-issue suppression to one fingerprint.
Deduplication regression coverage
tests/test_issue_dedup.py
Tests cover title adoption guards, repository-scoped suppression, cached issue updates, and repeated fingerprint updates.

Workflow artifact retrieval

Layer / File(s) Summary
Paginated artifact listing and ZIP validation
scripts/common/workflow_artifacts.py, tests/test_workflow_artifacts.py
Artifact retrieval supports name filtering, pagination, validation, duplicate removal, page limits, and ZIP size checks.

Sequence Diagram(s)

sequenceDiagram
  participant FailureReports
  participant parse_and_deduplicate
  participant process_failures
  participant issue_renderer
  participant IssueDedupPublisher
  FailureReports->>parse_and_deduplicate: classify and deduplicate failure entries
  parse_and_deduplicate->>process_failures: return UniqueFailure records
  process_failures->>issue_renderer: build fingerprints, titles, bodies, and traces
  issue_renderer->>IssueDedupPublisher: submit IssueContent by marker namespace
  IssueDedupPublisher->>FailureReports: create or update the reported issue
Loading

Possibly related PRs

Suggested reviewers: hanxizh9910, sarthakaggarwal97

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: reading failure types and grouping nameless failures.
Description check ✅ Passed The description directly explains the failure-type expansion, nameless failure grouping, artifact pagination, and related tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
scripts/test_failure_detector/issue_renderer.py (1)

574-578: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove _tool_labels_for since it has no callers.

_tool_labels_for is no longer used by the rendering paths, so remove the helper to avoid leaving code that describes a body shape the renderer no longer writes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test_failure_detector/issue_renderer.py` around lines 574 - 578,
Remove the unused _tool_labels_for helper and its associated implementation,
leaving the existing rendering paths and related symbols unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/common/issue_dedup.py`:
- Around line 189-202: Update the creation-comment handling around
issue.create_comment so it performs only one best-effort attempt instead of
passing the non-idempotent write through retry_github_call. Preserve the
existing exception handling and warning behavior for failures, including the
issue number and traceback.
- Line 247: Update the issue deduplication flow around the existing edit and
comment creation calls so _record_body(repo_name, existing.number, new_body)
runs immediately after existing.edit succeeds, before existing.create_comment;
remove the later recording call to avoid duplicate or stale-state handling.

---

Nitpick comments:
In `@scripts/test_failure_detector/issue_renderer.py`:
- Around line 574-578: Remove the unused _tool_labels_for helper and its
associated implementation, leaving the existing rendering paths and related
symbols unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2420fd94-05d7-46a1-b9a9-6adb2dcb7f05

📥 Commits

Reviewing files that changed from the base of the PR and between cab64ac and fc21793.

📒 Files selected for processing (9)
  • scripts/common/issue_dedup.py
  • scripts/common/workflow_artifacts.py
  • scripts/test_failure_detector/issue_renderer.py
  • scripts/test_failure_detector/manage_issues.py
  • scripts/test_failure_detector/parse_failures.py
  • tests/test_issue_dedup.py
  • tests/test_testfailuredetector_failure_parser.py
  • tests/test_testfailuredetector_issue_manager.py
  • tests/test_workflow_artifacts.py

Comment thread scripts/common/issue_dedup.py
Comment thread scripts/common/issue_dedup.py Outdated
Signed-off-by: Bonnie Chan <bonniecv@amazon.com>
@BChan-0
BChan-0 force-pushed the failure-detector-expansion-foundation-final branch from 8e011ca to c34d1e7 Compare August 7, 2026 20:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
scripts/test_failure_detector/issue_renderer.py (3)

684-684: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

_ROW_HEADING_RE does not match the Summary and Failing test(s) headings the body writes.

_build_body emits **Summary** and **Failing test(s)** at lines 616 and 620, without the colon inside the asterisks. The pattern requires **Summary:** and **Failing test(s):**, so those two alternatives never defuse a trace line that copies a real heading. Only the Environments alternative matches a heading the body actually writes, which is also the only heading a reader is anchored to today.

Either align the pattern with the emitted headings, or drop the two unused alternatives and state that only the Environments row needs defusing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test_failure_detector/issue_renderer.py` at line 684, Update
_ROW_HEADING_RE to match the exact heading format emitted by _build_body for
Summary and Failing test(s), placing the colon outside the closing markdown
emphasis, or remove those alternatives if only Environments requires defusing.
Preserve matching for the actual Environments heading.

198-224: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

A trace can be recorded as published while the comment drops its text.

Each fresh trace is truncated at the full _MAX_TRACE_CHARS budget and its digest is appended to published. The traces are then joined at line 224 and _build_comment truncates the joined string again at the same budget (line 838). If two fresh traces are large, the join loses the tail, but both digests are recorded, so reported suppresses the dropped trace on every later run.

Consider truncating each fresh trace to a share of the budget before joining, the same way _build_absorbed_trace_comment divides it, and digesting the form actually published.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test_failure_detector/issue_renderer.py` around lines 198 - 224,
Adjust the fresh-trace handling in the renderer loop so each candidate is
truncated to its share of the final comment budget before backtick bounding and
digesting, matching the per-trace allocation used by
_build_absorbed_trace_comment. Ensure fresh contains the same bounded forms that
the joined output publishes, so _published_digests only records trace text
retained in the final comment.

952-955: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove dead _extract_error_from_body.

_extract_error_from_body has no callers. Keep _extract_errors_from_body as the supported extractor, remove _extract_error_from_body, and update the stale references at lines 720 and 764.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test_failure_detector/issue_renderer.py` around lines 952 - 955,
Remove the unused _extract_error_from_body helper, retain
_extract_errors_from_body as the supported extractor, and update the stale
references near the issue-rendering logic to call the plural helper directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/test_failure_detector/issue_renderer.py`:
- Line 252: Update _COUNT_RE so the byte(s) and object(s) alternatives match
when followed by whitespace or end of input, removing the invalid word-boundary
requirement after the closing parenthesis. Preserve the existing matching
behavior for bytes, blocks, and their plural forms.

---

Nitpick comments:
In `@scripts/test_failure_detector/issue_renderer.py`:
- Line 684: Update _ROW_HEADING_RE to match the exact heading format emitted by
_build_body for Summary and Failing test(s), placing the colon outside the
closing markdown emphasis, or remove those alternatives if only Environments
requires defusing. Preserve matching for the actual Environments heading.
- Around line 198-224: Adjust the fresh-trace handling in the renderer loop so
each candidate is truncated to its share of the final comment budget before
backtick bounding and digesting, matching the per-trace allocation used by
_build_absorbed_trace_comment. Ensure fresh contains the same bounded forms that
the joined output publishes, so _published_digests only records trace text
retained in the final comment.
- Around line 952-955: Remove the unused _extract_error_from_body helper, retain
_extract_errors_from_body as the supported extractor, and update the stale
references near the issue-rendering logic to call the plural helper directly.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f4dc6db-d70b-49ee-bba1-66153f4eb101

📥 Commits

Reviewing files that changed from the base of the PR and between fc21793 and c34d1e7.

📒 Files selected for processing (3)
  • scripts/common/issue_dedup.py
  • scripts/test_failure_detector/issue_renderer.py
  • tests/test_issue_dedup.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_issue_dedup.py

# Allocation sizes drift run to run for the same leak ("49 bytes" vs
# "52 bytes"), so titles show them as N: "N bytes in N blocks are
# definitely lost".
_COUNT_RE = re.compile(r"\b\d[\d,]*(\s+(?:bytes?|blocks?|byte\(s\)|object\(s\)))\b")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

_COUNT_RE never matches the byte(s) and object(s) forms.

The trailing \b follows a literal ). A word boundary after ) requires a word character next, but these phrases are followed by a space or end of line. So "41 byte(s)" and "3 object(s)" keep their counts, and a title built from such a line changes whenever the magnitude changes.

The bytes? and blocks? alternatives are unaffected because they end in a word character.

🐛 Proposed fix
-_COUNT_RE = re.compile(r"\b\d[\d,]*(\s+(?:bytes?|blocks?|byte\(s\)|object\(s\)))\b")
+_COUNT_RE = re.compile(r"\b\d[\d,]*(\s+(?:bytes?\b|blocks?\b|byte\(s\)|object\(s\)))")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_COUNT_RE = re.compile(r"\b\d[\d,]*(\s+(?:bytes?|blocks?|byte\(s\)|object\(s\)))\b")
_COUNT_RE = re.compile(r"\b\d[\d,]*(\s+(?:bytes?\b|blocks?\b|byte\(s\)|object\(s\)))")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test_failure_detector/issue_renderer.py` at line 252, Update
_COUNT_RE so the byte(s) and object(s) alternatives match when followed by
whitespace or end of input, removing the invalid word-boundary requirement after
the closing parenthesis. Preserve the existing matching behavior for bytes,
blocks, and their plural forms.

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.

1 participant