Checkpoint interval optimum
Simulate a modelNeeds Python
Problem
A long job checkpoints so that a failure loses only the work since the last checkpoint. Each checkpoint costs C; between checkpoints the expected loss on failure is half the interval. The wasted fraction is therefore about C/I + I/(2·MTBF), which is minimal at I* = √(2·C·MTBF). The two terms trade off smoothly, so the curve is flat near its minimum: a moderately wrong interval costs little.
Find the minimum of C/I + I/(2·MTBF) and test the "flat near the minimum" claim. Simulate a job that runs for 30 days of simulated time. Failures arrive as a Poisson process with MTBF = 1 day. A checkpoint costs C = 30 s. Sweep I from 2 minutes to 6 hours and measure the fraction of time that is not useful work. Then repeat with MTBF = 4 days while keeping the interval tuned for 1 day.
The question. Where is the minimum waste, how close does the simulation come to the formula, and how much does a 4× wrong MTBF cost?
Predict
The minimum near I = 2,277 s (about 38 minutes) with an overhead of about 2.6%. With the wrong MTBF (4x off), the overhead rises only modestly (the best interval would differ by 2x), which shows the curve is flat.
Run it
1Install uv once. It fetches Python and the lab's packages for you.
Terminal curl -LsSf https://astral.sh/uv/install.sh | sh2Get the script. It is the whole lab: read it, then download it.
View the codeHide the code
checkpoint-interval-optimum.py # /// script # requires-python = ">=3.10" # dependencies = ["numpy"] # /// # Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT # https://thedataengineering.com/lab-code/LICENSE.txt """The checkpoint interval optimum. A 30-day job with Poisson failures (MTBF 1 day) and a 30 s checkpoint; on a failure it loses the work since the last checkpoint. Wasted time across intervals from 2 minutes to 6 hours, against C/I + I/(2 MTBF) and its minimum I* = sqrt(2 C MTBF). Then the same intervals when the MTBF is really 4 days. """ import argparse, json, math import numpy as np p = argparse.ArgumentParser(description=__doc__.splitlines()[0]) p.add_argument("--days", type=float, default=30, help="Simulated days of useful work.") p.add_argument("--mtbf-h", type=float, default=24, help="Mean time between failures, hours.") p.add_argument("--cost-s", type=float, default=30, help="Seconds per checkpoint.") p.add_argument("--trials", type=int, default=20, help="Simulated jobs per interval.") p.add_argument("--seed", type=int, default=0, help="Random seed.") p.add_argument("--json", help="Also write the results to this JSON file.") args = p.parse_args() rng = np.random.default_rng(args.seed) C = args.cost_s; work = args.days * 86400 intervals = np.unique(np.geomspace(120, 6 * 3600, 18).astype(int)) def overhead(I, mtbf): total = 0.0 for _ in range(args.trials): done, clock = 0.0, 0.0 next_fail = rng.exponential(mtbf) while done < work: seg = min(I, work - done) if clock + seg + C <= next_fail: clock += seg + C; done += seg else: clock = next_fail; next_fail = clock + rng.exponential(mtbf) # the segment is lost total += clock return (total / args.trials - work) / (total / args.trials) rows = [] for real in (args.mtbf_h, args.mtbf_h * 4): for I in intervals: rows.append({"mtbf_h": real, "interval_s": int(I), "waste_pct": overhead(I, real * 3600) * 100, "formula_pct": (C / I + I / (2 * real * 3600)) * 100}) best = math.sqrt(2 * C * args.mtbf_h * 3600) at_best = min((r for r in rows if r["mtbf_h"] == args.mtbf_h), key=lambda r: abs(r["interval_s"] - best)) same_I_4x = min((r for r in rows if r["mtbf_h"] == args.mtbf_h * 4), key=lambda r: abs(r["interval_s"] - best)) best4 = math.sqrt(2 * C * args.mtbf_h * 4 * 3600) opt_4x = min((r for r in rows if r["mtbf_h"] == args.mtbf_h * 4), key=lambda r: r["waste_pct"]) print(f"I* = sqrt(2 C MTBF) = {best:.0f} s ({best / 60:.0f} min); simulated waste near it {at_best['waste_pct']:.2f}% " f"(formula {at_best['formula_pct']:.2f}%)") print(f"if the MTBF is really 4x longer: waste at the same interval {same_I_4x['waste_pct']:.2f}%, best possible " f"{opt_4x['waste_pct']:.2f}% at {opt_4x['interval_s']:.0f} s (I* would be {best4:.0f} s)") if args.json: with open(args.json, "w") as f: json.dump({"tables": {"intervals": rows, "summary": [{"best_s": best, "waste_at_best": at_best["waste_pct"], "formula_at_best": at_best["formula_pct"], "waste_wrong_mtbf": same_I_4x["waste_pct"], "waste_opt_4x": opt_4x["waste_pct"], "best4_s": best4}]}}, f)3Run it in the folder you saved it to, with the defaults first. Then change a knob from the table below and run it again.
Terminal uv run checkpoint-interval-optimum.py uv run checkpoint-interval-optimum.py --help # every knob
| Knob · default | What it changes |
|---|---|
| --days30 | Simulated days of useful work. |
| --mtbf-h24 | Mean time between failures, hours. |
| --cost-s30 | Seconds per checkpoint. |
| --trials20 | Simulated jobs per interval. |
| --seed0 | Random seed. |
Our run
Data table
| Checkpoint interval (s) | series | Waste (%) |
|---|---|---|
| 120 | 24 | 20.1 |
| 162 | 24 | 15.7 |
| 221 | 24 | 12.1 |
| 300 | 24 | 9.26 |
| 407 | 24 | 7.1 |
| 552 | 24 | 5.49 |
| 750 | 24 | 4.28 |
| 1,020 | 24 | 3.46 |
| 1,380 | 24 | 2.95 |
| 1,880 | 24 | 2.66 |
| 2,550 | 24 | 2.7 |
| 3,460 | 24 | 2.93 |
| 4,690 | 24 | 3.35 |
| 6,370 | 24 | 4.29 |
| 8,640 | 24 | 5.19 |
| 11.7K | 24 | 7.18 |
| 15.9K | 24 | 9.23 |
| 21.6K | 24 | 11.9 |
| 120 | 96 | 20 |
| 162 | 96 | 15.6 |
| 221 | 96 | 12 |
| 300 | 96 | 9.13 |
| 407 | 96 | 6.92 |
| 552 | 96 | 5.25 |
| 750 | 96 | 3.95 |
| 1,020 | 96 | 3 |
| 1,380 | 96 | 2.3 |
| 1,880 | 96 | 1.83 |
| 2,550 | 96 | 1.53 |
| 3,460 | 96 | 1.31 |
| 4,690 | 96 | 1.33 |
| 6,370 | 96 | 1.4 |
| 8,640 | 96 | 1.6 |
| 11.7K | 96 | 2.19 |
| 15.9K | 96 | 2.64 |
| 21.6K | 96 | 3.37 |
I* = sqrt(2 C MTBF) = 2277 s (38 min); simulated waste near it 2.70% (formula 2.65%)
if the MTBF is really 4x longer: waste at the same interval 1.53%, best possible 1.31% at 3455 s (I* would be 4554 s)What we found
The optimum sat at 2,280 s, as the formula says, with 2.7% of time wasted in the simulation against 2.65% from the formula. If the MTBF is really four days, keeping that interval wasted 1.53%, against the best possible 1.31%: a small loss for a 4× error, because the curve is flat near its minimum. In this simulation the best interval for the longer MTBF was shorter than the formula's 4,550 s, a sign of how flat the curve is there.
Read it
Getting the checkpoint interval exactly right does not matter much; getting it in the right order of magnitude does. Far to the left of the optimum the checkpoint cost dominates (the curve rises as 1/I), far to the right the lost work does. The square-root rule also says how the optimum moves: four times the MTBF only doubles the interval.