Files
dolthub--dolt/go/store/prolly/map_diff_key_range_test.go
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

352 lines
10 KiB
Go

// Copyright 2022 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package prolly
import (
"context"
"errors"
"fmt"
"io"
"math/rand"
"sort"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dolthub/dolt/go/store/prolly/tree"
"github.com/dolthub/dolt/go/store/val"
)
func TestDiffKeyRangeMaps(t *testing.T) {
scales := []int{
10,
100,
1000,
10000,
}
for _, s := range scales {
name := fmt.Sprintf("test map RangeDiff at scale %d", s)
t.Run(name, func(t *testing.T) {
om, tuples := makeProllyMap(t, s)
require.Equal(t, s, len(tuples))
prollyMap := om.(Map)
kd, vd := prollyMap.Descriptors()
t.Run("BoundedKeyRange", func(t *testing.T) {
rngTest := makeRandomBoundedKeyRange(kd, tuples)
runDiffTestsWithKeyRange(t, s, prollyMap, tuples, rngTest)
})
t.Run("BoundedKeyRangeWithMissingKeys", func(t *testing.T) {
rngTest := makeBoundedKeyRangeWithMissingKeys(t, prollyMap, kd, vd, tuples)
runDiffTestsWithKeyRange(t, s, prollyMap, tuples, rngTest)
})
t.Run("UnboundedLowerKeyRange", func(t *testing.T) {
rngTest := makeRandomUnboundedLowerKeyRange(kd, tuples)
runDiffTestsWithKeyRange(t, s, prollyMap, tuples, rngTest)
})
t.Run("UnboundedUpperKeyRange", func(t *testing.T) {
rngTest := makeRandomUnboundedUpperKeyRange(kd, tuples)
runDiffTestsWithKeyRange(t, s, prollyMap, tuples, rngTest)
})
})
}
}
func runDiffTestsWithKeyRange(t *testing.T, s int, prollyMap testMap, tuples [][2]val.Tuple, rngTest keyRangeDiffTest) {
t.Run("map diff error handling", func(t *testing.T) {
testKeyRngMapDiffErrorHandling(t, prollyMap.(Map), rngTest)
})
t.Run("equal map diff", func(t *testing.T) {
testKeyRngEqualMapDiff(t, prollyMap.(Map), rngTest)
})
t.Run("map diff against empty", func(t *testing.T) {
testKeyRngMapDiffAgainstEmpty(t, s, rngTest)
})
// deletes
t.Run("single delete diff", func(t *testing.T) {
for k := 0; k < 100; k++ {
testKeyRngDeleteDiffs(t, prollyMap.(Map), tuples, 1, rngTest)
}
})
t.Run("many delete diffs", func(t *testing.T) {
for k := 0; k < 10; k++ {
testKeyRngDeleteDiffs(t, prollyMap.(Map), tuples, s/10, rngTest)
testKeyRngDeleteDiffs(t, prollyMap.(Map), tuples, s/2, rngTest)
}
})
t.Run("diff against empty map", func(t *testing.T) {
testKeyRngDeleteDiffs(t, prollyMap.(Map), tuples, s, rngTest)
})
// inserts
t.Run("single insert diff", func(t *testing.T) {
for k := 0; k < 100; k++ {
testKeyRngInsertDiffs(t, prollyMap.(Map), tuples, 1, rngTest)
}
})
t.Run("many insert diffs", func(t *testing.T) {
for k := 0; k < 10; k++ {
testKeyRngInsertDiffs(t, prollyMap.(Map), tuples, s/10, rngTest)
testKeyRngInsertDiffs(t, prollyMap.(Map), tuples, s/2, rngTest)
}
})
// updates
t.Run("single update diff", func(t *testing.T) {
for k := 0; k < 100; k++ {
testKeyRngUpdateDiffs(t, prollyMap.(Map), tuples, 1, rngTest)
}
})
t.Run("many update diffs", func(t *testing.T) {
for k := 0; k < 10; k++ {
testKeyRngUpdateDiffs(t, prollyMap.(Map), tuples, s/10, rngTest)
testKeyRngUpdateDiffs(t, prollyMap.(Map), tuples, s/2, rngTest)
}
})
}
func testKeyRngMapDiffErrorHandling(t *testing.T, m Map, test keyRangeDiffTest) {
ctx := context.Background()
expErr := errors.New("error case")
err := DiffMapsKeyRange(ctx, m, m, test.keyRange.start, test.keyRange.stop, func(ctx context.Context, diff tree.Diff) error {
return expErr
})
require.Error(t, expErr, err)
}
func testKeyRngEqualMapDiff(t *testing.T, m Map, rngTest keyRangeDiffTest) {
ctx := context.Background()
var counter int
err := DiffMapsKeyRange(ctx, m, m, rngTest.keyRange.start, rngTest.keyRange.stop, func(ctx context.Context, diff tree.Diff) error {
counter++
return nil
})
require.Error(t, io.EOF, err)
assert.Equal(t, 0, counter)
}
func testKeyRngMapDiffAgainstEmpty(t *testing.T, scale int, rngTest keyRangeDiffTest) {
ctx := context.Background()
m, tuples := makeProllyMap(t, scale)
empty, _ := makeProllyMap(t, 0)
inRange := getPairsInKeyRange(tuples, rngTest.keyRange)
cnt := 0
err := DiffMapsKeyRange(ctx, m.(Map), empty.(Map), rngTest.keyRange.start, rngTest.keyRange.stop, func(ctx context.Context, diff tree.Diff) error {
assert.Equal(t, inRange[cnt][0], val.Tuple(diff.Key))
assert.Equal(t, inRange[cnt][1], val.Tuple(diff.From))
assert.Nil(t, val.Tuple(diff.To))
assert.True(t, rngTest.keyRange.includes(val.Tuple(diff.Key)))
cnt++
return nil
})
require.Error(t, io.EOF, err)
assert.Equal(t, len(inRange), cnt)
cnt = 0
err = DiffMapsKeyRange(ctx, empty.(Map), m.(Map), rngTest.keyRange.start, rngTest.keyRange.stop, func(ctx context.Context, diff tree.Diff) error {
assert.Equal(t, inRange[cnt][0], val.Tuple(diff.Key))
assert.Equal(t, inRange[cnt][1], val.Tuple(diff.To))
assert.Nil(t, val.Tuple(diff.From))
assert.True(t, rngTest.keyRange.includes(val.Tuple(diff.Key)))
cnt++
return nil
})
require.Error(t, io.EOF, err)
assert.Equal(t, len(inRange), cnt)
}
func testKeyRngDeleteDiffs(t *testing.T, from Map, tups [][2]val.Tuple, numDeletes int, rngTest keyRangeDiffTest) {
ctx := context.Background()
rand.Shuffle(len(tups), func(i, j int) {
tups[i], tups[j] = tups[j], tups[i]
})
deletes := tups[:numDeletes]
sort.Slice(deletes, func(i, j int) bool {
cmp, err := from.keyDesc.Compare(ctx, deletes[i][0], deletes[j][0])
require.NoError(t, err)
return cmp < 0
})
inRange := getPairsInKeyRange(deletes, rngTest.keyRange)
to := makeMapWithDeletes(t, from, deletes...)
cnt := 0
err := DiffMapsKeyRange(ctx, from, to, rngTest.keyRange.start, rngTest.keyRange.stop, func(ctx context.Context, diff tree.Diff) error {
assert.Equal(t, tree.RemovedDiff, diff.Type)
assert.Equal(t, inRange[cnt][0], val.Tuple(diff.Key))
assert.True(t, rngTest.keyRange.includes(val.Tuple(diff.Key)))
cnt++
return nil
})
require.Error(t, io.EOF, err)
assert.Equal(t, len(inRange), cnt)
}
func testKeyRngInsertDiffs(t *testing.T, from Map, tups [][2]val.Tuple, numInserts int, rngTest keyRangeDiffTest) {
ctx := context.Background()
to, inserts := makeMapWithInserts(t, from, numInserts)
inRange := getPairsInKeyRange(inserts, rngTest.keyRange)
cnt := 0
err := DiffMapsKeyRange(ctx, from, to, rngTest.keyRange.start, rngTest.keyRange.stop, func(ctx context.Context, diff tree.Diff) error {
if !assert.Equal(t, tree.AddedDiff, diff.Type) {
fmt.Println("")
}
assert.Equal(t, inRange[cnt][0], val.Tuple(diff.Key))
assert.Equal(t, inRange[cnt][1], val.Tuple(diff.To))
assert.True(t, rngTest.keyRange.includes(val.Tuple(diff.Key)))
cnt++
return nil
})
require.Error(t, io.EOF, err)
assert.Equal(t, len(inRange), cnt)
}
func testKeyRngUpdateDiffs(t *testing.T, from Map, tups [][2]val.Tuple, numUpdates int, rngTest keyRangeDiffTest) {
ctx := context.Background()
rand.Shuffle(len(tups), func(i, j int) {
tups[i], tups[j] = tups[j], tups[i]
})
sub := tups[:numUpdates]
sort.Slice(sub, func(i, j int) bool {
cmp, err := from.keyDesc.Compare(ctx, sub[i][0], sub[j][0])
require.NoError(t, err)
return cmp < 0
})
kd, vd := from.Descriptors()
updates := makeUpdatesToTuples(kd, vd, sub...)
to := makeMapWithUpdates(t, from, updates...)
var inRange [][3]val.Tuple
for _, pair := range updates {
if rngTest.keyRange.includes(pair[0]) {
inRange = append(inRange, pair)
}
}
var cnt int
err := DiffMapsKeyRange(ctx, from, to, rngTest.keyRange.start, rngTest.keyRange.stop, func(ctx context.Context, diff tree.Diff) error {
assert.Equal(t, tree.ModifiedDiff, diff.Type)
assert.Equal(t, inRange[cnt][0], val.Tuple(diff.Key))
assert.Equal(t, inRange[cnt][1], val.Tuple(diff.From))
assert.Equal(t, inRange[cnt][2], val.Tuple(diff.To))
assert.True(t, rngTest.keyRange.includes(val.Tuple(diff.Key)))
cnt++
return nil
})
require.Error(t, io.EOF, err)
assert.Equal(t, len(inRange), cnt)
}
func makeRandomBoundedKeyRange(kd *val.TupleDesc, tuples [][2]val.Tuple) keyRangeDiffTest {
i := rand.Intn(len(tuples))
j := rand.Intn(len(tuples))
if j < i {
i, j = j, i
}
start := tuples[i][0]
stop := tuples[j][0]
kR := keyRange{kd: kd, start: start, stop: stop}
return keyRangeDiffTest{tuples: tuples, keyRange: kR}
}
func makeRandomUnboundedLowerKeyRange(kd *val.TupleDesc, tuples [][2]val.Tuple) keyRangeDiffTest {
i := rand.Intn(len(tuples))
end := tuples[i][0]
kR := keyRange{kd: kd, stop: end}
return keyRangeDiffTest{tuples: tuples, keyRange: kR}
}
func makeRandomUnboundedUpperKeyRange(kd *val.TupleDesc, tuples [][2]val.Tuple) keyRangeDiffTest {
i := rand.Intn(len(tuples))
start := tuples[i][0]
kR := keyRange{kd: kd, start: start}
return keyRangeDiffTest{tuples: tuples, keyRange: kR}
}
func makeBoundedKeyRangeWithMissingKeys(t *testing.T, m Map, kd *val.TupleDesc, vd *val.TupleDesc, tuples [][2]val.Tuple) keyRangeDiffTest {
ctx := context.Background()
inserts := generateInserts(t, m, kd, vd, 2)
low, hi := inserts[0][0], inserts[1][0]
cmp, err := kd.Compare(ctx, low, hi)
require.NoError(t, err)
if cmp > 0 {
hi, low = low, hi
}
kR := keyRange{kd: kd, start: low, stop: hi}
return keyRangeDiffTest{tuples: tuples, keyRange: kR}
}
func getPairsInKeyRange(tuples [][2]val.Tuple, rng keyRange) (keys [][2]val.Tuple) {
for _, pair := range tuples {
if rng.includes(pair[0]) {
keys = append(keys, pair)
}
}
return
}
type keyRange struct {
start val.Tuple
stop val.Tuple
kd *val.TupleDesc
}
func (kR keyRange) includes(k val.Tuple) bool {
ctx := context.Background()
if len(kR.start) != 0 {
cmp, err := kR.kd.Compare(ctx, k, kR.start)
if err != nil {
panic(err)
}
if cmp < 0 {
return false
}
}
if len(kR.stop) != 0 {
cmp, err := kR.kd.Compare(ctx, k, kR.stop)
if err != nil {
panic(err)
}
if cmp >= 0 {
return false
}
}
return true
}
type keyRangeDiffTest struct {
tuples [][2]val.Tuple
keyRange keyRange
}