Skip to content

Retry, dependency gating, and richer AsyncResult tracking for Queueable#38

Open
Mateusz7410 wants to merge 2 commits into
mainfrom
feature/async-0002
Open

Retry, dependency gating, and richer AsyncResult tracking for Queueable#38
Mateusz7410 wants to merge 2 commits into
mainfrom
feature/async-0002

Conversation

@Mateusz7410
Copy link
Copy Markdown
Collaborator

Summary

  • Retry & backoffretry(n) (capped at 10, throws above), backoff(...) strategies via a new top-level Backoff class, retryOn(...), with QueueableJobSetting__mdt defaults.
  • Dependency gatingdependsOn(Async.after(result|id) / afterPrevious().succeeded()/.failed()/.finished()) skips a job and its dependents when a dependency's outcome doesn't match. Targets are referenced by customJobId.
  • Chain control & failure handling — imperative Async.stopChain() / Async.skipJob(customJobId) from a finalizer; outcome reconciled from FinalizerContext so uncatchable failures are caught; rollbackOnJobExecuteFail fixed (was a no-op on its own — behavior change to that flag).
  • Observability — an AsyncResult__c row per job (ran or skipped) with status, chain id, class name, exception detail, skip reason, retry count, and a DependsOnResult__c self-lookup. New AsyncResultAccess permission set grants read FLS (these fields previously had none).
  • Docs updated (queueable.md, getting-started.md).

Test plan

  • RunLocalTests + coverage, non-namespaced scratch — 133 pass, 93%
  • RunLocalTests + coverage, btcdev-namespaced scratch — 133 pass, 93%
  • Real-org E2E: enqueued a dependency chain via anonymous Apex, polled until the async queue drained, verified Account markers + AsyncResult__c rows (status, exception, skip reason, dependency lookup, shared ChainId)
  • prettier --check passes on all changed files

Notes

  • rollbackOnJobExecuteFail now actually rolls back and lets the chain continue (previously a no-op unless paired with continueOnJobExecuteFail) — the one behavior change to a released flag.
  • Whole-repo prettier:verify reports pre-existing drift on files this branch does not touch (e.g. BatchableManager.cls); left out of scope.

Retry & backoff:
- retry(n), capped at 10 (throws above the cap instead of silently clamping)
- Backoff strategies (fixed / exponential / exponential-with-jitter) as a
  top-level Backoff class; retryOn(...) to scope retried exceptions
  (every exception retried by default), with QueueableJobSetting__mdt defaults

Dependency gating & chain control:
- dependsOn(Async.after(result|customJobId) / afterPrevious() +
  succeeded()/failed()/finished()) skips a job and its dependents when a
  dependency's outcome doesn't match; identity is the auto-generated customJobId
- imperative Async.stopChain() / Async.skipJob(customJobId), usable from a finalizer
- reconcile job outcome from FinalizerContext so uncatchable failures are detected
- fix rollbackOnJobExecuteFail to roll back and continue (was a no-op on its own)

Observability:
- one AsyncResult__c row per job (ran or skipped) with Status, ChainId, ClassName,
  exception detail, skip reason, retry count, and a DependsOnResult self-lookup
- AsyncResultAccess permission set granting read FLS (these fields had none)

Docs updated. RunLocalTests passes at 93% coverage on the non-namespaced and
btcdev-namespaced scratch orgs.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
async-lib Ready Ready Preview, Comment Jun 1, 2026 12:56pm

Request Review

@Mateusz7410 Mateusz7410 linked an issue May 31, 2026 that may be closed by this pull request
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 31, 2026

🧪 Apex Test Results

✅ All Tests Passed

==========================================
     APEX TEST EXECUTION SUMMARY
==========================================

📊 Total Tests: 140
✅ Passed: 140
❌ Failed: 0
⏭️  Skipped: 0


🎉 All tests passed successfully!

📦 Download Full Test Results & Logs


📊 Stats: 140 total | ✅ 140 passed | ❌ 0 failed
🤖 Automated comment by Salesforce CI

@codecov
Copy link
Copy Markdown

codecov Bot commented May 31, 2026

Codecov Report

❌ Patch coverage is 96.09756% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.75%. Comparing base (7856c10) to head (dc764f0).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...ce-app/main/default/classes/queue/QueueableJob.cls 89.23% 7 Missing ⚠️
force-app/main/default/classes/Async.cls 88.46% 3 Missing ⚠️
...pp/main/default/classes/queue/QueueableBuilder.cls 90.90% 3 Missing ⚠️
...-app/main/default/classes/queue/QueueableChain.cls 98.79% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #38      +/-   ##
==========================================
+ Coverage   96.52%   96.75%   +0.22%     
==========================================
  Files          14       15       +1     
  Lines         576      956     +380     
==========================================
+ Hits          556      925     +369     
- Misses         20       31      +11     
Flag Coverage Δ
Apex 96.75% <96.09%> (+0.22%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Let jobs classify failures per-exception and reset transient state before
a retry, addressing cases where type-only filtering and shallow cloning fall
short.

- isRetryable(Exception): overridable veto evaluated where the live exception
  exists (catch site for handled failures, finalizer getException() for
  uncatchable ones). AND-composed with retryOn(types): both gates must pass.
- resetForRetry(): overridable hook run on the retry clone to recreate or clear
  transient state (e.g. a Unit of Work) that a shallow clone would otherwise
  carry over.
- Replace failedExceptionType/Message strings with a FailureInfo value; the
  retry decision is now a stored boolean, the captured metadata is audit-only.
- Null-exception fallback: retry only when no retryOn filter is set; a throwing
  override is treated as not-retryable and recorded in RetryHistory.
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.

Add opt-in retry mechanism to QueueableJob` Retry support for failed Apex async jobs (e.g., Queueable)

1 participant