mirror of
https://gitee.com/milvus-io/milvus.git
synced 2024-12-01 11:29:48 +08:00
a0614a3ff8
The NodeID field is deprecated in current design Query Node segment detector shall read NodeIds instead of NodeID Fix #16911 Signed-off-by: Congqi Xia <congqi.xia@zilliz.com>
304 lines
7.2 KiB
Go
304 lines
7.2 KiB
Go
package querynode
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/milvus-io/milvus/internal/proto/querypb"
|
|
"github.com/milvus-io/milvus/internal/util/funcutil"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.etcd.io/etcd/server/v3/etcdserver/api/v3client"
|
|
)
|
|
|
|
func TestEtcdShardSegmentDetector_watch(t *testing.T) {
|
|
|
|
client := v3client.New(embedetcdServer.Server)
|
|
defer client.Close()
|
|
|
|
type testCase struct {
|
|
name string
|
|
oldRecords map[string]*querypb.SegmentInfo
|
|
updateRecords map[string]*querypb.SegmentInfo
|
|
delRecords []string
|
|
expectInitEvents []segmentEvent
|
|
expectUpdateEvents []segmentEvent
|
|
oldGarbage map[string]string
|
|
newGarbage map[string]string
|
|
|
|
collectionID int64
|
|
replicaID int64
|
|
channel string
|
|
}
|
|
cases := []testCase{
|
|
{
|
|
name: "normal init",
|
|
collectionID: 1,
|
|
replicaID: 1,
|
|
channel: "dml_1_1_v0",
|
|
oldRecords: map[string]*querypb.SegmentInfo{
|
|
"segment_1": {
|
|
CollectionID: 1,
|
|
SegmentID: 1,
|
|
NodeID: 1,
|
|
DmChannel: "dml_1_1_v0",
|
|
ReplicaIds: []int64{1, 2},
|
|
NodeIds: []UniqueID{1},
|
|
},
|
|
},
|
|
oldGarbage: map[string]string{
|
|
"noice1": string([]byte{23, 21, 20}),
|
|
},
|
|
expectInitEvents: []segmentEvent{
|
|
{
|
|
eventType: segmentAdd,
|
|
segmentID: 1,
|
|
nodeIDs: []int64{1},
|
|
state: segmentStateLoaded,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "normal init with other segments",
|
|
collectionID: 1,
|
|
replicaID: 1,
|
|
channel: "dml_1_1_v0",
|
|
oldRecords: map[string]*querypb.SegmentInfo{
|
|
"segment_1": {
|
|
CollectionID: 1,
|
|
SegmentID: 1,
|
|
NodeID: 1,
|
|
DmChannel: "dml_1_1_v0",
|
|
ReplicaIds: []int64{1, 2},
|
|
NodeIds: []UniqueID{1},
|
|
},
|
|
"segment_2": {
|
|
CollectionID: 1,
|
|
SegmentID: 2,
|
|
NodeID: 1,
|
|
DmChannel: "dml_1_1_v1",
|
|
ReplicaIds: []int64{1, 2},
|
|
NodeIds: []UniqueID{1},
|
|
},
|
|
"segment_3": {
|
|
CollectionID: 2,
|
|
SegmentID: 3,
|
|
NodeID: 2,
|
|
DmChannel: "dml_3_2_v0",
|
|
ReplicaIds: []int64{1, 2},
|
|
NodeIds: []UniqueID{2},
|
|
},
|
|
"segment_4": { // may not happen
|
|
CollectionID: 1,
|
|
SegmentID: 4,
|
|
NodeID: 1,
|
|
DmChannel: "dml_1_1_v0",
|
|
ReplicaIds: []int64{2},
|
|
NodeIds: []UniqueID{1},
|
|
},
|
|
},
|
|
expectInitEvents: []segmentEvent{
|
|
{
|
|
eventType: segmentAdd,
|
|
segmentID: 1,
|
|
nodeIDs: []int64{1},
|
|
state: segmentStateLoaded,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "normal add segment",
|
|
collectionID: 1,
|
|
replicaID: 1,
|
|
channel: "dml_1_1_v0",
|
|
updateRecords: map[string]*querypb.SegmentInfo{
|
|
"segment_1": {
|
|
CollectionID: 1,
|
|
SegmentID: 1,
|
|
NodeIds: []int64{1},
|
|
DmChannel: "dml_1_1_v0",
|
|
ReplicaIds: []int64{1, 2},
|
|
},
|
|
},
|
|
oldGarbage: map[string]string{
|
|
"noice1": string([]byte{23, 21, 20}),
|
|
},
|
|
expectUpdateEvents: []segmentEvent{
|
|
{
|
|
eventType: segmentAdd,
|
|
segmentID: 1,
|
|
nodeIDs: []int64{1},
|
|
state: segmentStateLoaded,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "normal add segment with other replica",
|
|
collectionID: 1,
|
|
replicaID: 1,
|
|
channel: "dml_1_1_v0",
|
|
updateRecords: map[string]*querypb.SegmentInfo{
|
|
"segment_1": {
|
|
CollectionID: 1,
|
|
SegmentID: 1,
|
|
NodeIds: []int64{1},
|
|
DmChannel: "dml_1_1_v0",
|
|
ReplicaIds: []int64{1, 2},
|
|
},
|
|
"segment_2": {
|
|
CollectionID: 1,
|
|
SegmentID: 2,
|
|
NodeIds: []int64{2},
|
|
DmChannel: "dml_2_1_v1",
|
|
ReplicaIds: []int64{2},
|
|
},
|
|
},
|
|
newGarbage: map[string]string{
|
|
"noice1": string([]byte{23, 21, 20}),
|
|
},
|
|
expectUpdateEvents: []segmentEvent{
|
|
{
|
|
eventType: segmentAdd,
|
|
segmentID: 1,
|
|
nodeIDs: []int64{1},
|
|
state: segmentStateLoaded,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "normal remove segment",
|
|
collectionID: 1,
|
|
replicaID: 1,
|
|
channel: "dml_1_1_v0",
|
|
oldRecords: map[string]*querypb.SegmentInfo{
|
|
"segment_1": {
|
|
CollectionID: 1,
|
|
SegmentID: 1,
|
|
NodeIds: []int64{1},
|
|
DmChannel: "dml_1_1_v0",
|
|
ReplicaIds: []int64{1, 2},
|
|
},
|
|
},
|
|
oldGarbage: map[string]string{
|
|
"noice1": string([]byte{23, 21, 20}),
|
|
},
|
|
expectInitEvents: []segmentEvent{
|
|
{
|
|
eventType: segmentAdd,
|
|
segmentID: 1,
|
|
nodeIDs: []int64{1},
|
|
state: segmentStateLoaded,
|
|
},
|
|
},
|
|
delRecords: []string{
|
|
"segment_1", "noice1",
|
|
},
|
|
expectUpdateEvents: []segmentEvent{
|
|
{
|
|
eventType: segmentDel,
|
|
segmentID: 1,
|
|
nodeIDs: []int64{1},
|
|
state: segmentStateOffline,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "normal remove segment with other replica",
|
|
collectionID: 1,
|
|
replicaID: 1,
|
|
channel: "dml_1_1_v0",
|
|
oldRecords: map[string]*querypb.SegmentInfo{
|
|
"segment_1": {
|
|
CollectionID: 1,
|
|
SegmentID: 1,
|
|
NodeIds: []int64{1},
|
|
DmChannel: "dml_1_1_v0",
|
|
ReplicaIds: []int64{1, 2},
|
|
},
|
|
"segment_2": {
|
|
CollectionID: 1,
|
|
SegmentID: 2,
|
|
NodeIds: []int64{2},
|
|
DmChannel: "dml_2_1_v1",
|
|
ReplicaIds: []int64{2},
|
|
},
|
|
},
|
|
expectInitEvents: []segmentEvent{
|
|
{
|
|
eventType: segmentAdd,
|
|
segmentID: 1,
|
|
nodeIDs: []int64{1},
|
|
state: segmentStateLoaded,
|
|
},
|
|
},
|
|
delRecords: []string{
|
|
"segment_1", "segment_2",
|
|
},
|
|
expectUpdateEvents: []segmentEvent{
|
|
{
|
|
eventType: segmentDel,
|
|
segmentID: 1,
|
|
nodeIDs: []int64{1},
|
|
state: segmentStateOffline,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
suffix := funcutil.RandomString(6)
|
|
rootPath := fmt.Sprintf("qn_shard_segment_detector_watch_%s", suffix)
|
|
|
|
ctx := context.Background()
|
|
// put existing records
|
|
for key, info := range tc.oldRecords {
|
|
bs, err := proto.Marshal(info)
|
|
require.NoError(t, err)
|
|
_, err = client.Put(ctx, path.Join(rootPath, key), string(bs))
|
|
require.NoError(t, err)
|
|
}
|
|
// put garbage data
|
|
for key, value := range tc.oldGarbage {
|
|
_, err := client.Put(ctx, path.Join(rootPath, key), value)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
sd := NewEtcdShardSegmentDetector(client, rootPath)
|
|
segments, ch := sd.watchSegments(tc.collectionID, tc.replicaID, tc.channel)
|
|
assert.ElementsMatch(t, tc.expectInitEvents, segments)
|
|
|
|
// update etcd kvs to generate events
|
|
go func() {
|
|
for key, info := range tc.updateRecords {
|
|
bs, err := proto.Marshal(info)
|
|
require.NoError(t, err)
|
|
_, err = client.Put(ctx, path.Join(rootPath, key), string(bs))
|
|
require.NoError(t, err)
|
|
}
|
|
for _, k := range tc.delRecords {
|
|
_, err := client.Delete(ctx, path.Join(rootPath, k))
|
|
require.NoError(t, err)
|
|
}
|
|
for key, value := range tc.newGarbage {
|
|
_, err := client.Put(ctx, path.Join(rootPath, key), value)
|
|
require.NoError(t, err)
|
|
}
|
|
// need a way to detect event processed
|
|
time.Sleep(time.Millisecond * 100)
|
|
|
|
sd.Close()
|
|
}()
|
|
|
|
var newEvents []segmentEvent
|
|
for evt := range ch {
|
|
newEvents = append(newEvents, evt)
|
|
}
|
|
assert.ElementsMatch(t, tc.expectUpdateEvents, newEvents)
|
|
})
|
|
}
|
|
}
|