Select by workload evidence and disqualifying constraints before preferences or popularity.

Build a Database Selection Matrix from Queries, Invariants, Latency, Scale, Operations, and Skills

Turn AtlasMart workload evidence into a selection matrix that uses hard constraints before weighted preferences, so a high score cannot hide a fatal invariant, recovery, geography, or operations mismatch.

Advanced140–185 minutesSelection-matrix labPython 3.13+ · standard libraryVendor-neutral evidence modelLast reviewed: August 2026
01

Derive selection criteria from concrete queries, commands, invariants, latency, scale, geography, recovery, security, operations, skills, and cost instead of product categories alone.

02

Separate hard disqualifiers from weighted preferences so a fatal transaction, residency, or restore mismatch cannot be averaged away by attractive secondary features.

03

Attach evidence to each candidate score and keep unknowns visible as experiments rather than optimistic assumptions.

04

Produce a repeatable decision record that can be revisited when workload, team, regulation, or provider economics change.

1. Start with AtlasMart decisions, not database brands

AtlasMart now has twenty-four chapters of mechanisms: transaction boundaries, replication, quorums, clocks, partitioning, storage engines, data models, search, consensus, conflict resolution, caching, change streams, security, recovery, performance and managed-service economics. The capstone begins by converting those mechanisms into a decision record. A selection matrix is not a beauty contest between products. It is a structured statement of which workload must be served, which facts must remain correct, what latency and recovery evidence is required, and what the team can operate.

For each command or query, record keys/predicates, cardinality, result size, ordering, frequency, p95/p99 target, consistency need and business invariant. Add data volume/growth, geography/residency, availability objective, Recovery Point Objective (RPO), Recovery Time Objective (RTO), security boundary, backup/restore needs, skill availability and cost range. Only then map candidates to evidence.

2. Hard constraints before weighted preferences

A disqualifying constraint is a requirement whose failure makes a candidate unacceptable even if it is excellent elsewhere. Examples include an order/payment invariant that needs an atomic boundary the proposed design does not supply, a regulated tenant that cannot be placed in the proposed region, or a restore path that cannot meet the tested RTO. Weighted scores are useful only among candidates that survive those gates.

Criterion Evidence Hard constraint? Example question
Invariant/transaction failure history + transaction test Often Can checkout prevent double allocation under retries?
Query/access path representative trace Sometimes Can 99% of reads stay partition-local?
Latency p50/p95/p99 distribution Sometimes Does p99 meet SLO during node loss?
Recovery restore/game-day evidence Often Can an isolated restore meet RPO/RTO?
Operations/skills runbook + staffing evidence Sometimes Can the team repair, upgrade and observe it?
Cost low/base/high TCO Budget-dependent What happens at 2× and 5× traffic?
Evidence rule

Score the requirement you tested, not the product reputation. “Fast,” “scalable,” “serverless,” “strongly consistent,” and “schema-less” are not measurements or complete guarantees.

3. Deliberately wrong approach: let a high total score hide a fatal mismatch

Suppose a low-latency key-value option wins a weighted matrix for the checkout ledger because it scores highly on latency, cost and operations. If the design then decomposes inventory, order and payment facts into independent keys without a correctness strategy for the required invariant, the weighted winner is invalid. The failure is in the decision method: a preference score was allowed to average away a correctness constraint.

The repair is to make the constraint explicit, eliminate or redesign candidates that cannot satisfy it, and preserve unresolved questions as experiments. A candidate can re-enter only when a concrete design changes the guarantee—for example, by moving the invariant into one atomic aggregate or selecting a transaction-capable boundary.

4. AtlasMart lab: constraint-first selection

Mandatory lab environment

Python 3.13+ standard library only. Candidate names are database classes, not product recommendations; scores are synthetic teaching inputs.

python · AtlasMart deterministic simulation
from dataclasses import dataclass

@dataclass(frozen=True)
class Candidate:
    name: str
    scores: dict
    guarantees: set

criteria = {
    "latency": 5,
    "operational_fit": 4,
    "query_fit": 5,
    "recovery": 4,
    "team_skill": 3,
    "cost": 2,
}

candidates = [
    Candidate("relational", {"latency":4,"operational_fit":5,"query_fit":5,"recovery":5,"team_skill":5,"cost":4}, {"multi_row_tx","joins","unique_constraints"}),
    Candidate("document",   {"latency":5,"operational_fit":4,"query_fit":4,"recovery":4,"team_skill":4,"cost":4}, {"single_document_atomic"}),
    Candidate("key_value",  {"latency":5,"operational_fit":5,"query_fit":2,"recovery":3,"team_skill":4,"cost":5}, {"single_key_atomic"}),
    Candidate("wide_column",{"latency":5,"operational_fit":3,"query_fit":3,"recovery":3,"team_skill":2,"cost":4}, {"partition_local_atomic"}),
]

def weighted(c):
    return sum(criteria[k] * c.scores[k] for k in criteria)

workloads = {
    "checkout_ledger": {
        "required": {"multi_row_tx","unique_constraints"},
        "bonus": {"relational":12,"document":0,"key_value":0,"wide_column":0},
        "notes": "inventory reservation + payment/order state must not split silently",
    },
    "catalog_page": {
        "required": set(),
        "bonus": {"relational":0,"document":18,"key_value":2,"wide_column":0},
        "notes": "aggregate-oriented reads; eventual search projection acceptable",
    },
}

print("NAIVE WEIGHTED RANKING")
for c in sorted(candidates, key=weighted, reverse=True):
    print(c.name, weighted(c))

print("\nCONSTRAINT-FIRST SELECTION")
for workload, req in workloads.items():
    print("workload=", workload, "--", req["notes"])
    survivors=[]
    for c in candidates:
        missing=req["required"] - c.guarantees
        effective=weighted(c)+req["bonus"].get(c.name,0)
        print(f"  {c.name:12} base={weighted(c):3} workload_bonus={req["bonus"].get(c.name,0):2} effective={effective:3} missing={sorted(missing)}")
        if not missing:
            survivors.append((effective,c))
    winner=max(survivors, key=lambda x:x[0])[1] if survivors else None
    print("  winner after disqualifiers:", winner.name if winner else "NONE")

print("\nUNKNOWN EVIDENCE REGISTER")
unknowns=[
    ("catalog_page","document","p99 under 10x catalog growth"),
    ("checkout_ledger","relational","restore test under regional failover"),
]
for row in unknowns:
    print("experiment required:", row)
Expected evidence

The program first prints a naive weighted ranking, then applies required guarantees per workload. For checkout, candidates lacking the stated multi-row transaction/unique-constraint requirements are visibly disqualified. It also leaves p99 and restore behavior as experiments instead of silently scoring unknowns.

5. Production judgment

Keep one matrix per bounded workload, not one giant score for the company. An orders ledger, search projection, fraud graph and telemetry feed have different invariants and query shapes. The answer may be one database for several workloads or several databases with explicit ownership; both are acceptable if the evidence supports them.

Version the matrix alongside architecture decisions. Re-run critical experiments after major engine upgrades, topology changes, new regions, regulatory changes, traffic shifts or team turnover. The next lesson applies the same discipline to a common failure of judgment: adopting NoSQL where a relational database already gives the simplest invariant and query boundary.

Check your understanding

  1. Why should a hard constraint be evaluated before a weighted score?
  2. What belongs in the evidence column of a selection matrix?
  3. Why is “supports transactions” too vague for selection?
  4. When should a candidate remain “unknown” rather than receive a hopeful score?
  5. What makes the matrix maintainable?
Review the answers

1. Because a high aggregate score cannot compensate for a fatal mismatch such as an invariant the candidate cannot preserve, forbidden data placement, or an unachievable recovery objective.

2. Workload traces, benchmark distributions, restore tests, failure drills, documentation for exact semantics, operational skill evidence, cost ranges, and unresolved experiments.

3. Transaction scope, isolation, cross-partition behavior, latency, failure semantics, and limits differ; the invariant must be mapped to the exact guarantee.

4. When the team lacks reproducible evidence for a material requirement such as tail latency, restore time, global-write semantics, or operational ownership.

5. Explicit assumptions, dated evidence, disqualifiers, weights, experiment links, and a trigger for reassessment when workload or constraints change.

References

Foundational and current implementation references used for this lesson:

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.