Skip to content
About
Main content

Overlap depth before and after a blind compaction

Simulate a modelNeeds Python with NumPy

Problem

is the number of files whose key range contains a given key: the number of files a point lookup must open. Small files that each hold a narrow range of keys have low overlap even if they arrived in no particular order. can make it worse: merging files that arrived together (time-adjacent) combines unrelated key ranges into one wide file, and every lookup then has to open it. Compacting by key range merges files that cover the same keys, and overlap falls.

Generate small files whose customer_id ranges are narrow but written in arrival order. Compute overlap depth at 1,000 random points. Then compact (a) the oldest files together (time-adjacent) and (b) files grouped by key range. Recompute the depth.

The question. What does each compaction do to the number of files a point lookup must open?


Predict

Compacting 200 small files by time (oldest together) into 20: how many files does a point lookup open on average?

Run it

  1. 1Install uv once. It fetches Python and the lab's packages for you.

    Terminal
    curl -LsSf https://astral.sh/uv/install.sh | sh
  2. 2Get the script. It is the whole lab: read it, then download it.

    overlap-depth-before-and-after-a.py42 linesDownload
    View the code
    overlap-depth-before-and-after-a.py
    # /// script
    # requires-python = ">=3.10"
    # dependencies = ["numpy"]
    # ///
    # Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT
    # https://thedataengineering.com/lab-code/LICENSE.txt
    """Overlap depth before and after a compaction.
    
    200 small files written in arrival order, each holding a narrow range of
    customer_id that wanders over time. Overlap depth (files whose [min, max]
    contains a point) at 1,000 random points, then after compacting (a) the oldest
    files together in time-adjacent groups of 10 and (b) files grouped by key
    range into the same number of outputs.
    """
    import argparse, json
    import numpy as np
    
    p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    p.add_argument("--files", type=int, default=200, help="Small files.")
    p.add_argument("--group", type=int, default=10, help="Files merged per compaction output.")
    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)
    centers = rng.uniform(0, 1_000_000, args.files)                         # each file's customers cluster somewhere: arrival order is not key order
    files = [np.clip(rng.normal(c, 5_000, 1000), 0, 1_000_000) for c in centers]
    def depth(fs):
        mn = np.array([f.min() for f in fs]); mx = np.array([f.max() for f in fs]); pts = rng.uniform(0, 1_000_000, 1000)
        d = np.array([((mn <= x) & (x <= mx)).sum() for x in pts]); return float(d.mean()), int(d.max()), len(fs)
    by_time = [np.concatenate(files[i:i + args.group]) for i in range(0, args.files, args.group)]
    allv = np.sort(np.concatenate(files)); k = len(by_time)
    by_key = np.array_split(allv, k)
    rows = []
    for name, fs in (("before: small files in arrival order", files), ("(a) compacted by time (oldest together)", by_time), ("(b) compacted by key range", by_key)):
        mean, mx, n = depth(fs); rows.append({"layout": name, "files": n, "mean_depth": mean, "max_depth": mx})
    for r in rows: print(f"{r['layout']:<42} {r['files']:>4} files   overlap depth mean {r['mean_depth']:6.2f}, max {r['max_depth']}")
    
    if args.json:
        with open(args.json, "w") as f:
            json.dump({"tables": {"layouts": rows}}, f)
    
  3. 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 overlap-depth-before-and-after-a.py
    uv run overlap-depth-before-and-after-a.py --help     # every knob
Knobs
Knob · defaultWhat it changes
--files200 Small files.
--group10 Files merged per compaction output.
--seed0 Random seed.

Our run

Overlap depth at 1,000 random keys

Overlap depth at 1,000 random keys
LayoutFilesMean depthMax depth
before: small files in arrival order2006.4113
(a) compacted by time (oldest together)2017.120
(b) compacted by key range200.9991
Reference runApple M2 · 8 cores · 8 GB
Output · uv run overlap-depth-before-and-after-a.py
before: small files in arrival order        200 files   overlap depth mean   6.41, max 13
(a) compacted by time (oldest together)      20 files   overlap depth mean  17.07, max 20
(b) compacted by key range                   20 files   overlap depth mean   1.00, max 1
macOS 26.6.2 · Python 3.13.3 · numpy 2.5.4 · run 2026-10-20

What we found

Before compaction, a lookup opened 6.41 files on average across 200. Compacting by time into 20 files raised that to 17.1: almost every merged file now spans the whole key range. Compacting by key range into the same number of files lowered it to 0.999.


Read it

Fewer files is not the goal of compaction; fewer files per query is. Merging by arrival time reduces the file count and can multiply the work of every lookup. Compaction should sort or cluster by the keys queries filter on.