Open michae2 opened 12 months ago
Added docs-known-limitation because we do not support certain RBR tables under RC in 23.2, due to lack of support for explicit unique checks.
Added this to KV because it depends on predicate locking.
Also added to Queries because there's some optbuilder work needed to use predicate locking once available.
[read committed weekly meeting] We think we could implement this without predicate locking by using CPut to write a tombstone in regions that don't contain the row.
Here's a demonstration of the problem. With this diff applied:
diff --git a/pkg/sql/opt/exec/execbuilder/relational.go b/pkg/sql/opt/exec/execbuilder/relational.go
index e6ecfde3c12..01a6750ef2d 100644
--- a/pkg/sql/opt/exec/execbuilder/relational.go
+++ b/pkg/sql/opt/exec/execbuilder/relational.go
@@ -3105,11 +3105,13 @@ func (b *Builder) buildLocking(toLock opt.TableID, locking opt.Locking) (opt.Loc
"cannot execute SELECT %s in a read-only transaction", locking.Strength.String(),
)
}
- if locking.Form == tree.LockPredicate {
- return opt.Locking{}, unimplemented.NewWithIssuef(
- 110873, "explicit unique checks are not yet supported under read committed isolation",
- )
- }
+ /*
+ if locking.Form == tree.LockPredicate {
+ return opt.Locking{}, unimplemented.NewWithIssuef(
+ 110873, "explicit unique checks are not yet supported under read committed isolation",
+ )
+ }
+ */
// Check if we can actually use shared locks here, or we need to use
// non-locking reads instead.
if locking.Strength == tree.ForShare || locking.Strength == tree.ForKeyShare {
diff --git a/pkg/sql/upsert.go b/pkg/sql/upsert.go
index 56d270b9ca5..3d03d6cd5eb 100644
--- a/pkg/sql/upsert.go
+++ b/pkg/sql/upsert.go
@@ -13,6 +13,7 @@ package sql
import (
"context"
"sync"
+ "time"
"github.com/cockroachdb/cockroach/pkg/sql/catalog"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo"
@@ -180,6 +181,10 @@ func (n *upsertNode) processSourceRow(params runParams, rowVals tree.Datums) err
rowVals = rowVals[:ord]
}
+ if n.run.tw.tableWriterBase.desc.GetName() == "xy" {
+ time.Sleep(5 * time.Second)
+ }
+
// Process the row. This is also where the tableWriter will accumulate
// the row for later.
return n.run.tw.row(params.ctx, rowVals, pm, n.run.traceKV)
Start a multi-region demo cluster:
./cockroach demo --multitenant=false --nodes=6 --global
Create a regional by row table named "xy":
ALTER DATABASE defaultdb PRIMARY REGION "us-east1";
ALTER DATABASE defaultdb ADD REGION "us-west1";
CREATE TABLE xy (x INT PRIMARY KEY, y INT) LOCALITY REGIONAL BY ROW;
Connect to the cluster from two different regions. In both regions, issue simultaneous UPSERTs to table xy under read committed isolation:
-- from us-east1
SET default_transaction_isolation = 'READ COMMITTED';
UPSERT INTO xy VALUES (1, 1);
-- from us-west1
SET default_transaction_isolation = 'READ COMMITTED';
UPSERT INTO xy VALUES (1, 2);
Now there should be two rows with x=1:
demo@127.0.0.1:26257/defaultdb> SELECT * FROM xy;
x | y
----+----
1 | 2
1 | 1
(2 rows)
Breaking this into multiple parts for added visibility. This is about INSERT & UPDATE. I'll open a new issue to track UPSERT and ON CONFLICT, both of which make use of arbiter indexes.
In CockroachDB, most unique constraints are enforced by an accompanying unique index. The uniqueness check which maintains the constraint is simply the use of
ConditionalPut
instead ofPut
when writing a row to that index.However, for unique constraints without an accompanying unique index, the uniqueness check is an explicit search in the target table for any potential conflicting rows. Under read committed isolation this search must use predicate locking, which is not yet implemented.
Consequently, any
INSERT
,UPDATE
, orUPSERT
that needs to perform an explicit unique check is not yet support under read committed isolation.This affects inserts, updates, or upserts into:
UNIQUE WITHOUT INDEX
constraints (an experimental feature).UNIQUE
andPRIMARY KEY
constraints on tables usingPARTITION ALL BY
(an experimental feature).UNIQUE
andPRIMARY KEY
constraints on tables usingLOCALITY REGIONAL BY ROW
, when (a) the region is not included in the constraint and (b) cannot be computed from the constraint columns.The last case is the most common and the most complex, so here are some examples with and without explicit unique checks:
Jira issue: CRDB-31651
Epic CRDB-38938