Task · SFL-0037

cli: backfill --dry-run — preview file count, source bytes and API calls before pushing a historical window

Status
To Do
Labels
followup, phase-1, roadmap
Milestone
Feature roadmap — operator ergonomics & platform capabilities
Updated
2026-08-14

Description

What

sf2loki backfill has no way to preview the size of a window before it starts pushing.

The subparser at src/sf2loki/cli.py:99-142 defines --since, --until, --event-types, --interval, --ingest-timestamps, --concurrency, --org — no preview/estimate flag. The dispatch block at src/sf2loki/cli.py:202-240 calls run_backfill unconditionally.

run_backfill (src/sf2loki/backfill.py:707) goes straight to execution:

The only volume report is _print_summary (src/sf2loki/backfill.py:697), printed after the run.

The volume signal needed for a preview is already fetched for free. The listing SOQL selects LogFileLength (src/sf2loki/salesforce/eventlogfile_client.py:169) and parses it into EventLogFileMeta.length (src/sf2loki/salesforce/eventlogfile_client.py:190, field declared at :90). Nothing in the backfill path reads .length today. So per-type file counts and total source bytes for a window are computable from listing calls alone — no blob downloads, no pushes.

Two related facts verified while scoping this, both of which the preview output and docs should state:

  1. Backfill is not bounded by the egress budget. EgressGovernor is constructed only in src/sf2loki/app.py:1031-1032 for the daemon pipeline; src/sf2loki/backfill.py never imports it and pushes through LokiSink directly (src/sf2loki/backfill.py:740, _push_with_retry at :345). A sink.loki.egress daily byte budget does not cap a backfill run. docs/sources/cost-controls.md does not mention backfill at all.
  2. Backfill does not apply per-type sample. Sampling is applied only in the live sources (src/sf2loki/sources/eventlogfile_source.py:431,646,694, eventlog_objects_source.py:497, apexlog_source.py:256, pubsub_source.py:704-705). backfill.py applies transforms/row filters (src/sf2loki/backfill.py:770-775, _shape_file_rows at :291) but never EventLogFileTypeConfig.sample. A preview therefore must not print a “post-sampling rows” estimate — there is no sampling in this path to model.

Why it matters

An operator runs sf2loki backfill --since 2026-01-01 for a high-volume type such as ApiTotalUsage against a Grafana Cloud stack shared with production streaming. There is no supported way to answer “how much will this push?” first. The costs are discovered only mid-run:

Aborting mid-run is safe (the checkpoint is resumable) but the bytes already pushed are already billed and already resident in Loki. The information needed to size the window correctly costs only listing SOQL calls and is already on the wire.

Proposed approach

Add --dry-run to the backfill subparser in src/sf2loki/cli.py:99-142, plumbed through to run_backfill as a keyword argument.

Behaviour of run_backfill(..., dry_run=True):

Docs:


Imported from GitHub issue #121 on 2026-08-14, when this repo migrated from GitHub Issues to Backlog.md. The original issue has been deleted; its verbatim body, labels and comments are preserved in archive/issues-dump.json (jq '.[] | select(.number == 121)' archive/issues-dump.json).

Filed from the 2026-07-30 full-repo audit (11 finder lanes + adversarial verification per finding).

Acceptance Criteria

Definition of Done

References

View the source file on GitHub