Schedule icon
WorkingDirectory icon
Script icon
Docker icon
Log icon
If icon
SlackIncomingWebhook icon

Compact small files in a Delta Lake table and vacuum it safely

Check a Delta Lake table for small files, compact it past your thresholds, then dry-run or apply a vacuum that respects the 7-day retention. No credentials.

Categories
Data

Streaming and micro-batch jobs write many small files into a Delta table. Reads slow down as the file count grows, and nothing outside a managed platform compacts the table for you. Compaction and vacuum are the two maintenance commands that fix this, and they need different care. Compaction only adds a new table version. Vacuum deletes files, which removes the history that time travel and slow readers depend on.

This flow measures the table first and compacts only when the numbers justify it. It then lists what vacuum could remove, and deletes files only when you ask for APPLY and the files are older than the retention period. It runs with no credentials: in demo mode it builds its own throwaway Delta table.

This blueprint was created by Uttam345.

What you get

  • A before and after view of the table: file count, small file count, small file ratio, table size.
  • Compaction that is skipped on healthy tables, so a nightly schedule does not create a new version for nothing.
  • A vacuum that defaults to DRY_RUN and cannot be pointed below the 168 hour minimum, because the input has a floor of 168 and delta-rs rejects lower values.
  • A report line per run, plus an optional Slack message when a run compacted or deleted files.

Who it's for

  • Data engineers who run Delta tables outside Databricks, on object storage, and maintain them by hand.
  • Teams that want compaction on a schedule but want a dry run before anything is deleted.

Why orchestrate this with Kestra

A cron job running two Python calls has no record of what it changed and no way to stop between measuring and acting. Here each step is its own task with its own logs and outputs. The decision to compact is a visible output of assess_table. The vacuum listing is a separate task, so a failure there leaves the compacted table intact and the run shows exactly which step stopped.

Prerequisites

  • Docker for the task runner. Each Python task runs in python:3.12-slim.
  • Network access to PyPI. Every Python task runs pip install for deltalake==1.6.6, pyarrow==25.0.1 and kestra==2.0.0, so a run takes a little longer than the work itself.
  • No secrets for the demo.
  • Optional secret SLACK_WEBHOOK_URL, used only when notify_slack is true.

How it works

  1. work (io.kestra.plugin.core.flow.WorkingDirectory) keeps the demo table and every step that touches it in one directory.
  2. seed_demo_table (io.kestra.plugin.scripts.python.Script) runs only in demo mode. It appends 40 small batches with write_deltalake, which leaves 40 tiny data files.
  3. assess_table reads get_add_actions and counts files under small_file_mb. It sets needs_compaction when both min_small_files and small_file_ratio are met.
  4. compact_small_files runs optimize.compact toward target_file_mb when needs_compaction is true, then counts the files again.
  5. vacuum_unreferenced makes two dry runs. One lists files older than retention_hours. The other lists every unreferenced file regardless of age, with the retention check turned off, which is safe only because it deletes nothing. With vacuum_mode: APPLY it then deletes the first list and nothing else.
  6. report (io.kestra.plugin.core.log.Log) prints the before and after summary.
  7. notify (io.kestra.plugin.core.flow.If) posts to Slack through io.kestra.plugin.slack.notifications.SlackIncomingWebhook when notify_slack is true and the run compacted or deleted files.
  8. errors logs the usual causes when any step fails.

Inputs

  • demo_mode (BOOL, default true): build and use a throwaway table.
  • table_uri (STRING, optional): your table, used when demo_mode is false. It must be reachable from inside the task container, so a path on your host will not work.
  • small_file_mb (INT, default 32): files smaller than this are small.
  • min_small_files (INT, default 10): do not compact below this count.
  • small_file_ratio (FLOAT, default 0.5): do not compact below this share of small files.
  • target_file_mb (INT, default 128): compaction target file size.
  • retention_hours (INT, default 168, minimum 168): files younger than this are never deleted.
  • vacuum_mode (SELECT, default DRY_RUN): APPLY deletes eligible files.
  • notify_slack (BOOL, default false): Slack message when a run changed something.

Outputs

Each Python task returns values you can read in later tasks or in the execution view:

  • {{ outputs.assess_table.vars.small_ratio }} and {{ outputs.assess_table.vars.needs_compaction }}
  • {{ outputs.compact_small_files.vars.files_removed }} and {{ outputs.compact_small_files.vars.files_after }}
  • {{ outputs.vacuum_unreferenced.vars.eligible_files }} and {{ outputs.vacuum_unreferenced.vars.deleted_files }}

Quick start

  1. Save the flow and run it with the defaults.
  2. Expect 40 files, 40 small, then compaction rewriting 40 files into 1.
  3. Expect the vacuum line to show 40 unreferenced files and 0 older than 168 hours. That is correct. The 40 old files stay on disk until they pass the retention period, which is what keeps time travel and running readers safe. Nothing is deleted in the demo.
  4. To see a deletion, point table_uri at a table of your own with demo_mode off and set vacuum_mode to APPLY.

How to extend

  • Open a table on object storage: table_uri accepts any location delta-rs can open. Pass credentials as environment variables on the Python tasks, for example AWS_ACCESS_KEY_ID: "{{ secret('AWS_ACCESS_KEY_ID') }}". They are not wired here because the demo needs no secrets.
  • Limit compaction to recent partitions: optimize.compact accepts partition_filters.
  • Track fragmentation over time: write small_ratio to the KV store with io.kestra.plugin.core.kv.Set and compare it on the next run.
  • Enable the nightly trigger once table_uri points at your own table. Run it outside your heaviest write window.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.