Skip to content

Commit 10017e8

Browse files
committed
Add monetate_crc32 multi-column vindex. (#1)
Compute multi-column vindex compatible with existing application layer sharding. Supports integer, varchar, and varbinary columns. Signed-off-by: Jeffrey J. Persch <jjpersch@monetate.com>
1 parent 5b0d3ad commit 10017e8

2 files changed

Lines changed: 137 additions & 0 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
Copyright 2023 Monetate, Inc.
3+
*/
4+
5+
package vindexes
6+
7+
import (
8+
"bytes"
9+
"context"
10+
"encoding/binary"
11+
"hash/crc32"
12+
"strings"
13+
14+
"vitess.io/vitess/go/sqltypes"
15+
"vitess.io/vitess/go/vt/key"
16+
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
17+
"vitess.io/vitess/go/vt/vterrors"
18+
)
19+
20+
var _ MultiColumn = (*MonetateCRC32)(nil)
21+
22+
type MonetateCRC32 struct {
23+
name string
24+
}
25+
26+
// NewMonetateCRC32 creates a new MonetateCRC32.
27+
func NewMonetateCRC32(name string, m map[string]string) (Vindex, error) {
28+
return &MonetateCRC32{name: name}, nil
29+
}
30+
31+
func (m *MonetateCRC32) String() string {
32+
return m.name
33+
}
34+
35+
func (m *MonetateCRC32) Cost() int {
36+
return 1
37+
}
38+
39+
func (m *MonetateCRC32) IsUnique() bool {
40+
return true
41+
}
42+
43+
func (m *MonetateCRC32) NeedsVCursor() bool {
44+
return false
45+
}
46+
47+
func (m *MonetateCRC32) Map(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.ShardDestination, error) {
48+
out := make([]key.ShardDestination, 0, len(rowsColValues))
49+
for _, colValues := range rowsColValues {
50+
ksid, err := ChecksumValues(colValues)
51+
if err != nil {
52+
out = append(out, key.DestinationNone{})
53+
continue
54+
}
55+
out = append(out, key.DestinationKeyspaceID(ksid))
56+
}
57+
return out, nil
58+
}
59+
60+
func (m *MonetateCRC32) Verify(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) {
61+
out := make([]bool, 0, len(rowsColValues))
62+
for idx, colValues := range rowsColValues {
63+
ksid, err := ChecksumValues(colValues)
64+
if err != nil {
65+
return nil, err
66+
}
67+
out = append(out, bytes.Equal(ksid, ksids[idx]))
68+
}
69+
return out, nil
70+
}
71+
72+
func (m *MonetateCRC32) PartialVindex() bool {
73+
return false
74+
}
75+
76+
func ChecksumValues(colValues []sqltypes.Value) ([]byte, error) {
77+
// concat string values of columns, separated by slashes
78+
var parts []string
79+
for _, colVal := range colValues {
80+
if !(colVal.IsIntegral() || colVal.IsText() || colVal.IsBinary()) {
81+
return nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "invalid monetate vindex value")
82+
}
83+
parts = append(parts, colVal.ToString())
84+
}
85+
var shardKey = strings.Join(parts, "/")
86+
var checksum = crc32.ChecksumIEEE([]byte(shardKey))
87+
var vshard = checksum % 1048576 // 20 bit vshard id
88+
89+
var hashed [4]byte
90+
binary.BigEndian.PutUint32(hashed[:], vshard<<12)
91+
return hashed[:], nil
92+
}
93+
94+
func init() {
95+
Register("monetate_crc32", NewMonetateCRC32)
96+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
Copyright 2023 Monetate, Inc.
3+
*/
4+
5+
package vindexes
6+
7+
import (
8+
"context"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
14+
"vitess.io/vitess/go/sqltypes"
15+
"vitess.io/vitess/go/vt/key"
16+
)
17+
18+
func TestMonetateCRC32Map(t *testing.T) {
19+
vindex, err := CreateVindex("monetate_crc32", "monetate", map[string]string{})
20+
require.NoError(t, err)
21+
mutiCol := vindex.(MultiColumn)
22+
23+
got, err := mutiCol.Map(context.Background(), nil, [][]sqltypes.Value{{
24+
// visitor shard key
25+
sqltypes.NewInt64(1), sqltypes.NewInt64(2), sqltypes.NewInt64(3),
26+
}, {
27+
// customer shard key
28+
sqltypes.NewInt64(1), sqltypes.NewVarBinary("customer_id"),
29+
}, {
30+
// customer shard key, invalid customer id as null
31+
sqltypes.NewInt64(1), sqltypes.NULL,
32+
}})
33+
assert.NoError(t, err)
34+
35+
want := []key.Destination{
36+
key.DestinationKeyspaceID("\x1b\x1c\x60\x00"),
37+
key.DestinationKeyspaceID("\xc4\x4b\xe0\x00"),
38+
key.DestinationNone{},
39+
}
40+
assert.Equal(t, want, got)
41+
}

0 commit comments

Comments
 (0)