Files
filament/packages
Josh Salway 8f341895a9 [4.x] fix(actions): batch cancel, failed() handler, explicit retry defaults (#19754)
* fix(actions): add batch-cancel early-return and failed() handler to ExportCsv and ImportCsv

Two pure additions on both row-processor jobs. No existing property
is touched; `$maxExceptions = 5`, `retryUntil()`, `backoff()`,
`middleware()`, and `tags()` delegation all preserved.

- `if ($this->batch()?->cancelled()) { return; }` at the top of
  `handle()` so a batch cancelled via `Bus::findBatch($id)->cancel()`
  stops in-flight chunks on the next worker cycle instead of running
  every popped chunk to completion.
- `failed()` handler with scalar-only log context (`export_id` /
  `import_id`, `page`, `batch_id`, exception class, exception
  message). No secret leakage.

Tests:

- `{Export,Import}CsvBatchCancellationTest`: use
  `withFakeBatch(cancelledAt: ...)` + Mockery
  `shouldNotReceive('getAttribute')->with('user')` to prove the
  early-return fires.
- `{Export,Import}CsvFailedHandlerTest`: call
  `failed(new RuntimeException(...))` against `Log::shouldReceive('error')`
  to pin the structured log context shape.

* fix(actions): add failed() + explicit $tries/$maxExceptions to three standalone jobs

These three jobs have never had retry properties set. The implicit
design is "inherit the worker default," which varies by platform:
`queue:work` uses 1, `vapor:work` signature says 0 but runtime
overrides to `SQS_TRIES ?? 3`, Horizon supervisor configs vary.
Make the intent explicit per each job's idempotency and runtime
profile.

PrepareCsvExport (Batchable orchestrator, non-idempotent):
`$tries = 1` (retry would re-dispatch children via `batch()->add()`),
`$maxExceptions = 0`, batch-cancel early-return, `failed()` handler.
No `$timeout`: work scales with source-query size, keep worker-
inherited timeout rather than cap at 60s.

ExportCompletion (not Batchable, non-idempotent notifier):
`$tries = 1` (retry would double-notify), `$maxExceptions = 0`,
`failed()` handler. No `$timeout`: single `Notification::send()` or
`Notification::sendToDatabase()` call, sub-second runtime.

CreateXlsxFile (not Batchable, idempotent file converter):
`$tries = 3` (re-reads CSV chunks and overwrites same XLSX, so
transient failures recover), `$backoff = [30, 60, 300]` (avoids
0-second retry loop since `$tries > 1`), `$maxExceptions = 0`,
`failed()` handler. No `$timeout`: XLSX conversion runtime is
O(row count), large exports need worker-inherited timeout (Vapor
Lambda ceiling, Horizon supervisor) rather than 60s cap.

None of these three delegate `retryUntil()`, so `$tries` is the
active attempts bound. Public API unchanged.

* Remove tests (too brittle)

* Remove failed() hooks (too opinionated)

* cs

---------

Co-authored-by: Dan Harrin <git@danharrin.com>
2026-04-24 08:49:45 +01:00
..
2026-02-17 09:39:16 +00:00