Skip to content

Commit 343c0ef

Browse files
committed
Merge branch 'main' of github.com:PeerDB-io/peerdb into fix/mysql-snapshot-invisible-columns
2 parents 63565d1 + cf2c6dc commit 343c0ef

5 files changed

Lines changed: 167 additions & 79 deletions

File tree

flow/connectors/mongo/qrep_partition.go

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ func (c *MongoConnector) numericPartitions(
139139
return utils.FullTablePartition(), nil
140140
}
141141

142-
ranges := computeNumericRanges(minVal, maxVal, numPartitions)
142+
ranges := utils.ComputeRanges(minVal, maxVal, numPartitions)
143143
c.logger.Info("[mongo] using numeric _id partitioning", slog.Int("numPartitions", len(ranges)))
144144
partitions := make([]*protos.QRepPartition, 0, len(ranges))
145145
for i, rng := range ranges {
@@ -148,27 +148,6 @@ func (c *MongoConnector) numericPartitions(
148148
return partitions, nil
149149
}
150150

151-
func computeNumericRanges(minVal int64, maxVal int64, numPartitions int64) [][2]int64 {
152-
// span arithmetic is done in uint64 to avoid overflow
153-
span := uint64(maxVal) - uint64(minVal)
154-
step := span / uint64(numPartitions)
155-
if span%uint64(numPartitions) != 0 {
156-
step++
157-
}
158-
159-
ranges := make([][2]int64, 0, numPartitions)
160-
start := minVal
161-
for {
162-
if remaining := uint64(maxVal) - uint64(start); remaining <= step {
163-
ranges = append(ranges, [2]int64{start, maxVal})
164-
return ranges
165-
}
166-
end := int64(uint64(start) + step)
167-
ranges = append(ranges, [2]int64{start, end})
168-
start = end
169-
}
170-
}
171-
172151
// stringPartitions builds partitions for a string _id collection. Because string
173152
// keys (e.g. package names) are not uniformly distributed, uniform division of the
174153
// keyspace would be badly skewed; instead we sample the collection and pick quantile

flow/connectors/mongo/qrep_partition_test.go

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package connmongo
22

33
import (
4-
"math"
54
"testing"
65

76
"github.com/stretchr/testify/require"
@@ -119,51 +118,6 @@ func TestComputeStringBoundaries(t *testing.T) {
119118
})
120119
}
121120

122-
func assertContiguousNumeric(t *testing.T, ranges [][2]int64, minVal, maxVal int64) {
123-
t.Helper()
124-
require.NotEmpty(t, ranges)
125-
require.Equal(t, minVal, ranges[0][0])
126-
require.Equal(t, maxVal, ranges[len(ranges)-1][1])
127-
for i := range ranges {
128-
require.Less(t, ranges[i][0], ranges[i][1])
129-
if i+1 < len(ranges) {
130-
require.Equal(t, ranges[i][1], ranges[i+1][0])
131-
}
132-
}
133-
}
134-
135-
func TestComputeNumericRanges(t *testing.T) {
136-
t.Run("even division", func(t *testing.T) {
137-
ranges := computeNumericRanges(1, 100, 10)
138-
require.Len(t, ranges, 10)
139-
assertContiguousNumeric(t, ranges, 1, 100)
140-
})
141-
142-
t.Run("span smaller than partition count", func(t *testing.T) {
143-
ranges := computeNumericRanges(0, 5, 10)
144-
require.Len(t, ranges, 5)
145-
assertContiguousNumeric(t, ranges, 0, 5)
146-
})
147-
148-
t.Run("two adjacent values", func(t *testing.T) {
149-
ranges := computeNumericRanges(7, 8, 4)
150-
require.Len(t, ranges, 1)
151-
assertContiguousNumeric(t, ranges, 7, 8)
152-
})
153-
154-
t.Run("span does not overflow", func(t *testing.T) {
155-
ranges := computeNumericRanges(math.MinInt64+5, math.MaxInt64-5, 10)
156-
require.Len(t, ranges, 10)
157-
assertContiguousNumeric(t, ranges, math.MinInt64+5, math.MaxInt64-5)
158-
})
159-
160-
t.Run("full int64 domain does not overflow", func(t *testing.T) {
161-
ranges := computeNumericRanges(math.MinInt64, math.MaxInt64, 7)
162-
require.Len(t, ranges, 7)
163-
assertContiguousNumeric(t, ranges, math.MinInt64, math.MaxInt64)
164-
})
165-
}
166-
167121
func TestToRangeFilter(t *testing.T) {
168122
t.Run("numeric range is half-open", func(t *testing.T) {
169123
filter, err := toRangeFilter(DefaultDocumentKeyColumnName, utils.CreateNumericPartition(10, 20, false).Range)

flow/connectors/utils/partition.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"google.golang.org/protobuf/types/known/timestamppb"
1313

1414
"github.com/PeerDB-io/peerdb/flow/generated/protos"
15-
"github.com/PeerDB-io/peerdb/flow/shared"
1615
)
1716

1817
const FullTablePartitionID = "full-table-partition-id"
@@ -314,32 +313,50 @@ func (p *PartitionHelper) AddPartitionsWithRange(start any, end any, numPartitio
314313

315314
switch r := partition.Range.Range.(type) {
316315
case *protos.PartitionRange_IntRange:
317-
size := shared.DivCeil(r.IntRange.End-r.IntRange.Start, numPartitions)
318-
for i := range numPartitions {
319-
if err := p.AddPartition(r.IntRange.Start+size*i, min(r.IntRange.Start+size*(i+1), r.IntRange.End)); err != nil {
316+
for _, rng := range ComputeRanges(r.IntRange.Start, r.IntRange.End, numPartitions) {
317+
if err := p.AddPartition(rng[0], rng[1]); err != nil {
320318
return err
321319
}
322320
}
323321
case *protos.PartitionRange_UintRange:
324-
size := shared.DivCeil(r.UintRange.End-r.UintRange.Start, uint64(numPartitions))
325-
for i := range uint64(numPartitions) {
326-
if err := p.AddPartition(r.UintRange.Start+size*i, min(r.UintRange.Start+size*(i+1), r.UintRange.End)); err != nil {
322+
for _, rng := range ComputeRanges(r.UintRange.Start, r.UintRange.End, numPartitions) {
323+
if err := p.AddPartition(rng[0], rng[1]); err != nil {
327324
return err
328325
}
329326
}
330327
case *protos.PartitionRange_TimestampRange:
331328
tstart := r.TimestampRange.Start.AsTime().UnixMicro()
332329
tend := r.TimestampRange.End.AsTime().UnixMicro()
333-
size := shared.DivCeil(tend-tstart, numPartitions)
334-
for i := range numPartitions {
335-
if err := p.AddPartition(time.UnixMicro(tstart+size*i), time.UnixMicro(min(tstart+size*(i+1), tend))); err != nil {
330+
for _, rng := range ComputeRanges(tstart, tend, numPartitions) {
331+
if err := p.AddPartition(time.UnixMicro(rng[0]), time.UnixMicro(rng[1])); err != nil {
336332
return err
337333
}
338334
}
339335
}
340336
return nil
341337
}
342338

339+
func ComputeRanges[T int64 | uint64](minVal T, maxVal T, numPartitions int64) [][2]T {
340+
// span arithmetic is done in uint64 to avoid overflow
341+
span := uint64(maxVal) - uint64(minVal)
342+
step := span / uint64(numPartitions)
343+
if span%uint64(numPartitions) != 0 {
344+
step++
345+
}
346+
347+
ranges := make([][2]T, 0, numPartitions)
348+
start := minVal
349+
for {
350+
if remaining := uint64(maxVal) - uint64(start); remaining <= step {
351+
ranges = append(ranges, [2]T{start, maxVal})
352+
return ranges
353+
}
354+
end := T(uint64(start) + step)
355+
ranges = append(ranges, [2]T{start, end})
356+
start = end
357+
}
358+
}
359+
343360
func (p *PartitionHelper) getPartitionForStartAndEnd(start any, end any) (*protos.QRepPartition, error) {
344361
if start == nil || end == nil {
345362
return nil, nil
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package utils
2+
3+
import (
4+
"log/slog"
5+
"math"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
"go.temporal.io/sdk/log"
10+
11+
"github.com/PeerDB-io/peerdb/flow/generated/protos"
12+
)
13+
14+
func intRangeOf(t *testing.T, partition *protos.QRepPartition) *protos.IntPartitionRange {
15+
t.Helper()
16+
r, ok := partition.Range.Range.(*protos.PartitionRange_IntRange)
17+
require.True(t, ok)
18+
return r.IntRange
19+
}
20+
21+
func uintRangeOf(t *testing.T, partition *protos.QRepPartition) *protos.UIntPartitionRange {
22+
t.Helper()
23+
r, ok := partition.Range.Range.(*protos.PartitionRange_UintRange)
24+
require.True(t, ok)
25+
return r.UintRange
26+
}
27+
28+
func assertContiguousRanges[T int64 | uint64](t *testing.T, ranges [][2]T, minVal, maxVal T) {
29+
t.Helper()
30+
require.NotEmpty(t, ranges)
31+
require.Equal(t, minVal, ranges[0][0])
32+
require.Equal(t, maxVal, ranges[len(ranges)-1][1])
33+
for i := range ranges {
34+
require.Less(t, ranges[i][0], ranges[i][1])
35+
if i+1 < len(ranges) {
36+
require.Equal(t, ranges[i][1], ranges[i+1][0])
37+
}
38+
}
39+
}
40+
41+
func TestComputeRanges(t *testing.T) {
42+
t.Run("even division", func(t *testing.T) {
43+
ranges := ComputeRanges[int64](1, 100, 10)
44+
require.Len(t, ranges, 10)
45+
assertContiguousRanges(t, ranges, int64(1), int64(100))
46+
})
47+
48+
t.Run("span smaller than partition count", func(t *testing.T) {
49+
ranges := ComputeRanges[int64](0, 5, 10)
50+
require.Len(t, ranges, 5)
51+
assertContiguousRanges(t, ranges, int64(0), int64(5))
52+
})
53+
54+
t.Run("two adjacent values", func(t *testing.T) {
55+
ranges := ComputeRanges[int64](7, 8, 4)
56+
require.Len(t, ranges, 1)
57+
assertContiguousRanges(t, ranges, int64(7), int64(8))
58+
})
59+
60+
t.Run("full int64 domain does not overflow", func(t *testing.T) {
61+
ranges := ComputeRanges[int64](math.MinInt64, math.MaxInt64, 7)
62+
require.Len(t, ranges, 7)
63+
assertContiguousRanges(t, ranges, int64(math.MinInt64), int64(math.MaxInt64))
64+
})
65+
66+
t.Run("full uint64 domain does not overflow", func(t *testing.T) {
67+
ranges := ComputeRanges[uint64](0, math.MaxUint64, 7)
68+
require.Len(t, ranges, 7)
69+
assertContiguousRanges(t, ranges, uint64(0), uint64(math.MaxUint64))
70+
})
71+
72+
t.Run("same min/max values", func(t *testing.T) {
73+
ranges := ComputeRanges[int64](7, 7, 4)
74+
require.Len(t, ranges, 1)
75+
require.Equal(t, int64(7), ranges[0][0])
76+
require.Equal(t, int64(7), ranges[len(ranges)-1][1])
77+
})
78+
}
79+
80+
func TestAddPartitionsWithRange(t *testing.T) {
81+
logger := log.NewStructuredLogger(slog.Default())
82+
83+
t.Run("negative to positive int range covers domain contiguously", func(t *testing.T) {
84+
helper := NewPartitionHelper(logger)
85+
require.NoError(t, helper.AddPartitionsWithRange(int64(-100), int64(100), 4))
86+
87+
partitions := helper.GetPartitions()
88+
require.Len(t, partitions, 4)
89+
require.Equal(t, int64(-100), intRangeOf(t, partitions[0]).Start)
90+
require.Equal(t, int64(100), intRangeOf(t, partitions[len(partitions)-1]).End)
91+
for i := 1; i < len(partitions); i++ {
92+
require.Equal(t, intRangeOf(t, partitions[i-1]).End+1, intRangeOf(t, partitions[i]).Start)
93+
}
94+
})
95+
96+
t.Run("int range spanning full int64 domain does not overflow", func(t *testing.T) {
97+
helper := NewPartitionHelper(logger)
98+
require.NoError(t, helper.AddPartitionsWithRange(int64(math.MinInt64), int64(math.MaxInt64), 7))
99+
100+
partitions := helper.GetPartitions()
101+
require.Len(t, partitions, 7)
102+
require.Equal(t, int64(math.MinInt64), intRangeOf(t, partitions[0]).Start)
103+
require.Equal(t, int64(math.MaxInt64), intRangeOf(t, partitions[len(partitions)-1]).End)
104+
for i, partition := range partitions {
105+
r := intRangeOf(t, partition)
106+
require.LessOrEqual(t, r.Start, r.End)
107+
if i > 0 {
108+
require.Equal(t, intRangeOf(t, partitions[i-1]).End+1, r.Start)
109+
}
110+
}
111+
})
112+
113+
t.Run("uint range spanning full uint64 domain does not overflow", func(t *testing.T) {
114+
helper := NewPartitionHelper(logger)
115+
require.NoError(t, helper.AddPartitionsWithRange(uint64(0), uint64(math.MaxUint64), 7))
116+
117+
partitions := helper.GetPartitions()
118+
require.Len(t, partitions, 7)
119+
require.Equal(t, uint64(0), uintRangeOf(t, partitions[0]).Start)
120+
require.Equal(t, uint64(math.MaxUint64), uintRangeOf(t, partitions[len(partitions)-1]).End)
121+
for i, partition := range partitions {
122+
r := uintRangeOf(t, partition)
123+
require.LessOrEqual(t, r.Start, r.End)
124+
if i > 0 {
125+
require.Equal(t, uintRangeOf(t, partitions[i-1]).End+1, r.Start)
126+
}
127+
}
128+
})
129+
130+
t.Run("span smaller than partition count yields fewer partitions", func(t *testing.T) {
131+
helper := NewPartitionHelper(logger)
132+
require.NoError(t, helper.AddPartitionsWithRange(int64(0), int64(5), 10))
133+
134+
partitions := helper.GetPartitions()
135+
require.Len(t, partitions, 5)
136+
require.Equal(t, int64(5), intRangeOf(t, partitions[len(partitions)-1]).End)
137+
})
138+
}

flow/e2e/congen.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func CreatePeer(t *testing.T, peer *protos.Peer) {
4141
t.Helper()
4242
pool, err := catalogTestAccessPool()
4343
require.NoError(t, err)
44-
res, err := utils.CreatePeerNoValidate(t.Context(), shared.CatalogPool{Pool: pool}, peer, false)
44+
res, err := utils.CreatePeerNoValidate(t.Context(), shared.CatalogPool{Pool: pool}, peer, true)
4545
require.NoError(t, err)
4646
if res.Status != protos.CreatePeerStatus_CREATED {
4747
require.Fail(t, res.Message)

0 commit comments

Comments
 (0)