Browse documentationOpen

byrcsc/laravel-data-sync · 1.x

Run history and status.

Read recent runs with sync:status, interpret the counters and statuses, watch queued batch progress, and query the history models directly.

php artisan sync:status                    # the last 10 runs
php artisan sync:status products           # one definition
php artisan sync:status products --limit=25
php artisan sync:status --failures         # one line per failed row

Runs are listed newest first, across all definitions unless you name one.

  Run                          Sync      Status · path             C/U/S/F    Progress
  01JQ8Z3M0K2N4P6R8T0V2X4Y6A   products  completed · bulk          980/20/0/0 1000 rows
  01JQ8Z2A9F1M3P5R7T9V1X3Y5B   products  running · bulk            420/0/0/0  42% (84/200 jobs)
  01JQ8Z1K7D0L2N4Q6S8U0W2Y4C   products  completed · bulk (dry run) 0/0/0/0   12 rows
ColumnWhat it shows
RunThe run ULID — what sync:retry takes
SyncThe definition name
Status · pathThe run status, a dry-run marker, and the write path
C/U/S/FRows created, updated, skipped, failed
ProgressQueued batch progress when a batch exists, otherwise the row total

The write path reads bulk, eloquent, or transfer. It is stored on the run at creation, so which path a run took is recorded rather than inferred. See the write path tradeoff.

Statuses

StatusMeaning
pendingRecorded, waiting on a worker
runningIn progress
completedEvery row written
completed_with_errorsFinished; some rows failed and were recorded
failedThe run did not finish
cancelledThe queued batch was cancelled

completed_with_errors is a success for the file and a problem for some of its rows: the checksum is marked processed, the file is archived, and the failed rows sit in sync_row_failures waiting for you. failed is the opposite — nothing is marked processed and the file is preserved for retry.

Counters

  • Created and updated are decided per row against the destination table.
  • Skipped covers rows the write mode declined (CreateOnly on an existing row, UpdateOnly on a new one), rows that lost a duplicate resolution, and rows a beforeSave() hook returned false for.
  • Failed covers rows that could not be mapped and rows that failed validation.

For a transfer, one file is one row: 1 created for a transferred file, 1 skipped when the destination already held it or the filename did not match.

Batch progress

While a queued run is in flight, sync:status reads its batch record and reports real progress — 42% (84/200 jobs) — instead of a row count. Once the batch record is pruned by the framework, the row total is shown instead.

A run stuck at running with no batch progress usually means a dead worker. sync:doctor warns about runs that have been running for more than an hour.

Failed rows

php artisan sync:status products --failures
01JQ8Z3M0K2N4P6R8T0V2X4Y6A row 412: {"price_cents":["The price cents must be an integer."]} {"SKU":"DS-1041","Price":"n\/a"}
01JQ8Z3M0K2N4P6R8T0V2X4Y6A row 907: {"row":["Undefined array key \"Currency\""]} [payload capture disabled]

Each line carries the run ULID, the row number as the reader counts it, the validation errors keyed by attribute, and the captured raw payload. Rows whose payload capture was off show [payload capture disabled] — see sensitive data.

The run itself also carries a summary: error_summary holds a JSON histogram of field: message counts, which answers "what went wrong with this file" without reading 900 individual rows.

Warnings

sync_runs.warnings records non-fatal problems — most often a source cleanup that failed after a successful import, such as a read-only supplier directory. The run still completed and the data is still imported. Query the column when a supplier reports files reappearing:

SyncRun::query()->whereNotNull('warnings')->latest('id')->get();

Querying the history

use ByRcsc\LaravelDataSync\Models\SyncRun;

$run = SyncRun::query()->where('ulid', $ulid)->firstOrFail();

$run->status;            // RunStatus enum
$run->write_path;        // WritePath enum, or null for a transfer
$run->dry_run;           // bool
$run->rows_created;
$run->unmapped_columns;  // columns in the file no field mapped
$run->error_summary;
$run->archive_path;
$run->file;              // the SyncFile ledger row
$run->failures;          // SyncRowFailure records

SyncRun::query()
    ->where('definition', 'products')
    ->whereIn('status', ['failed', 'completed_with_errors'])
    ->whereDate('created_at', today())
    ->get();

Both models read their table names from data-sync.tables, so custom names work without subclassing. They are plain Eloquent models: build a dashboard, expose them through an admin panel, or ship a daily digest from them.

Remember that runs are pruned on a retention schedule while the ledger is not — see retention and pruning before building reporting that assumes runs live forever.