Skip to content

Commit

Permalink
kv: add external testing of parent-child interactions
Browse files Browse the repository at this point in the history
Release note: None
  • Loading branch information
ajwerner committed Feb 1, 2021
1 parent 22759e7 commit 00101e6
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
1 change: 1 addition & 0 deletions pkg/kv/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ go_test(
"//pkg/config/zonepb",
"//pkg/keys",
"//pkg/kv/kvserver",
"//pkg/kv/kvserver/concurrency/lock",
"//pkg/kv/kvserver/kvserverbase",
"//pkg/roachpb",
"//pkg/security",
Expand Down
103 changes: 103 additions & 0 deletions pkg/kv/txn_external_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/base"
"github.com/cockroachdb/cockroach/pkg/kv"
"github.com/cockroachdb/cockroach/pkg/kv/kvserver"
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/lock"
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/testutils/kvclientutils"
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
Expand Down Expand Up @@ -410,3 +411,105 @@ func TestChildTransactionDeadlockDetection(t *testing.T) {
t.Fatalf("unexpected outcome: a=%s, b=%s", strA, strB)
}
}

// TestChildTxnSelfInteractions is an integration-style test of child
// transaction interactions with parent state.
func TestChildTxnSelfInteractions(t *testing.T) {
defer leaktest.AfterTest(t)()
defer log.Scope(t).Close(t)
ctx := context.Background()
type testCase struct {
name string
f func(t *testing.T, db *kv.DB)
}
run := func(test testCase) {
t.Run(test.name, func(t *testing.T) {
tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{})
defer tc.Stopper().Stop(ctx)
db := tc.Server(0).DB()
test.f(t, db)
})
}
testCases := []testCase{
{
"child reads parent's write",
func(t *testing.T, db *kv.DB) {
require.NoError(t, db.Txn(ctx, func(
ctx context.Context, txn *kv.Txn,
) error {
require.NoError(t, txn.Put(ctx, "foo", "bar"))
return txn.ChildTxn(ctx, func(
ctx context.Context, childTxn *kv.Txn,
) error {
got, err := childTxn.Get(ctx, "foo")
require.NoError(t, err)
gotBytes, err := got.Value.GetBytes()
require.NoError(t, err)
require.Equal(t, string(gotBytes), "bar")
return nil
})
}))
},
},
{
"child write-write conflict gets an error",
func(t *testing.T, db *kv.DB) {
require.Regexp(t,
`descendant transaction \w+ attempted to write over ancestor \w+ at key "foo"`,
db.Txn(ctx, func(
ctx context.Context, txn *kv.Txn,
) error {
require.NoError(t, txn.Put(ctx, "foo", "bar"))

// This attempt to write over the parent's intent will fail.
return txn.ChildTxn(ctx, func(
ctx context.Context, childTxn *kv.Txn,
) error {
err := childTxn.Put(ctx, "foo", "baz")
return err
})
}))
},
},
{

// In this case the child should pass through the read lock of the parent,
// write successfully, and then it should push the parent which will then
// be forced to refresh.
"child read-write conflict forces parent to get an error (locking)",
func(t *testing.T, db *kv.DB) {
k := roachpb.Key("foo")
require.NoError(t, db.Put(ctx, k, "bar"))
require.Regexp(t,
`cannot forward provisional commit timestamp due to overlapping write`,
db.Txn(ctx, func(
ctx context.Context, txn *kv.Txn,
) error {
scan := &roachpb.ScanRequest{

KeyLocking: lock.Exclusive,
}
scan.Key = k
scan.EndKey = k.PrefixEnd()
b := txn.NewBatch()
b.AddRawRequest(scan)
require.NoError(t, txn.Run(ctx, b))

// The below write will succeed but the forwarding of the parent above
// the write's timestamp will fail as it detects that the parent's
// read has been invalidated.
return txn.ChildTxn(ctx, func(
ctx context.Context, childTxn *kv.Txn,
) error {
err := childTxn.Put(ctx, k, "baz")
require.NoError(t, err)
return nil
})
}))
},
},
}
for _, tc := range testCases {
run(tc)
}
}

0 comments on commit 00101e6

Please sign in to comment.