Operating a pipeline — run it for real
The question for this lesson: your pipeline is built and validated. Now you have to operate it: run it on real volumes without exhausting memory, know whether it actually succeeded, and run it on a schedule. How?
What you’ll be able to do
Section titled “What you’ll be able to do”- Bound a run’s memory with
--memory-limit(and let Clinker spill to disk under pressure). - Read the exit code to tell a clean run from a partial one from a failure.
- Find a run’s metrics and logs.
- Schedule a pipeline with external tools, because Clinker has no built-in scheduler.
Clinker is a finite batch job
Section titled “Clinker is a finite batch job”This is the mental model the whole lesson rests on: Clinker reads its inputs, processes
them, writes its outputs, and exits. It is not a server and not a daemon; there is no
“running Clinker” sitting in the background waiting. Every run is one finite job with a
beginning and an end, exactly like grep or a backup script. That single fact decides how
you bound it, watch it, and schedule it.
Bound the memory; spill when it’s tight
Section titled “Bound the memory; spill when it’s tight”Clinker accounts retained state and responds to pressure with admission refusal, backpressure, spilling, or a controlled failure. Spilling helps when an operator can release enough memory; it does not guarantee that every workload fits every budget. Source decoding, writer preparation, irreducible state, and the process baseline still need space.
clinker run customer_etl.yaml --memory-limit 256M --threads 4The default memory limit is 512M. You can also set it in the pipeline:
pipeline: name: customer_etl memory: { limit: "256M", backpressure: spill }🌱 New here? — backpressure: spill
The spill policy prefers freeing retained records to disk. A producer-pausing policy also needs a satisfiable budget above measured baseline RSS; otherwise it could wait forever for memory the process cannot release. Spill-only mode can attempt progress below that baseline, but can still fail to admit required state. Neither mode turns the configured limit into an OS-enforced RSS ceiling.
Read the exit code
Section titled “Read the exit code”When the run ends, its exit code is how a script (or you) know what happened. The ones to recognize:
0is clean completion. Filters and routing may intentionally omit rows.2means completed, but some rows were dead-lettered (the DLQ from lesson 07). The job did its work; you just have rows to review. Whether that is acceptable depends on your data-quality policy.1is a failure: a config/schema error, a fail-fast error, or the memory budget was blown.3is too many bad rows (a DLQ-rate limit) or a CXL evaluation error;4is an I/O, format, or spill error;130is interrupted (Ctrl-C / SIGTERM).
Find the metrics and logs
Section titled “Find the metrics and logs”To see how a run behaved (rows in/out per node, timings), spool metrics to a directory and collect them:
clinker run customer_etl.yaml --metrics-spool-dir ./metricsclinker metrics collect ./metricsFor more detail while it runs, raise the log level:
clinker run customer_etl.yaml --log-level debugSchedule it: with your own tools
Section titled “Schedule it: with your own tools”Because Clinker is a finite batch job with no built-in scheduler or daemon, you don’t ask Clinker to “run nightly.” You run it once, and let your operating system or orchestrator do the scheduling: cron, a systemd timer, or Airflow.
# crontab — run the customer ETL every night at 02:000 2 * * * clinker run /etc/clinker/customer_etl.yaml >> /var/log/customer_etl.log 2>&1That’s the whole pattern: a normal command, on a normal schedule, with its exit code and logs captured like any other batch job.