cockroachdb / cockroachdb/cockroach

sql/opt: elide synthesized UWI check on RBR table when an enforced FK proves the region

Open
#171,702 0 comments 0 reactions 0 assignees View on GitHub
A-multiregion A-sql-optimizer C-enhancement O-agent O-support P-3 T-sql-queries
Dominant language
Go
Stars
32.5k
Forks
4.1k
PR merge metrics
PR metrics pending

Description

**Is your feature request related to a problem? Please describe.**

On a `REGIONAL BY ROW` table, the optimizer synthesizes a `UNIQUE WITHOUT INDEX` constraint for every unique index, using only the user-declared columns (the implicit `crdb_region` is stripped via `ExplicitColumnStartIdx()`). See [`opt_catalog.go:1237-1265`](https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/opt_catalog.go#L1237-L1265).

This synthesized constraint must be enforced on every INSERT by fanning out across all regions, since the user-declared columns alone don't constrain the lookup to one region. In a 3-region cluster this adds ~100-150 ms per unique index per insert (one cross-region RTT per remote region), even when the schema makes the cross-region check provably unnecessary.

The case where the check *is* provably unnecessary: a child RBR table with

```sql
UNIQUE INDEX child_parent_id_idx (parent_id);
FOREIGN KEY (parent_id, crdb_region) REFERENCES parent(id, crdb_region);
```

where `parent.id` is globally unique. The validated FK forces every `(parent_id, crdb_region)` pair in the child to correspond to exactly one `(id, crdb_region)` pair in the parent, and `parent.id` being globally unique means each `parent_id` value is bound to exactly one `crdb_region`. So the global `UNIQUE WITHOUT INDEX (parent_id)` on the child is logically equivalent to `UNIQUE WITHOUT INDEX (parent_id, crdb_region)` — a single-region check.

**Describe the solution you'd like**

At the synthesis point in [`opt_catalog.go:1237-1265`](https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/opt_catalog.go#L1237-L1265), inspect the table's outbound validated FKs. If there's an enforced FK `(child_col1, ..., child_colN, crdb_region) -> parent(parent_col1, ..., parent_colN, crdb_region)` covering all of the child's user-declared unique constraint columns, and the parent's `(parent_col1, ..., parent_colN)` is itself globally unique, then either

- extend the synthesized constraint columns to include `crdb_region` (turning the check into a single-region lookup), or
- set `canElideUniqueCheck: true` for that constraint.

The inference should be performed autonomously by the optimizer from FK structure already in the table descriptor — no opt-in storage parameter or session setting required.

**Describe alternatives you've considered**

1. **Manual `skip_unique_checks` index storage parameter** (v26.2+, [`storageparam/indexstorageparam/index_storage_param.go:225-250`](https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/storageparam/indexstorageparam/index_storage_param.go#L225-L250)) — works today on v26.2 for users who explicitly opt in. Bypasses uniqueness enforcement entirely; the user takes on responsibility for correctness. The autonomous derivation proposed here is strictly safer because the check is still performed when the FK proof doesn't hold, and is just elided/locality-bound when it does.
2. **Schema restructure** — move `crdb_region` into the user-declared PK and prepend to all unique indexes (`PRIMARY KEY (crdb_region, id)`, `UNIQUE INDEX (crdb_region, col)`). Works on all versions but requires a PK swap and weakens the declared global-uniqueness invariant.
3. **Drop the unique constraint** — only viable if uniqueness was incidental.

**Additional context**

Relevant optimizer code paths:

- UWI synthesis for implicitly-partitioned unique indexes: [`opt_catalog.go:1237-1265`](https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/opt_catalog.go#L1237-L1265)
- Per-insert UWI check builder: [`mutation_builder_unique.go:73-83`](https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/opt/optbuilder/mutation_builder_unique.go#L73-L83)
- Manual elision via `skip_unique_checks`: [`storageparam/indexstorageparam/index_storage_param.go:225-250`](https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/storageparam/indexstorageparam/index_storage_param.go#L225-L250)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.