Skip to content
About
Main content

Anomaly count equals rate times window

Simulate a modelNeeds Python with NumPy

Problem

A consistency needs two operations close enough in time to conflict: a write and a read inside the , two transactions inside each other's read-then-write gap. By Little's rule, the number of operations inside a W at any moment is the arrival rate times the window: λ × W. So the exposure to anomalies is set by two terms, and the window is the one you control. The same code is a hundred times more exposed when lag grows from 20 ms to 2 s, and a thousand times less exposed when a read-modify-write happens inside one statement instead of across two .

Simulate conflicting operations arriving at random (a Poisson process) and count how many fall inside a window W of an earlier operation. Compare with λ × W for W = 20 ms (normal replica lag), 2 s (lag under load), 50 ms (application read-modify-write) and 50 µs (one SQL statement).

The script also sweeps a range of windows in between, for the chart.

The question. At 667 conflicting operations per second, how many fall inside each window, and how far apart are a 50 ms application read-modify-write and a 50 µs single statement?


Predict

How many times more conflicting operations does a 50 ms application read-modify-write expose than a 50 µs single statement?

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.

    anomaly-count-equals-rate-times-window.py44 linesDownload
    View the code
    anomaly-count-equals-rate-times-window.py
    # /// script
    # requires-python = ">=3.10"
    # dependencies = ["numpy"]
    # ///
    # Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT
    # https://thedataengineering.com/lab-code/LICENSE.txt
    """Anomaly count equals rate times window.
    
    Conflicting operations arrive at random (a Poisson process); for each one,
    count how many others fall inside a window W after it, and compare the
    average with lambda x W for windows from one SQL statement to lag under load.
    """
    import argparse, json
    import numpy as np
    
    p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    p.add_argument("--rate", type=float, default=667, help="lambda: conflicting operations per second (667/s = 40,000 per minute).")
    p.add_argument("--seconds", type=float, default=60, help="Simulated time.")
    p.add_argument("--seed", type=int, default=7, 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)
    
    lam, T = args.rate, args.seconds
    n = rng.poisson(lam * T)
    t = np.sort(rng.uniform(0, T, n))
    named = [("single statement 50us", 50e-6), ("replica lag 20ms", 0.020), ("app read-modify-write 50ms", 0.050),
             ("lag under load 2s", 2.0)]
    grid = sorted(set([w for _, w in named] + list(np.logspace(-5, 0.5, 12))))
    rows = []
    for W in grid:
        inside = np.searchsorted(t, t + W) - np.arange(n) - 1      # other operations in the next W seconds
        rows.append({"window_s": float(W), "measured": float(inside.mean()), "lambda_W": lam * W})
    for name, W in named:
        r = next(r for r in rows if r["window_s"] == W)
        print(f"{name:30s} measured {r['measured']:10.4f}   lambda*W {r['lambda_W']:10.4f}")
    a, b = (next(r for r in rows if r["window_s"] == W)["measured"] for W in (0.050, 50e-6))
    print(f"\napp read-modify-write window / single statement window: {a / b:,.0f}x more conflicts")
    
    if args.json:
        with open(args.json, "w") as f:
            json.dump({"tables": {"windows": rows, "named": [{"window": nm, **next(r for r in rows if r["window_s"] == W)} for nm, W in named],
                                  "summary": [{"ratio": a / b, "n": int(n)}]}}, 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 anomaly-count-equals-rate-times-window.py
    uv run anomaly-count-equals-rate-times-window.py --help     # every knob
Knobs
Knob · defaultWhat it changes
--rate667 lambda: conflicting operations per second (667/s = 40,000 per minute).
--seconds60 Simulated time.
--seed7 Random seed.

Our run

Operations inside the window, measured against λ × WApple M2 · 8 cores · 8 GB
Measuredλ × W
Data table
Operations inside the window, measured against λ × W
Window (seconds)Measuredλ × W
10 µs0.0070.007
31.6 µs0.0220.021
50 µs0.0340.033
100 µs0.0670.067
316 µs0.2110.211
1 ms0.6660.667
3.16 ms2.112.11
10 ms6.666.67
20 ms13.313.3
31.6 ms21.121.1
50 ms33.433.4
100 ms66.866.7
316 ms211211
1 s663667
2 s1,3201,330
3.16 s2,0602,110
macOS 26.6.2 · Python 3.13.3 · numpy 2.5.4 · run 2026-10-20

The four windows

The four windows
WindowMeasuredλ × W
single statement 50us0.0340.033
replica lag 20ms13.313.3
app read-modify-write 50ms33.433.4
lag under load 2s1,3201,330
Output · uv run anomaly-count-equals-rate-times-window.py
single statement 50us          measured     0.0341   lambda*W     0.0334
replica lag 20ms               measured    13.3480   lambda*W    13.3400
app read-modify-write 50ms     measured    33.4035   lambda*W    33.3500
lag under load 2s              measured  1315.1177   lambda*W  1334.0000

app read-modify-write window / single statement window: 978x more conflicts

What we found

The measured averages matched λ × W across five orders of magnitude of window, as expected: 13.3 for 20 ms of replica lag, 1,320 for 2 s, 33.4 for a 50 ms application read-modify-write and 0.034 for a 50 µs statement. The application round trip exposed 978× more conflicting operations than the single statement.


Read it

This is the window argument of Transactional isolation.

Anomalies in flight ≈ conflicting rate × window. You rarely control the rate; you always control the window. Move a read-modify-write into one statement (SET balance = balance + 50), shorten replica lag, send a user's reads to the just after that user writes: each shrinks W, and the exposure shrinks with it, without changing the .