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”A batch job over a big file could try to hold too much in memory. Clinker won’t: you give it
a memory budget, and when a buffering step (an aggregate, a reshape) would exceed it,
Clinker spills the overflow to disk and keeps going instead of crashing.
Set the budget on the command line:
clinker run customer_etl.yaml --memory-limit 256M --threads 4--memory-limit defaults to 512M; --threads sets how much runs in parallel. Or pin the
budget in the pipeline itself. This is Clinker’s real scd_type2 example, which sets a tiny
budget on purpose so even a small run exercises the spill path:
pipeline: name: scd_type2_backfill memory: { limit: "16K", backpressure: spill } # tight budget → spill to disk🌱 New here? — backpressure: spill
backpressure says what to do when a step fills its memory budget. spill writes the
overflow to a temp file and reads it back: slower, but the run finishes in bounded memory.
(The other modes pause or do both.) The point: a job too big for RAM still completes.
Now change it. Take that comfortable --memory-limit 256M and crank the budget down to a
deliberately tiny --memory-limit 16K, or set it in the pipeline the way the scd_type2
example does:
pipeline: memory: { limit: "16K", backpressure: spill } # far below what the data needsRead 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 success. Every row made it through.2means completed, but some rows were dead-lettered (the DLQ from lesson 07). The job did its work; you just have rows to review. This is not a failure.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.