Job Lifecycle¶
Monitor, list, wait on, and clean up batch Spark jobs.
Overview¶
Once a job is submitted with submit_job() (see Batch Jobs), SparkClient
provides lifecycle management APIs for tracking it through to completion. These
APIs follow the same pattern as TrainerClient, so if you’ve used Kubeflow
Trainer, this will look familiar:
TrainerClient |
SparkClient |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Status Model¶
Job state is derived from the underlying SparkApplication resource and
simplified into four SDK-level states:
class SparkJobStatus(str, Enum):
CREATED = "Created"
RUNNING = "Running"
COMPLETED = "Completed"
FAILED = "Failed"
SDK Status |
SparkApplication States |
|---|---|
|
SUBMITTED, or no state reported yet |
|
RUNNING, SUCCEEDING, SUSPENDING, SUSPENDED, RESUMING |
|
COMPLETED |
|
FAILED, SUBMISSION_FAILED, FAILING, PENDING_RERUN, INVALIDATING, UNKNOWN |
Any SparkApplication state the SDK doesn’t recognize maps to FAILED and logs a
warning, so newly introduced operator states are handled conservatively.
The SparkJob Model¶
@dataclass
class SparkJob:
name: str
namespace: str
status: SparkJobStatus | None = None
creation_timestamp: datetime | None = None
num_executors: int | None = None
driver_pod_name: str | None = None
Lifecycle APIs¶
Get a job:
job = client.get_job(job_name)
print(f"Status: {job.status}")
List jobs, optionally filtered by status:
from kubeflow.spark import SparkJobStatus
jobs = client.list_jobs()
for job in jobs:
print(f"{job.name}: {job.status}")
running = client.list_jobs(status={SparkJobStatus.RUNNING})
Wait for a job to reach a desired status:
completed_job = client.wait_for_job_status(job_name, timeout=3600)
print(f"Final status: {completed_job.status}")
Important
By default, wait_for_job_status() waits for COMPLETED. If the job
instead reaches FAILED — and FAILED isn’t in the status set
you’re waiting for — it raises a RuntimeError immediately rather than
waiting out the timeout. If you want to handle both outcomes yourself
without an exception, wait on both explicitly:
job = client.wait_for_job_status(
job_name,
status={SparkJobStatus.COMPLETED, SparkJobStatus.FAILED},
timeout=3600,
)
if job.status == SparkJobStatus.FAILED:
... # handle failure
timeout and polling_interval must both be positive — a zero or
negative value raises ValueError before any polling starts.
Stream logs:
for line in client.get_job_logs(job_name, follow=True):
print(line)
Note
get_job_logs() reads from the driver pod only, via the Kubernetes API.
Executor-level log access isn’t wired in yet — the driver is where Spark
surfaces stage failures, exceptions, and final job status, so it covers the
common debugging path. Log retrieval is only available while the driver pod
exists — if it has been deleted (for example, due to TTL-based cleanup),
logs may no longer be available.
Delete a job:
client.delete_job(job_name)
Note
get_job_logs() above covers raw pod logs. Structured metrics, job health,
event streaming, and Spark UI access are planned as a dedicated
Observability guide that builds on the job model defined here — watch
this page’s “See also” once that lands.
Common Patterns¶
Submit and wait for completion:
job_name = client.submit_job(job=FileJob(file_source="https://raw.githubusercontent.com/<repo>/<branch>/etl.py"))
completed_job = client.wait_for_job_status(job_name, timeout=3600)
Wait for completion with a timeout:
client.wait_for_job_status(job_name, timeout=3600) # 1 hour max
List all your running jobs:
from kubeflow.spark import SparkJobStatus
jobs = client.list_jobs(status={SparkJobStatus.RUNNING})
for job in jobs:
print(f"{job.name}: {job.status}")
Clean up after inspecting logs:
for line in client.get_job_logs(job_name):
print(line)
client.delete_job(job_name)