Schedule icon
SparkCLI icon
Docker icon
If icon
Pause icon
Fail icon
Log icon
SlackIncomingWebhook icon

Clean orphaned Spark output staging safely

Safely clean abandoned Spark output staging directories with age, liveness, approval and post-delete checks.

Categories
Data

Safety

A killed Spark write can leave _temporary or _SPARK_STAGING trees beside committed partitions. They can consume a second copy of the output and a later reader can mistake them for data.

Guard What it stops
Output-root allowlist A delete can only target a staging child below output_path.
Age plus Spark-master liveness A recent write or a path containing a live application id is spared.
Approval and immediate re-read A changed tree during the pause fails before any delete.
Post-delete tree and committed digest A survivor or changed committed path fails the execution.

How it works

  1. inspect_before (io.kestra.plugin.spark.SparkCLI) runs hadoop fs -ls -R against the output URI, reads the Spark master JSON endpoint and emits candidate paths, ages, bytes and a digest of every non-staging path.
  2. remediation_gate (io.kestra.plugin.core.flow.If) keeps the default dry run read-only. When armed and candidates exist, hold (io.kestra.plugin.core.flow.Pause) records operator approval.
  3. prewrite_recheck (io.kestra.plugin.spark.SparkCLI) repeats the detector after the pause. stale_plan_guard (io.kestra.plugin.core.flow.If) fails if either the candidate set or committed digest changed.
  4. remediate (io.kestra.plugin.spark.SparkCLI) invokes hadoop fs -rm -r -f only for the rechecked staging roots, serially within this flow's queue.
  5. verify (io.kestra.plugin.spark.SparkCLI) lists the tree again and fails if staging survived or the committed digest changed.

What you get

  • A measured staging byte count before and after cleanup from the storage CLI.
  • A dry-run report that names old staging roots and spares recent or live ones.
  • An approval record and a failed execution when the plan goes stale.

Who it's for

Data platform teams operating Spark output paths on S3-compatible object storage.

Why orchestrate this with Kestra

Spark and object storage expose the raw listing and delete primitives but do not provide an approval record, a stale-plan boundary or a durable post-delete assertion. Kestra supplies those controls with an execution history and a disabled safe-mode schedule.

Tested end to end

The test fixture uses Apache Spark 3.5.1 and an S3-compatible MinIO service on a private Docker network. The flow runs the SparkCLI plugin 1.2.9 on Kestra 2.0.5; the evidence packet records the exact execution ids and before/after byte totals.

Case Execution Result
Healthy control 11Tk2kQj7IH4wsPBpoddVc SUCCESS, no candidates
Degraded dry run XyHP2xSa5rmiorrnYkQq7 SUCCESS, 8,388,625 staging bytes identified
Declined approval 1zCY8fUgDpm353OiegJsgR SUCCESS, staging preserved
Remediation 1BwQ81CVhLUzlyfyqooQzp SUCCESS, 8,388,625 bytes removed and 13 committed bytes preserved
Repeat QfQWujeCzzMjzoMjpg665 SUCCESS, no candidates
Ineffective write 4OSFafdykAQrG03iDFc91J FAILED at stale-plan guard
Race or partial 6tjwDb2gifaBPDe7Bv4X1L FAILED at stale-plan guard

Prerequisites

  • Kestra 2.0.5 with the Spark plugin and a Docker task runner on the same network as the Spark master and S3 endpoint.
  • A Spark standalone master JSON endpoint and an S3 identity that can list and delete only the chosen output prefix.

Secrets

  • SPARK_S3_ACCESS_KEY: access key for the output prefix.
  • SPARK_S3_SECRET_KEY: secret key for the output prefix.
  • SLACK_WEBHOOK_URL: incoming webhook for failed-run alerts.

Inputs

Input Default Purpose
output_path s3a://sstage/output Bounded output root.
s3_endpoint secret('SPARK_S3_ENDPOINT') S3A endpoint for the output store.
spark_master_web_url secret('SPARK_MASTER_WEB_URL') Spark standalone master JSON endpoint.
task_network bridge Docker network for task runners.
max_job_runtime_seconds 3600 Age boundary for staging candidates.
auto_remediate false Arms the approval and delete path.
dry_run true Forces read-only behavior by default.

Quick start

  1. Configure the three secrets and set the S3 and Spark endpoint inputs.
  2. Execute with dry_run=true and auto_remediate=false; inspect the candidate byte total.
  3. Set auto_remediate=true, dry_run=false, execute and approve the listed roots from the Pause.
  4. Confirm verify reports zero surviving staging roots and an unchanged committed digest.

How to extend

Add a tenant-specific path policy to the detector and keep it below the same output-root boundary. If your cluster manager exposes live applications through another endpoint, replace only the liveness read while keeping the post-approval recheck.

Limits: the detector treats an application id as live only when the Spark standalone master JSON contains it. It does not infer liveness from a history server or a scheduler whose application ids are not in that endpoint.

Links

This blueprint was created by zkasuran.

See How

New to Kestra?

Use blueprints to kickstart your first workflows.