Skip to content
About
Main content

Count I-confluence in your own schema

Simulate a modelNeeds Python

Problem

Some constraints can be checked by each alone and still hold after replicas merge; others cannot. The test is confluence (): if two states that each satisfy the invariant, reached independently from a common ancestor, always merge into a state that satisfies it, the invariant needs no coordination. Per-row checks pass. Uniqueness of a value users choose, upper bounds on a quantity transactions push upward, and foreign keys under deletion fail: two replicas can each accept the same username, or each grant the last unit. For bounds there is one escape, escrow: pre-split the slack between replicas, at a coordination rate of consumption ÷ slack per replica.

List every constraint in a real schema: UNIQUE, CHECK, NOT NULL, foreign keys, "balance >= 0", "stock >= 0", "at most N". Mark each as I-confluent or not using the table in The unification and its price (uniqueness, upper bounds, and foreign keys under deletion are not). For each that is not, write down what mechanism enforces it today, and how often that mechanism coordinates. If it is a bound, compute the escrow rate: consumption per second divided by slack per replica.

The script classifies a small example shop schema (24 constraints, with assumed consumption rates for the bounds); replace its list with your own schema's constraints.

The question. What share of the constraints really needs coordination, and how often would escrow have to refill for each bound?


Predict

In the example schema, what share of the constraints needs coordination?

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.

    count-i-confluence-in-your-own.py75 linesDownload
    View the code
    count-i-confluence-in-your-own.py
    # /// script
    # requires-python = ">=3.10"
    # ///
    # Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT
    # https://thedataengineering.com/lab-code/LICENSE.txt
    """Count I-confluence in your own schema.
    
    Lists every constraint of a schema, marks each as invariant-confluent (safe
    to check on each replica alone) or not, names what would enforce the others,
    and for each bound computes the escrow rate. The schema below is a small shop;
    replace SCHEMA with your own constraints.
    """
    import argparse, json
    
    p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    p.add_argument("--replicas", type=int, default=3, help="Replicas that accept writes (for the escrow split).")
    p.add_argument("--json", help="Also write the results to this JSON file.")
    args = p.parse_args()
    
    # (table, constraint, kind, detail). kinds: not_null, check_row, unique_generated, unique_chosen,
    # fk_insert_only, fk_with_deletes, lower_bound (with decrements), upper_bound (with increments)
    SCHEMA = [
        ("customers", "id PRIMARY KEY (uuid)", "unique_generated", {}),
        ("customers", "email UNIQUE", "unique_chosen", {}),
        ("customers", "email NOT NULL", "not_null", {}),
        ("customers", "country CHECK (length = 2)", "check_row", {}),
        ("products", "id PRIMARY KEY (uuid)", "unique_generated", {}),
        ("products", "sku UNIQUE", "unique_chosen", {}),
        ("products", "price CHECK (price > 0)", "check_row", {}),
        ("products", "name NOT NULL", "not_null", {}),
        ("stock", "on_hand >= 0", "lower_bound", {"slack": 900, "use_per_s": 2.5}),
        ("orders", "id PRIMARY KEY (uuid)", "unique_generated", {}),
        ("orders", "customer_id REFERENCES customers (customers are never deleted)", "fk_insert_only", {}),
        ("orders", "status CHECK (in placed, paid, shipped)", "check_row", {}),
        ("orders", "total CHECK (total >= 0)", "check_row", {}),
        ("order_lines", "order_id REFERENCES orders ON DELETE CASCADE", "fk_with_deletes", {}),
        ("order_lines", "product_id REFERENCES products (products are never deleted)", "fk_insert_only", {}),
        ("order_lines", "qty CHECK (qty > 0)", "check_row", {}),
        ("wallets", "balance >= 0", "lower_bound", {"slack": 50_000, "use_per_s": 12}),
        ("coupons", "redemptions <= max_uses", "upper_bound", {"slack": 500, "use_per_s": 0.4}),
        ("coupons", "code UNIQUE", "unique_chosen", {}),
        ("sessions", "token NOT NULL", "not_null", {}),
        ("sessions", "user_id REFERENCES customers", "fk_insert_only", {}),
        ("reviews", "rating CHECK (1..5)", "check_row", {}),
        ("reviews", "UNIQUE (customer_id, product_id)", "unique_chosen", {}),
        ("audit", "at NOT NULL", "not_null", {}),
    ]
    RULE = {
        "not_null": (True, "row-local: any merge of valid rows is valid"),
        "check_row": (True, "row-local: any merge of valid rows is valid"),
        "unique_generated": (True, "uniqueness by construction (uuid or a per-replica id range)"),
        "fk_insert_only": (True, "inserts only: a referenced row never disappears"),
        "unique_chosen": (False, "two replicas can accept the same chosen value: needs a unique index on one node, or a reservation"),
        "fk_with_deletes": (False, "a delete on one replica and an insert on another can orphan a row: needs coordination on delete"),
        "lower_bound": (False, "two replicas can each spend the same slack: needs a lock, or escrow (pre-split slack)"),
        "upper_bound": (False, "two replicas can each grant the last unit: needs a lock, or escrow"),
    }
    rows = []
    for table, c, kind, d in SCHEMA:
        ok, why = RULE[kind]
        r = {"table": table, "constraint": c, "i_confluent": "yes" if ok else "no", "why": why}
        if "slack" in d:                                  # escrow: each replica gets slack / replicas, refilled when spent
            per = d["slack"] / args.replicas
            r["escrow_refills_per_hour"] = d["use_per_s"] / per * 3600
        rows.append(r)
        print(f"{table:<12} {c:<62} {'I-confluent' if ok else 'NEEDS COORDINATION'}"
              + (f"   escrow: {r['escrow_refills_per_hour']:.1f} refills/hour" if "escrow_refills_per_hour" in r else ""))
    need = sum(r["i_confluent"] == "no" for r in rows)
    print(f"\n{need} of {len(rows)} constraints ({need / len(rows):.0%}) need coordination")
    
    if args.json:
        with open(args.json, "w") as f:
            json.dump({"tables": {"constraints": rows, "summary": [{"need": need, "total": len(rows),
                       "need_pct": need / len(rows) * 100}]}}, 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 count-i-confluence-in-your-own.py
    uv run count-i-confluence-in-your-own.py --help     # every knob
Knobs
Knob · defaultWhat it changes
--replicas3 Replicas that accept writes (for the escrow split).

Our run

The example schema, constraint by constraint

The example schema, constraint by constraint
TableConstraintI-confluentEscrow refills / hour
customersid PRIMARY KEY (uuid)yes—
customersemail UNIQUEno—
customersemail NOT NULLyes—
customerscountry CHECK (length = 2)yes—
productsid PRIMARY KEY (uuid)yes—
productssku UNIQUEno—
productsprice CHECK (price > 0)yes—
productsname NOT NULLyes—
stockon_hand >= 0no30
ordersid PRIMARY KEY (uuid)yes—
orderscustomer_id REFERENCES customers (customers are never deleted)yes—
ordersstatus CHECK (in placed, paid, shipped)yes—
orderstotal CHECK (total >= 0)yes—
order_linesorder_id REFERENCES orders ON DELETE CASCADEno—
order_linesproduct_id REFERENCES products (products are never deleted)yes—
order_linesqty CHECK (qty > 0)yes—
walletsbalance >= 0no2.59
couponsredemptions <= max_usesno8.64
couponscode UNIQUEno—
sessionstoken NOT NULLyes—
sessionsuser_id REFERENCES customersyes—
reviewsrating CHECK (1..5)yes—
reviewsUNIQUE (customer_id, product_id)no—
auditat NOT NULLyes—
Reference runApple M2 · 8 cores · 8 GB
Output · uv run count-i-confluence-in-your-own.py
customers    id PRIMARY KEY (uuid)                                          I-confluent
customers    email UNIQUE                                                   NEEDS COORDINATION
customers    email NOT NULL                                                 I-confluent
customers    country CHECK (length = 2)                                     I-confluent
products     id PRIMARY KEY (uuid)                                          I-confluent
products     sku UNIQUE                                                     NEEDS COORDINATION
products     price CHECK (price > 0)                                        I-confluent
products     name NOT NULL                                                  I-confluent
stock        on_hand >= 0                                                   NEEDS COORDINATION   escrow: 30.0 refills/hour
orders       id PRIMARY KEY (uuid)                                          I-confluent
orders       customer_id REFERENCES customers (customers are never deleted) I-confluent
orders       status CHECK (in placed, paid, shipped)                        I-confluent
orders       total CHECK (total >= 0)                                       I-confluent
order_lines  order_id REFERENCES orders ON DELETE CASCADE                   NEEDS COORDINATION
order_lines  product_id REFERENCES products (products are never deleted)    I-confluent
order_lines  qty CHECK (qty > 0)                                            I-confluent
wallets      balance >= 0                                                   NEEDS COORDINATION   escrow: 2.6 refills/hour
coupons      redemptions <= max_uses                                        NEEDS COORDINATION   escrow: 8.6 refills/hour
coupons      code UNIQUE                                                    NEEDS COORDINATION
sessions     token NOT NULL                                                 I-confluent
sessions     user_id REFERENCES customers                                   I-confluent
reviews      rating CHECK (1..5)                                            I-confluent
reviews      UNIQUE (customer_id, product_id)                               NEEDS COORDINATION
audit        at NOT NULL                                                    I-confluent

8 of 24 constraints (33%) need coordination
macOS 26.6.2 · Python 3.13.3 · run 2026-10-20

What we found

In the example schema, 8 of 24 constraints (33.3%) need coordination: the user-chosen unique values (email, SKU, coupon code, one review per customer and product), the cascade-deleted foreign key, and the three bounds. That is more than the chapter's "often under 20%", because this schema was written to include each kind of constraint at least once; a real schema is dominated by NOT NULL and per-row CHECK constraints, which pass. The result is only as good as the list, so the number that matters is the one for your own schema.


Read it

The short list is where the coordination budget goes: one linearizable for usernames at sign-up, escrow for stock and coupons, and a policy for deletes that other rows reference. Everything else can be checked locally, in any region, during a .

I-confluence fails exactly when the invariant is an upper bound on a quantity that transactions push upward, or a uniqueness condition. Escrow turns such a bound into local checks at the price of : units reserved at a replica where nobody is buying.