diff --git a/cmd/storage/benchmark.go b/cmd/storage/benchmark.go deleted file mode 100644 index 6a7c06f9a209b25df74545d1f23f58b2d1c80594..0000000000000000000000000000000000000000 --- a/cmd/storage/benchmark.go +++ /dev/null @@ -1,315 +0,0 @@ -package main - -import ( - "context" - "crypto/md5" - "flag" - "fmt" - "log" - "math/rand" - "os" - "sync" - "sync/atomic" - "time" - - "github.com/pivotal-golang/bytefmt" - "github.com/zilliztech/milvus-distributed/internal/storage" - storagetype "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -// Global variables -var durationSecs, threads, loops, numVersion, batchOpSize int -var valueSize uint64 -var valueData []byte -var batchValueData [][]byte -var counter, totalKeyCount, keyNum int32 -var endTime, setFinish, getFinish, deleteFinish time.Time -var totalKeys [][]byte - -var logFileName = "benchmark.log" -var logFile *os.File - -var store storagetype.Store -var wg sync.WaitGroup - -func runSet() { - for time.Now().Before(endTime) { - num := atomic.AddInt32(&keyNum, 1) - key := []byte(fmt.Sprint("key", num)) - for ver := 1; ver <= numVersion; ver++ { - atomic.AddInt32(&counter, 1) - err := store.PutRow(context.Background(), key, valueData, "empty", uint64(ver)) - if err != nil { - log.Fatalf("Error setting key %s, %s", key, err.Error()) - //atomic.AddInt32(&setCount, -1) - } - } - } - // Remember last done time - setFinish = time.Now() - wg.Done() -} - -func runBatchSet() { - for time.Now().Before(endTime) { - num := atomic.AddInt32(&keyNum, int32(batchOpSize)) - keys := make([][]byte, batchOpSize) - versions := make([]uint64, batchOpSize) - batchSuffix := make([]string, batchOpSize) - for n := batchOpSize; n > 0; n-- { - keys[n-1] = []byte(fmt.Sprint("key", num-int32(n))) - } - for ver := 1; ver <= numVersion; ver++ { - atomic.AddInt32(&counter, 1) - err := store.PutRows(context.Background(), keys, batchValueData, batchSuffix, versions) - if err != nil { - log.Fatalf("Error setting batch keys %s %s", keys, err.Error()) - //atomic.AddInt32(&batchSetCount, -1) - } - } - } - setFinish = time.Now() - wg.Done() -} - -func runGet() { - for time.Now().Before(endTime) { - num := atomic.AddInt32(&counter, 1) - //num := atomic.AddInt32(&keyNum, 1) - //key := []byte(fmt.Sprint("key", num)) - num = num % totalKeyCount - key := totalKeys[num] - _, err := store.GetRow(context.Background(), key, uint64(numVersion)) - if err != nil { - log.Fatalf("Error getting key %s, %s", key, err.Error()) - //atomic.AddInt32(&getCount, -1) - } - } - // Remember last done time - getFinish = time.Now() - wg.Done() -} - -func runBatchGet() { - for time.Now().Before(endTime) { - num := atomic.AddInt32(&keyNum, int32(batchOpSize)) - //keys := make([][]byte, batchOpSize) - //for n := batchOpSize; n > 0; n-- { - // keys[n-1] = []byte(fmt.Sprint("key", num-int32(n))) - //} - end := num % totalKeyCount - if end < int32(batchOpSize) { - end = int32(batchOpSize) - } - start := end - int32(batchOpSize) - keys := totalKeys[start:end] - versions := make([]uint64, batchOpSize) - for i := range versions { - versions[i] = uint64(numVersion) - } - atomic.AddInt32(&counter, 1) - _, err := store.GetRows(context.Background(), keys, versions) - if err != nil { - log.Fatalf("Error getting key %s, %s", keys, err.Error()) - //atomic.AddInt32(&batchGetCount, -1) - } - } - // Remember last done time - getFinish = time.Now() - wg.Done() -} - -func runDelete() { - for time.Now().Before(endTime) { - num := atomic.AddInt32(&counter, 1) - //num := atomic.AddInt32(&keyNum, 1) - //key := []byte(fmt.Sprint("key", num)) - num = num % totalKeyCount - key := totalKeys[num] - err := store.DeleteRow(context.Background(), key, uint64(numVersion)) - if err != nil { - log.Fatalf("Error getting key %s, %s", key, err.Error()) - //atomic.AddInt32(&deleteCount, -1) - } - } - // Remember last done time - deleteFinish = time.Now() - wg.Done() -} - -func runBatchDelete() { - for time.Now().Before(endTime) { - num := atomic.AddInt32(&keyNum, int32(batchOpSize)) - //keys := make([][]byte, batchOpSize) - //for n := batchOpSize; n > 0; n-- { - // keys[n-1] = []byte(fmt.Sprint("key", num-int32(n))) - //} - end := num % totalKeyCount - if end < int32(batchOpSize) { - end = int32(batchOpSize) - } - start := end - int32(batchOpSize) - keys := totalKeys[start:end] - atomic.AddInt32(&counter, 1) - versions := make([]uint64, batchOpSize) - for i := range versions { - versions[i] = uint64(numVersion) - } - err := store.DeleteRows(context.Background(), keys, versions) - if err != nil { - log.Fatalf("Error getting key %s, %s", keys, err.Error()) - //atomic.AddInt32(&batchDeleteCount, -1) - } - } - // Remember last done time - getFinish = time.Now() - wg.Done() -} - -func main() { - // Parse command line - myflag := flag.NewFlagSet("myflag", flag.ExitOnError) - myflag.IntVar(&durationSecs, "d", 5, "Duration of each test in seconds") - myflag.IntVar(&threads, "t", 1, "Number of threads to run") - myflag.IntVar(&loops, "l", 1, "Number of times to repeat test") - var sizeArg string - var storeType string - myflag.StringVar(&sizeArg, "z", "1k", "Size of objects in bytes with postfix K, M, and G") - myflag.StringVar(&storeType, "s", "s3", "Storage type, tikv or minio or s3") - myflag.IntVar(&numVersion, "v", 1, "Max versions for each key") - myflag.IntVar(&batchOpSize, "b", 100, "Batch operation kv pair number") - - if err := myflag.Parse(os.Args[1:]); err != nil { - os.Exit(1) - } - - // Check the arguments - var err error - if valueSize, err = bytefmt.ToBytes(sizeArg); err != nil { - log.Fatalf("Invalid -z argument for object size: %v", err) - } - var option = storagetype.Option{TikvAddress: "localhost:2379", Type: storeType, BucketName: "zilliz-hz"} - - store, err = storage.NewStore(context.Background(), option) - if err != nil { - log.Fatalf("Error when creating storage " + err.Error()) - } - logFile, err = os.OpenFile(logFileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0777) - if err != nil { - log.Fatalf("Prepare log file error, " + err.Error()) - } - - // Echo the parameters - log.Printf("Benchmark log will write to file %s\n", logFile.Name()) - fmt.Fprintf(logFile, "Parameters: duration=%d, threads=%d, loops=%d, valueSize=%s, batchSize=%d, versions=%d\n", durationSecs, threads, loops, sizeArg, batchOpSize, numVersion) - // Init test data - valueData = make([]byte, valueSize) - rand.Read(valueData) - hasher := md5.New() - hasher.Write(valueData) - - batchValueData = make([][]byte, batchOpSize) - for i := range batchValueData { - batchValueData[i] = make([]byte, valueSize) - rand.Read(batchValueData[i]) - hasher := md5.New() - hasher.Write(batchValueData[i]) - } - - // Loop running the tests - for loop := 1; loop <= loops; loop++ { - - // reset counters - counter = 0 - keyNum = 0 - totalKeyCount = 0 - totalKeys = nil - - // Run the batchSet case - // key seq start from setCount - counter = 0 - startTime := time.Now() - endTime = startTime.Add(time.Second * time.Duration(durationSecs)) - for n := 1; n <= threads; n++ { - wg.Add(1) - go runBatchSet() - } - wg.Wait() - - setTime := setFinish.Sub(startTime).Seconds() - bps := float64(uint64(counter)*valueSize*uint64(batchOpSize)) / setTime - fmt.Fprintf(logFile, "Loop %d: BATCH PUT time %.1f secs, batchs = %d, kv pairs = %d, speed = %sB/sec, %.1f operations/sec, %.1f kv/sec.\n", - loop, setTime, counter, counter*int32(batchOpSize), bytefmt.ByteSize(uint64(bps)), float64(counter)/setTime, float64(counter*int32(batchOpSize))/setTime) - // Record all test keys - //totalKeyCount = keyNum - //totalKeys = make([][]byte, totalKeyCount) - //for i := int32(0); i < totalKeyCount; i++ { - // totalKeys[i] = []byte(fmt.Sprint("key", i)) - //} - // - //// Run the get case - //counter = 0 - //startTime = time.Now() - //endTime = startTime.Add(time.Second * time.Duration(durationSecs)) - //for n := 1; n <= threads; n++ { - // wg.Add(1) - // go runGet() - //} - //wg.Wait() - // - //getTime := getFinish.Sub(startTime).Seconds() - //bps = float64(uint64(counter)*valueSize) / getTime - //fmt.Fprint(logFile, fmt.Sprintf("Loop %d: GET time %.1f secs, kv pairs = %d, speed = %sB/sec, %.1f operations/sec, %.1f kv/sec.\n", - // loop, getTime, counter, bytefmt.ByteSize(uint64(bps)), float64(counter)/getTime, float64(counter)/getTime)) - - // Run the batchGet case - //counter = 0 - //startTime = time.Now() - //endTime = startTime.Add(time.Second * time.Duration(durationSecs)) - //for n := 1; n <= threads; n++ { - // wg.Add(1) - // go runBatchGet() - //} - //wg.Wait() - // - //getTime = getFinish.Sub(startTime).Seconds() - //bps = float64(uint64(counter)*valueSize*uint64(batchOpSize)) / getTime - //fmt.Fprint(logFile, fmt.Sprintf("Loop %d: BATCH GET time %.1f secs, batchs = %d, kv pairs = %d, speed = %sB/sec, %.1f operations/sec, %.1f kv/sec.\n", - // loop, getTime, counter, counter*int32(batchOpSize), bytefmt.ByteSize(uint64(bps)), float64(counter)/getTime, float64(counter * int32(batchOpSize))/getTime)) - // - //// Run the delete case - //counter = 0 - //startTime = time.Now() - //endTime = startTime.Add(time.Second * time.Duration(durationSecs)) - //for n := 1; n <= threads; n++ { - // wg.Add(1) - // go runDelete() - //} - //wg.Wait() - // - //deleteTime := deleteFinish.Sub(startTime).Seconds() - //bps = float64(uint64(counter)*valueSize) / deleteTime - //fmt.Fprint(logFile, fmt.Sprintf("Loop %d: Delete time %.1f secs, kv pairs = %d, %.1f operations/sec, %.1f kv/sec.\n", - // loop, deleteTime, counter, float64(counter)/deleteTime, float64(counter)/deleteTime)) - // - //// Run the batchDelete case - //counter = 0 - //startTime = time.Now() - //endTime = startTime.Add(time.Second * time.Duration(durationSecs)) - //for n := 1; n <= threads; n++ { - // wg.Add(1) - // go runBatchDelete() - //} - //wg.Wait() - // - //deleteTime = setFinish.Sub(startTime).Seconds() - //bps = float64(uint64(counter)*valueSize*uint64(batchOpSize)) / setTime - //fmt.Fprint(logFile, fmt.Sprintf("Loop %d: BATCH DELETE time %.1f secs, batchs = %d, kv pairs = %d, %.1f operations/sec, %.1f kv/sec.\n", - // loop, setTime, counter, counter*int32(batchOpSize), float64(counter)/setTime, float64(counter * int32(batchOpSize))/setTime)) - - // Print line mark - lineMark := "\n" - fmt.Fprint(logFile, lineMark) - } - log.Print("Benchmark test done.") -} diff --git a/deployments/docker/docker-compose.yml b/deployments/docker/docker-compose.yml index 60bf5d9fff1f2a0aa47274becc742f778aaeb77a..0ae708a19ecb9ababafe5fcdb6bd5f9d5eac529e 100644 --- a/deployments/docker/docker-compose.yml +++ b/deployments/docker/docker-compose.yml @@ -36,6 +36,14 @@ services: networks: - milvus + jaeger: + image: jaegertracing/all-in-one:latest + ports: + - "6831:6831/udp" + - "16686:16686" + networks: + - milvus + networks: milvus: diff --git a/docker-compose.yml b/docker-compose.yml index cba23befabc4c39314b179b3227a1d3570ee3c31..9f3599abb9a5b139323717b4e98e4a9d7e91b8f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,5 +83,10 @@ services: networks: - milvus + jaeger: + image: jaegertracing/all-in-one:latest + networks: + - milvus + networks: milvus: diff --git a/go.mod b/go.mod index 47afde2bff278667721c394a64396793788e92f2..bb426c8ba01c5a6bcfdd5778e54e5f1aba3f7a79 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,17 @@ go 1.15 require ( code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48 // indirect + github.com/HdrHistogram/hdrhistogram-go v1.0.1 // indirect github.com/apache/pulsar-client-go v0.1.1 - github.com/aws/aws-sdk-go v1.30.8 + github.com/apache/thrift v0.13.0 + github.com/aws/aws-sdk-go v1.30.8 // indirect github.com/coreos/etcd v3.3.25+incompatible // indirect - github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 + github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect github.com/frankban/quicktest v1.10.2 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/git-hooks/git-hooks v1.3.1 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/mock v1.3.1 github.com/golang/protobuf v1.3.2 github.com/google/btree v1.0.0 github.com/klauspost/compress v1.10.11 // indirect @@ -20,12 +23,12 @@ require ( github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/onsi/ginkgo v1.12.1 // indirect github.com/onsi/gomega v1.10.0 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pingcap/check v0.0.0-20200212061837-5e12011dc712 // indirect github.com/pingcap/errors v0.11.4 // indirect github.com/pingcap/log v0.0.0-20200828042413-fce0951f1463 // indirect - github.com/pivotal-golang/bytefmt v0.0.0-20200131002437-cf55d5288a48 + github.com/pivotal-golang/bytefmt v0.0.0-20200131002437-cf55d5288a48 // indirect github.com/prometheus/client_golang v1.5.1 // indirect github.com/prometheus/common v0.10.0 // indirect github.com/prometheus/procfs v0.1.3 // indirect @@ -35,7 +38,9 @@ require ( github.com/spf13/cast v1.3.0 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 - github.com/tikv/client-go v0.0.0-20200824032810-95774393107b + github.com/tikv/client-go v0.0.0-20200824032810-95774393107b // indirect + github.com/uber/jaeger-client-go v2.25.0+incompatible + github.com/uber/jaeger-lib v2.4.0+incompatible // indirect github.com/urfave/cli v1.22.5 // indirect github.com/yahoo/athenz v1.9.16 // indirect go.etcd.io/etcd v0.5.0-alpha.5.0.20191023171146-3cf2f69b5738 @@ -50,7 +55,7 @@ require ( google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150 // indirect google.golang.org/grpc v1.31.0 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect - gopkg.in/yaml.v2 v2.3.0 + gopkg.in/yaml.v2 v2.3.0 // indirect honnef.co/go/tools v0.0.1-2020.1.4 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/go.sum b/go.sum index eb4ef6b6a4059c712a42b92603ceb5d687117066..14c1fca608a146e91e855a8c730df2ad5684861a 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/HdrHistogram/hdrhistogram-go v1.0.1 h1:GX8GAYDuhlFQnI2fRDHQhTlkHMz8bEn0jTI6LJU0mpw= +github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= @@ -24,6 +26,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1C github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/pulsar-client-go v0.1.1 h1:v/kU+2ZCC6yFIcbZrFtWa9/nvVzVr18L+xYJUvZSxEQ= github.com/apache/pulsar-client-go v0.1.1/go.mod h1:mlxC65KL1BLhGO2bnT9zWMttVzR2czVPb27D477YpyU= +github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/ardielle/ardielle-go v1.5.2 h1:TilHTpHIQJ27R1Tl/iITBzMwiUGSlVfiVhwDNGM3Zj4= github.com/ardielle/ardielle-go v1.5.2/go.mod h1:I4hy1n795cUhaVt/ojz83SNVCYIGsAFAONtv2Dr7HUI= github.com/ardielle/ardielle-tools v1.5.4/go.mod h1:oZN+JRMnqGiIhrzkRN9l26Cej9dEx4jeNG6A+AdkShk= @@ -117,6 +121,7 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18h github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v0.0.0-20180814211427-aa810b61a9c7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -343,6 +348,7 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/protocolbuffers/protobuf v3.14.0+incompatible h1:8r0H76h/Q/lEnFFY60AuM23NOnaDMi6bd7zuboSYM+o= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446 h1:/NRJ5vAYoqz+7sG51ubIDHXeWO8DlTSrToPu6q11ziA= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -403,6 +409,12 @@ github.com/tikv/client-go v0.0.0-20200824032810-95774393107b/go.mod h1:K0NcdVNrX github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/uber/jaeger-client-go v1.6.0 h1:3+zLlq+4npI5fg8IsgAje3YsP7TcEdNzJScyqFIzxEQ= +github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= +github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v1.5.0 h1:OHbgr8l656Ub3Fw5k9SWnBfIEwvoHQ+W2y+Aa9D1Uyo= +github.com/uber/jaeger-lib v2.4.0+incompatible h1:fY7QsGQWiCt8pajv4r7JEvmATdCVaWxXbjwyYwsNaLQ= +github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= github.com/unrolled/render v1.0.0 h1:XYtvhA3UkpB7PqkvhUFYmpKD55OudoIeygcfus4vcd4= diff --git a/internal/core/src/common/FieldMeta.h b/internal/core/src/common/FieldMeta.h index 0adf92bd49b42d1c0945c19e19a7508a0ab57937..9486ed2524095fd9e9ddb31af26012227215ecbc 100644 --- a/internal/core/src/common/FieldMeta.h +++ b/internal/core/src/common/FieldMeta.h @@ -18,7 +18,7 @@ namespace milvus { inline int -field_sizeof(DataType data_type, int dim = 1) { +datatype_sizeof(DataType data_type, int dim = 1) { switch (data_type) { case DataType::BOOL: return sizeof(bool); @@ -78,7 +78,7 @@ datatype_name(DataType data_type) { } inline bool -field_is_vector(DataType datatype) { +datatype_is_vector(DataType datatype) { return datatype == DataType::VECTOR_BINARY || datatype == DataType::VECTOR_FLOAT; } @@ -119,9 +119,9 @@ struct FieldMeta { int get_sizeof() const { if (is_vector()) { - return field_sizeof(type_, get_dim()); + return datatype_sizeof(type_, get_dim()); } else { - return field_sizeof(type_, 1); + return datatype_sizeof(type_, 1); } } diff --git a/internal/core/src/common/Schema.cpp b/internal/core/src/common/Schema.cpp index 9b5a0c3b8a623d1bd87dea62e234d133792436c0..c7b91a7b4c03d2bd0d1873a2382bd25489944870 100644 --- a/internal/core/src/common/Schema.cpp +++ b/internal/core/src/common/Schema.cpp @@ -50,7 +50,7 @@ Schema::ParseFrom(const milvus::proto::schema::CollectionSchema& schema_proto) { schema->primary_key_offset_opt_ = schema->size(); } - if (field_is_vector(data_type)) { + if (datatype_is_vector(data_type)) { auto type_map = RepeatedKeyValToMap(child.type_params()); auto index_map = RepeatedKeyValToMap(child.index_params()); if (!index_map.count("metric_type")) { diff --git a/internal/core/src/common/Types.h b/internal/core/src/common/Types.h index aee866d457978f6a57e8b4e2a7369a63f872e06c..9d36a9c505920e73270040413b3f9a0b11529c8b 100644 --- a/internal/core/src/common/Types.h +++ b/internal/core/src/common/Types.h @@ -14,13 +14,15 @@ #include <faiss/MetricType.h> #include <string> #include <boost/align/aligned_allocator.hpp> +#include <memory> #include <vector> namespace milvus { using Timestamp = uint64_t; // TODO: use TiKV-like timestamp using engine::DataType; using engine::FieldElementType; -using engine::QueryResult; +using engine::idx_t; + using MetricType = faiss::MetricType; MetricType @@ -39,4 +41,33 @@ constexpr std::false_type always_false{}; template <typename T> using aligned_vector = std::vector<T, boost::alignment::aligned_allocator<T, 512>>; +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct QueryResult { + QueryResult() = default; + QueryResult(uint64_t num_queries, uint64_t topK) : topK_(topK), num_queries_(num_queries) { + auto count = get_row_count(); + result_distances_.resize(count); + internal_seg_offsets_.resize(count); + } + + [[nodiscard]] uint64_t + get_row_count() const { + return topK_ * num_queries_; + } + + public: + uint64_t num_queries_; + uint64_t topK_; + uint64_t seg_id_; + std::vector<float> result_distances_; + + public: + // TODO(gexi): utilize these field + std::vector<int64_t> internal_seg_offsets_; + std::vector<int64_t> result_offsets_; + std::vector<std::vector<char>> row_data_; +}; + +using QueryResultPtr = std::shared_ptr<QueryResult>; + } // namespace milvus diff --git a/internal/core/src/indexbuilder/IndexWrapper.cpp b/internal/core/src/indexbuilder/IndexWrapper.cpp index fcced635e0e508894a70995b68002310c7b8182f..5d95eabf3d97ba40fcb9ee646b480a9c5a280254 100644 --- a/internal/core/src/indexbuilder/IndexWrapper.cpp +++ b/internal/core/src/indexbuilder/IndexWrapper.cpp @@ -55,6 +55,7 @@ IndexWrapper::parse_impl(const std::string& serialized_params_str, knowhere::Con } auto stoi_closure = [](const std::string& s) -> int { return std::stoi(s); }; + auto stof_closure = [](const std::string& s) -> int { return std::stof(s); }; /***************************** meta *******************************/ check_parameter<int>(conf, milvus::knowhere::meta::DIM, stoi_closure, std::nullopt); @@ -88,7 +89,7 @@ IndexWrapper::parse_impl(const std::string& serialized_params_str, knowhere::Con check_parameter<int>(conf, milvus::knowhere::IndexParams::edge_size, stoi_closure, std::nullopt); /************************** NGT Search Params *****************************/ - check_parameter<int>(conf, milvus::knowhere::IndexParams::epsilon, stoi_closure, std::nullopt); + check_parameter<float>(conf, milvus::knowhere::IndexParams::epsilon, stof_closure, std::nullopt); check_parameter<int>(conf, milvus::knowhere::IndexParams::max_search_edges, stoi_closure, std::nullopt); /************************** NGT_PANNG Params *****************************/ @@ -274,6 +275,12 @@ IndexWrapper::QueryWithParam(const knowhere::DatasetPtr& dataset, const char* se std::unique_ptr<IndexWrapper::QueryResult> IndexWrapper::QueryImpl(const knowhere::DatasetPtr& dataset, const knowhere::Config& conf) { + auto load_raw_data_closure = [&]() { LoadRawData(); }; // hide this pointer + auto index_type = get_index_type(); + if (is_in_nm_list(index_type)) { + std::call_once(raw_data_loaded_, load_raw_data_closure); + } + auto res = index_->Query(dataset, conf, nullptr); auto ids = res->Get<int64_t*>(milvus::knowhere::meta::IDS); auto distances = res->Get<float*>(milvus::knowhere::meta::DISTANCE); @@ -291,5 +298,19 @@ IndexWrapper::QueryImpl(const knowhere::DatasetPtr& dataset, const knowhere::Con return std::move(query_res); } +void +IndexWrapper::LoadRawData() { + auto index_type = get_index_type(); + if (is_in_nm_list(index_type)) { + auto bs = index_->Serialize(config_); + auto bptr = std::make_shared<milvus::knowhere::Binary>(); + auto deleter = [&](uint8_t*) {}; // avoid repeated deconstruction + bptr->data = std::shared_ptr<uint8_t[]>(static_cast<uint8_t*>(raw_data_.data()), deleter); + bptr->size = raw_data_.size(); + bs.Append(RAW_DATA, bptr); + index_->Load(bs); + } +} + } // namespace indexbuilder } // namespace milvus diff --git a/internal/core/src/indexbuilder/IndexWrapper.h b/internal/core/src/indexbuilder/IndexWrapper.h index 65c6f149febf89bd30521e0478ba4eb2782b8583..16f2721712c655bff7b2e7d53a235e32ed1d6458 100644 --- a/internal/core/src/indexbuilder/IndexWrapper.h +++ b/internal/core/src/indexbuilder/IndexWrapper.h @@ -66,6 +66,9 @@ class IndexWrapper { void StoreRawData(const knowhere::DatasetPtr& dataset); + void + LoadRawData(); + template <typename T> void check_parameter(knowhere::Config& conf, @@ -92,6 +95,7 @@ class IndexWrapper { milvus::json index_config_; knowhere::Config config_; std::vector<uint8_t> raw_data_; + std::once_flag raw_data_loaded_; }; } // namespace indexbuilder diff --git a/internal/core/src/query/CMakeLists.txt b/internal/core/src/query/CMakeLists.txt index a1de1d4ed502053407f16f1fc6e107c163cda653..7488270fee4817935004d4754359bea7a4b6329b 100644 --- a/internal/core/src/query/CMakeLists.txt +++ b/internal/core/src/query/CMakeLists.txt @@ -4,13 +4,16 @@ set(MILVUS_QUERY_SRCS generated/PlanNode.cpp generated/Expr.cpp visitors/ShowPlanNodeVisitor.cpp - visitors/ExecPlanNodeVisitor.cpp visitors/ShowExprVisitor.cpp + visitors/ExecPlanNodeVisitor.cpp visitors/ExecExprVisitor.cpp + visitors/VerifyPlanNodeVisitor.cpp + visitors/VerifyExprVisitor.cpp Plan.cpp Search.cpp SearchOnSealed.cpp - BruteForceSearch.cpp + SearchBruteForce.cpp + SubQueryResult.cpp ) add_library(milvus_query ${MILVUS_QUERY_SRCS}) -target_link_libraries(milvus_query milvus_proto milvus_utils) +target_link_libraries(milvus_query milvus_proto milvus_utils knowhere) diff --git a/internal/core/src/query/Plan.cpp b/internal/core/src/query/Plan.cpp index 96653593516c15f98797a9f2d4c18e316b0161e1..d2a8bf04b33237bea0770dbd7d9a576e6838f4d0 100644 --- a/internal/core/src/query/Plan.cpp +++ b/internal/core/src/query/Plan.cpp @@ -21,6 +21,7 @@ #include <boost/align/aligned_allocator.hpp> #include <boost/algorithm/string.hpp> #include <algorithm> +#include "query/generated/VerifyPlanNodeVisitor.h" namespace milvus::query { @@ -106,7 +107,7 @@ Parser::ParseRangeNode(const Json& out_body) { auto field_name = out_iter.key(); auto body = out_iter.value(); auto data_type = schema[field_name].get_data_type(); - Assert(!field_is_vector(data_type)); + Assert(!datatype_is_vector(data_type)); switch (data_type) { case DataType::BOOL: @@ -138,6 +139,8 @@ Parser::CreatePlanImpl(const std::string& dsl_str) { if (predicate != nullptr) { vec_node->predicate_ = std::move(predicate); } + VerifyPlanNodeVisitor verifier; + vec_node->accept(verifier); auto plan = std::make_unique<Plan>(schema); plan->tag2field_ = std::move(tag2field_); @@ -152,7 +155,7 @@ Parser::ParseTermNode(const Json& out_body) { auto field_name = out_iter.key(); auto body = out_iter.value(); auto data_type = schema[field_name].get_data_type(); - Assert(!field_is_vector(data_type)); + Assert(!datatype_is_vector(data_type)); switch (data_type) { case DataType::BOOL: { return ParseTermNodeImpl<bool>(field_name, body); diff --git a/internal/core/src/query/Search.cpp b/internal/core/src/query/Search.cpp index e1c0135f5f95cd62d641fb8635a2901f95f75a1b..35c9ec0c51eecfdda943654fadbbfa9566f7259d 100644 --- a/internal/core/src/query/Search.cpp +++ b/internal/core/src/query/Search.cpp @@ -16,7 +16,7 @@ #include <faiss/utils/distances.h> #include "utils/tools.h" -#include "query/BruteForceSearch.h" +#include "query/SearchBruteForce.h" namespace milvus::query { @@ -34,13 +34,13 @@ create_bitmap_view(std::optional<const BitmapSimple*> bitmaps_opt, int64_t chunk } Status -QueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, - const query::QueryInfo& info, - const float* query_data, - int64_t num_queries, - Timestamp timestamp, - std::optional<const BitmapSimple*> bitmaps_opt, - QueryResult& results) { +FloatSearch(const segcore::SegmentSmallIndex& segment, + const query::QueryInfo& info, + const float* query_data, + int64_t num_queries, + Timestamp timestamp, + std::optional<const BitmapSimple*> bitmaps_opt, + QueryResult& results) { auto& schema = segment.get_schema(); auto& indexing_record = segment.get_indexing_record(); auto& record = segment.get_insert_record(); @@ -75,6 +75,7 @@ QueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, const auto& indexing_entry = indexing_record.get_vec_entry(vecfield_offset); auto search_conf = indexing_entry.get_search_conf(topK); + // TODO: use sub_qr for (int chunk_id = 0; chunk_id < max_indexed_id; ++chunk_id) { auto indexing = indexing_entry.get_vec_indexing(chunk_id); auto dataset = knowhere::GenDataset(num_queries, dim, query_data); @@ -99,10 +100,12 @@ QueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, Assert(vec_chunk_size == indexing_entry.get_chunk_size()); auto max_chunk = upper_div(ins_barrier, vec_chunk_size); + // TODO: use sub_qr for (int chunk_id = max_indexed_id; chunk_id < max_chunk; ++chunk_id) { std::vector<int64_t> buf_uids(total_count, -1); std::vector<float> buf_dis(total_count, std::numeric_limits<float>::max()); + // should be not visitable faiss::float_maxheap_array_t buf = {(size_t)num_queries, (size_t)topK, buf_uids.data(), buf_dis.data()}; auto& chunk = vec_ptr->get_chunk(chunk_id); @@ -112,6 +115,7 @@ QueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, auto nsize = element_end - element_begin; auto bitmap_view = create_bitmap_view(bitmaps_opt, chunk_id); + // TODO: make it wrapped faiss::knn_L2sqr(query_data, chunk.data(), dim, num_queries, nsize, &buf, bitmap_view); Assert(buf_uids.size() == total_count); @@ -134,13 +138,13 @@ QueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, } Status -BinaryQueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, - const query::QueryInfo& info, - const uint8_t* query_data, - int64_t num_queries, - Timestamp timestamp, - std::optional<const BitmapSimple*> bitmaps_opt, - QueryResult& results) { +BinarySearch(const segcore::SegmentSmallIndex& segment, + const query::QueryInfo& info, + const uint8_t* query_data, + int64_t num_queries, + Timestamp timestamp, + std::optional<const BitmapSimple*> bitmaps_opt, + QueryResult& results) { auto& schema = segment.get_schema(); auto& indexing_record = segment.get_indexing_record(); auto& record = segment.get_insert_record(); @@ -169,8 +173,8 @@ BinaryQueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, auto total_count = topK * num_queries; // step 3: small indexing search - std::vector<int64_t> final_uids(total_count, -1); - std::vector<float> final_dis(total_count, std::numeric_limits<float>::max()); + // TODO: this is too intrusive + // TODO: use QuerySubResult instead query::dataset::BinaryQueryDataset query_dataset{metric_type, num_queries, topK, code_size, query_data}; using segcore::BinaryVector; @@ -181,30 +185,27 @@ BinaryQueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, auto vec_chunk_size = vec_ptr->get_chunk_size(); auto max_chunk = upper_div(ins_barrier, vec_chunk_size); + SubQueryResult final_result(num_queries, topK, metric_type); for (int chunk_id = max_indexed_id; chunk_id < max_chunk; ++chunk_id) { - std::vector<int64_t> buf_uids(total_count, -1); - std::vector<float> buf_dis(total_count, std::numeric_limits<float>::max()); - auto& chunk = vec_ptr->get_chunk(chunk_id); auto element_begin = chunk_id * vec_chunk_size; auto element_end = std::min(ins_barrier, (chunk_id + 1) * vec_chunk_size); auto nsize = element_end - element_begin; auto bitmap_view = create_bitmap_view(bitmaps_opt, chunk_id); - BinarySearchBruteForce(query_dataset, chunk.data(), nsize, buf_dis.data(), buf_uids.data(), bitmap_view); + auto sub_result = BinarySearchBruteForce(query_dataset, chunk.data(), nsize, bitmap_view); // convert chunk uid to segment uid - for (auto& x : buf_uids) { + for (auto& x : sub_result.mutable_labels()) { if (x != -1) { x += chunk_id * vec_chunk_size; } } - - segcore::merge_into(num_queries, topK, final_dis.data(), final_uids.data(), buf_dis.data(), buf_uids.data()); + final_result.merge(sub_result); } - results.result_distances_ = std::move(final_dis); - results.internal_seg_offsets_ = std::move(final_uids); + results.result_distances_ = std::move(final_result.mutable_values()); + results.internal_seg_offsets_ = std::move(final_result.mutable_labels()); results.topK_ = topK; results.num_queries_ = num_queries; diff --git a/internal/core/src/query/Search.h b/internal/core/src/query/Search.h index a5334038ac570e50bd143cdc1d0731ffd2a1e009..f130b9aacae84e93e733b5436f359ff03f9b77f3 100644 --- a/internal/core/src/query/Search.h +++ b/internal/core/src/query/Search.h @@ -14,27 +14,29 @@ #include "segcore/SegmentSmallIndex.h" #include <deque> #include <boost/dynamic_bitset.hpp> +#include "query/SubQueryResult.h" namespace milvus::query { using BitmapChunk = boost::dynamic_bitset<>; using BitmapSimple = std::deque<BitmapChunk>; +// TODO: merge these two search into one // note: c++17 don't support optional ref Status -QueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, - const QueryInfo& info, - const float* query_data, - int64_t num_queries, - Timestamp timestamp, - std::optional<const BitmapSimple*> bitmap_opt, - QueryResult& results); +FloatSearch(const segcore::SegmentSmallIndex& segment, + const QueryInfo& info, + const float* query_data, + int64_t num_queries, + Timestamp timestamp, + std::optional<const BitmapSimple*> bitmap_opt, + QueryResult& results); Status -BinaryQueryBruteForceImpl(const segcore::SegmentSmallIndex& segment, - const query::QueryInfo& info, - const uint8_t* query_data, - int64_t num_queries, - Timestamp timestamp, - std::optional<const BitmapSimple*> bitmaps_opt, - QueryResult& results); +BinarySearch(const segcore::SegmentSmallIndex& segment, + const query::QueryInfo& info, + const uint8_t* query_data, + int64_t num_queries, + Timestamp timestamp, + std::optional<const BitmapSimple*> bitmaps_opt, + QueryResult& results); } // namespace milvus::query diff --git a/internal/core/src/query/BruteForceSearch.cpp b/internal/core/src/query/SearchBruteForce.cpp similarity index 62% rename from internal/core/src/query/BruteForceSearch.cpp rename to internal/core/src/query/SearchBruteForce.cpp index de9e0143b1861895204405c55e93b3ae3bce99d4..4d46a1ee12038a7af90208b2e4a0554f48690680 100644 --- a/internal/core/src/query/BruteForceSearch.cpp +++ b/internal/core/src/query/SearchBruteForce.cpp @@ -9,58 +9,16 @@ // 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 -#include "BruteForceSearch.h" +#include "SearchBruteForce.h" #include <vector> #include <common/Types.h> #include <boost/dynamic_bitset.hpp> #include <queue> +#include "SubQueryResult.h" namespace milvus::query { -void -BinarySearchBruteForceNaive(MetricType metric_type, - int64_t code_size, - const uint8_t* binary_chunk, - int64_t chunk_size, - int64_t topk, - int64_t num_queries, - const uint8_t* query_data, - float* result_distances, - idx_t* result_labels, - faiss::ConcurrentBitsetPtr bitset) { - // THIS IS A NAIVE IMPLEMENTATION, ready for optimize - Assert(metric_type == faiss::METRIC_Jaccard); - Assert(code_size % 4 == 0); - - using T = std::tuple<float, int>; - - for (int64_t q = 0; q < num_queries; ++q) { - auto query_ptr = query_data + code_size * q; - auto query = boost::dynamic_bitset(query_ptr, query_ptr + code_size); - std::vector<T> max_heap(topk + 1, std::make_tuple(std::numeric_limits<float>::max(), -1)); - - for (int64_t i = 0; i < chunk_size; ++i) { - auto element_ptr = binary_chunk + code_size * i; - auto element = boost::dynamic_bitset(element_ptr, element_ptr + code_size); - auto the_and = (query & element).count(); - auto the_or = (query | element).count(); - auto distance = the_or ? (float)(the_or - the_and) / the_or : 0; - if (distance < std::get<0>(max_heap[0])) { - max_heap[topk] = std::make_tuple(distance, i); - std::push_heap(max_heap.begin(), max_heap.end()); - std::pop_heap(max_heap.begin(), max_heap.end()); - } - } - std::sort(max_heap.begin(), max_heap.end()); - for (int k = 0; k < topk; ++k) { - auto info = max_heap[k]; - result_distances[k + q * topk] = std::get<0>(info); - result_labels[k + q * topk] = std::get<1>(info); - } - } -} - -void +SubQueryResult BinarySearchBruteForceFast(MetricType metric_type, int64_t code_size, const uint8_t* binary_chunk, @@ -68,9 +26,11 @@ BinarySearchBruteForceFast(MetricType metric_type, int64_t topk, int64_t num_queries, const uint8_t* query_data, - float* result_distances, - idx_t* result_labels, - faiss::ConcurrentBitsetPtr bitset) { + const faiss::BitsetView& bitset) { + SubQueryResult sub_result(num_queries, topk, metric_type); + float* result_distances = sub_result.get_values(); + idx_t* result_labels = sub_result.get_labels(); + const idx_t block_size = chunk_size; bool use_heap = true; @@ -132,18 +92,26 @@ BinarySearchBruteForceFast(MetricType metric_type, } else { PanicInfo("Unsupported metric type"); } + return sub_result; } void +FloatSearchBruteForceFast(MetricType metric_type, + const float* chunk_data, + int64_t chunk_size, + float* result_distances, + idx_t* result_labels, + const faiss::BitsetView& bitset) { + // TODO +} + +SubQueryResult BinarySearchBruteForce(const dataset::BinaryQueryDataset& query_dataset, const uint8_t* binary_chunk, int64_t chunk_size, - float* result_distances, - idx_t* result_labels, - faiss::ConcurrentBitsetPtr bitset) { + const faiss::BitsetView& bitset) { // TODO: refactor the internal function - BinarySearchBruteForceFast(query_dataset.metric_type, query_dataset.code_size, binary_chunk, chunk_size, - query_dataset.topk, query_dataset.num_queries, query_dataset.query_data, - result_distances, result_labels, bitset); + return BinarySearchBruteForceFast(query_dataset.metric_type, query_dataset.code_size, binary_chunk, chunk_size, + query_dataset.topk, query_dataset.num_queries, query_dataset.query_data, bitset); } } // namespace milvus::query diff --git a/internal/core/src/query/BruteForceSearch.h b/internal/core/src/query/SearchBruteForce.h similarity index 87% rename from internal/core/src/query/BruteForceSearch.h rename to internal/core/src/query/SearchBruteForce.h index 4d9cba96df3b84579d4a0c18c74b6a25cdba3546..d8114e19d6a13d8c48a57e8c34cd42f4ce68575e 100644 --- a/internal/core/src/query/BruteForceSearch.h +++ b/internal/core/src/query/SearchBruteForce.h @@ -13,6 +13,7 @@ #include <faiss/utils/BinaryDistance.h> #include "segcore/ConcurrentVector.h" #include "common/Schema.h" +#include "query/SubQueryResult.h" namespace milvus::query { using MetricType = faiss::MetricType; @@ -28,12 +29,10 @@ struct BinaryQueryDataset { } // namespace dataset -void +SubQueryResult BinarySearchBruteForce(const dataset::BinaryQueryDataset& query_dataset, const uint8_t* binary_chunk, int64_t chunk_size, - float* result_distances, - idx_t* result_labels, - faiss::ConcurrentBitsetPtr bitset = nullptr); + const faiss::BitsetView& bitset = nullptr); } // namespace milvus::query diff --git a/internal/core/src/query/SubQueryResult.cpp b/internal/core/src/query/SubQueryResult.cpp new file mode 100644 index 0000000000000000000000000000000000000000..660d7b44f2cf2f11103972221b1a0ef1438cd5bc --- /dev/null +++ b/internal/core/src/query/SubQueryResult.cpp @@ -0,0 +1,77 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// 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 + +#include "utils/EasyAssert.h" +#include "query/SubQueryResult.h" +#include "segcore/Reduce.h" + +namespace milvus::query { + +template <bool is_desc> +void +SubQueryResult::merge_impl(const SubQueryResult& right) { + Assert(num_queries_ == right.num_queries_); + Assert(topk_ == right.topk_); + Assert(metric_type_ == right.metric_type_); + Assert(is_desc == is_descending(metric_type_)); + + for (int64_t qn = 0; qn < num_queries_; ++qn) { + auto offset = qn * topk_; + + int64_t* __restrict__ left_labels = this->get_labels() + offset; + float* __restrict__ left_values = this->get_values() + offset; + + auto right_labels = right.get_labels() + offset; + auto right_values = right.get_values() + offset; + + std::vector<float> buf_values(topk_); + std::vector<int64_t> buf_labels(topk_); + + auto lit = 0; // left iter + auto rit = 0; // right iter + + for (auto buf_iter = 0; buf_iter < topk_; ++buf_iter) { + auto left_v = left_values[lit]; + auto right_v = right_values[rit]; + // optimize out at compiling + if (is_desc ? (left_v >= right_v) : (left_v <= right_v)) { + buf_values[buf_iter] = left_values[lit]; + buf_labels[buf_iter] = left_labels[lit]; + ++lit; + } else { + buf_values[buf_iter] = right_values[rit]; + buf_labels[buf_iter] = right_labels[rit]; + ++rit; + } + } + std::copy_n(buf_values.data(), topk_, left_values); + std::copy_n(buf_labels.data(), topk_, left_labels); + } +} + +void +SubQueryResult::merge(const SubQueryResult& sub_result) { + Assert(metric_type_ == sub_result.metric_type_); + if (is_descending(metric_type_)) { + this->merge_impl<true>(sub_result); + } else { + this->merge_impl<false>(sub_result); + } +} + +SubQueryResult +SubQueryResult::merge(const SubQueryResult& left, const SubQueryResult& right) { + auto left_copy = left; + left_copy.merge(right); + return left_copy; +} + +} // namespace milvus::query diff --git a/internal/core/src/query/SubQueryResult.h b/internal/core/src/query/SubQueryResult.h new file mode 100644 index 0000000000000000000000000000000000000000..6cf7aace5850cff86633218997316d4563cd791c --- /dev/null +++ b/internal/core/src/query/SubQueryResult.h @@ -0,0 +1,98 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// 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 + +#pragma once +#include "common/Types.h" +#include <limits> +#include <vector> +namespace milvus::query { + +class SubQueryResult { + public: + SubQueryResult(int64_t num_queries, int64_t topk, MetricType metric_type) + : metric_type_(metric_type), + num_queries_(num_queries), + topk_(topk), + labels_(num_queries * topk, -1), + values_(num_queries * topk, init_value(metric_type)) { + } + + public: + static constexpr float + init_value(MetricType metric_type) { + return (is_descending(metric_type) ? -1 : 1) * std::numeric_limits<float>::max(); + } + + static constexpr bool + is_descending(MetricType metric_type) { + // TODO + if (metric_type == MetricType::METRIC_INNER_PRODUCT) { + return true; + } else { + return false; + } + } + + public: + int64_t + get_num_queries() const { + return num_queries_; + } + int64_t + get_topk() const { + return topk_; + } + + const int64_t* + get_labels() const { + return labels_.data(); + } + int64_t* + get_labels() { + return labels_.data(); + } + const float* + get_values() const { + return values_.data(); + } + float* + get_values() { + return values_.data(); + } + auto& + mutable_labels() { + return labels_; + } + auto& + mutable_values() { + return values_; + } + + static SubQueryResult + merge(const SubQueryResult& left, const SubQueryResult& right); + + void + merge(const SubQueryResult& sub_result); + + private: + template <bool is_desc> + void + merge_impl(const SubQueryResult& sub_result); + + private: + int64_t num_queries_; + int64_t topk_; + MetricType metric_type_; + std::vector<int64_t> labels_; + std::vector<float> values_; +}; + +} // namespace milvus::query diff --git a/internal/core/src/query/generated/ExecExprVisitor.h b/internal/core/src/query/generated/ExecExprVisitor.h index 250d68a6e567a52f9cf9c6bc8c7c886abf12f3f3..a9e0574a6e527a6f8fe5856ec640f15072824390 100644 --- a/internal/core/src/query/generated/ExecExprVisitor.h +++ b/internal/core/src/query/generated/ExecExprVisitor.h @@ -21,7 +21,7 @@ #include "ExprVisitor.h" namespace milvus::query { -class ExecExprVisitor : ExprVisitor { +class ExecExprVisitor : public ExprVisitor { public: void visit(BoolUnaryExpr& expr) override; diff --git a/internal/core/src/query/generated/ExecPlanNodeVisitor.h b/internal/core/src/query/generated/ExecPlanNodeVisitor.h index 0eb33384d71eec5e01a486014c27c1049e442c46..c026c689857958c27b44daf2c160b51592b26c44 100644 --- a/internal/core/src/query/generated/ExecPlanNodeVisitor.h +++ b/internal/core/src/query/generated/ExecPlanNodeVisitor.h @@ -19,7 +19,7 @@ #include "PlanNodeVisitor.h" namespace milvus::query { -class ExecPlanNodeVisitor : PlanNodeVisitor { +class ExecPlanNodeVisitor : public PlanNodeVisitor { public: void visit(FloatVectorANNS& node) override; diff --git a/internal/core/src/query/generated/ShowExprVisitor.h b/internal/core/src/query/generated/ShowExprVisitor.h index 55659e24c04e4a419a97b50ec39cfaffb1bcb558..6a1ed2646fc641b7670a0c7f100da9ed8408dc06 100644 --- a/internal/core/src/query/generated/ShowExprVisitor.h +++ b/internal/core/src/query/generated/ShowExprVisitor.h @@ -19,7 +19,7 @@ #include "ExprVisitor.h" namespace milvus::query { -class ShowExprVisitor : ExprVisitor { +class ShowExprVisitor : public ExprVisitor { public: void visit(BoolUnaryExpr& expr) override; diff --git a/internal/core/src/query/generated/ShowPlanNodeVisitor.h b/internal/core/src/query/generated/ShowPlanNodeVisitor.h index b921ec81fc5aa3eb29eb15c091a72738cfc57d4b..c518c3f7d0b23204f804c035db3471bcf08c4831 100644 --- a/internal/core/src/query/generated/ShowPlanNodeVisitor.h +++ b/internal/core/src/query/generated/ShowPlanNodeVisitor.h @@ -20,7 +20,7 @@ #include "PlanNodeVisitor.h" namespace milvus::query { -class ShowPlanNodeVisitor : PlanNodeVisitor { +class ShowPlanNodeVisitor : public PlanNodeVisitor { public: void visit(FloatVectorANNS& node) override; diff --git a/internal/core/src/query/generated/VerifyExprVisitor.cpp b/internal/core/src/query/generated/VerifyExprVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44af4dde81bdeea864b8bef34d064e4fbd4f2fee --- /dev/null +++ b/internal/core/src/query/generated/VerifyExprVisitor.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// 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 + +#error TODO: copy this file out, and modify the content. +#include "query/generated/VerifyExprVisitor.h" + +namespace milvus::query { +void +VerifyExprVisitor::visit(BoolUnaryExpr& expr) { + // TODO +} + +void +VerifyExprVisitor::visit(BoolBinaryExpr& expr) { + // TODO +} + +void +VerifyExprVisitor::visit(TermExpr& expr) { + // TODO +} + +void +VerifyExprVisitor::visit(RangeExpr& expr) { + // TODO +} + +} // namespace milvus::query diff --git a/internal/core/src/query/generated/VerifyExprVisitor.h b/internal/core/src/query/generated/VerifyExprVisitor.h new file mode 100644 index 0000000000000000000000000000000000000000..6b04a76978d7db2c8247ac45399b50b85f44309d --- /dev/null +++ b/internal/core/src/query/generated/VerifyExprVisitor.h @@ -0,0 +1,40 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// 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 + +#pragma once +// Generated File +// DO NOT EDIT +#include <optional> +#include <boost/dynamic_bitset.hpp> +#include <utility> +#include <deque> +#include "segcore/SegmentSmallIndex.h" +#include "query/ExprImpl.h" +#include "ExprVisitor.h" + +namespace milvus::query { +class VerifyExprVisitor : public ExprVisitor { + public: + void + visit(BoolUnaryExpr& expr) override; + + void + visit(BoolBinaryExpr& expr) override; + + void + visit(TermExpr& expr) override; + + void + visit(RangeExpr& expr) override; + + public: +}; +} // namespace milvus::query diff --git a/internal/core/src/query/generated/VerifyPlanNodeVisitor.cpp b/internal/core/src/query/generated/VerifyPlanNodeVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7b0656f57041427e8159e899f891ca0f391c901 --- /dev/null +++ b/internal/core/src/query/generated/VerifyPlanNodeVisitor.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// 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 + +#error TODO: copy this file out, and modify the content. +#include "query/generated/VerifyPlanNodeVisitor.h" + +namespace milvus::query { +void +VerifyPlanNodeVisitor::visit(FloatVectorANNS& node) { + // TODO +} + +void +VerifyPlanNodeVisitor::visit(BinaryVectorANNS& node) { + // TODO +} + +} // namespace milvus::query diff --git a/internal/core/src/query/generated/VerifyPlanNodeVisitor.h b/internal/core/src/query/generated/VerifyPlanNodeVisitor.h new file mode 100644 index 0000000000000000000000000000000000000000..a964e6c08f920bcf3b2b1f4e7f70ebbddb0e264a --- /dev/null +++ b/internal/core/src/query/generated/VerifyPlanNodeVisitor.h @@ -0,0 +1,37 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// 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 + +#pragma once +// Generated File +// DO NOT EDIT +#include "utils/Json.h" +#include "query/PlanImpl.h" +#include "segcore/SegmentBase.h" +#include <utility> +#include "PlanNodeVisitor.h" + +namespace milvus::query { +class VerifyPlanNodeVisitor : public PlanNodeVisitor { + public: + void + visit(FloatVectorANNS& node) override; + + void + visit(BinaryVectorANNS& node) override; + + public: + using RetType = QueryResult; + VerifyPlanNodeVisitor() = default; + + private: + std::optional<RetType> ret_; +}; +} // namespace milvus::query diff --git a/internal/core/src/query/visitors/ExecPlanNodeVisitor.cpp b/internal/core/src/query/visitors/ExecPlanNodeVisitor.cpp index 6409f248ac89750704c3fb448775e9f5508e379f..c7cb08a247edd2d8edfbce95625db37a71a66958 100644 --- a/internal/core/src/query/visitors/ExecPlanNodeVisitor.cpp +++ b/internal/core/src/query/visitors/ExecPlanNodeVisitor.cpp @@ -79,7 +79,7 @@ ExecPlanNodeVisitor::visit(FloatVectorANNS& node) { SearchOnSealed(segment->get_schema(), sealed_indexing, node.query_info_, src_data, num_queries, timestamp_, bitset_pack, ret); } else { - QueryBruteForceImpl(*segment, node.query_info_, src_data, num_queries, timestamp_, bitset_pack, ret); + FloatSearch(*segment, node.query_info_, src_data, num_queries, timestamp_, bitset_pack, ret); } ret_ = ret; @@ -104,7 +104,7 @@ ExecPlanNodeVisitor::visit(BinaryVectorANNS& node) { bitset_pack = &bitmap_holder; } - BinaryQueryBruteForceImpl(*segment, node.query_info_, src_data, num_queries, timestamp_, bitset_pack, ret); + BinarySearch(*segment, node.query_info_, src_data, num_queries, timestamp_, bitset_pack, ret); ret_ = ret; } diff --git a/internal/core/src/query/visitors/ShowExprVisitor.cpp b/internal/core/src/query/visitors/ShowExprVisitor.cpp index 79ae412c7a140c1041d0ad3b727b8f4dfc746a95..c9c52718546bfd9a209bf29ac84af3d8cd8af181 100644 --- a/internal/core/src/query/visitors/ShowExprVisitor.cpp +++ b/internal/core/src/query/visitors/ShowExprVisitor.cpp @@ -112,7 +112,7 @@ TermExtract(const TermExpr& expr_raw) { void ShowExprVisitor::visit(TermExpr& expr) { Assert(!ret_.has_value()); - Assert(field_is_vector(expr.data_type_) == false); + Assert(datatype_is_vector(expr.data_type_) == false); auto terms = [&] { switch (expr.data_type_) { case DataType::BOOL: @@ -161,7 +161,7 @@ ConditionExtract(const RangeExpr& expr_raw) { void ShowExprVisitor::visit(RangeExpr& expr) { Assert(!ret_.has_value()); - Assert(field_is_vector(expr.data_type_) == false); + Assert(datatype_is_vector(expr.data_type_) == false); auto conditions = [&] { switch (expr.data_type_) { case DataType::BOOL: diff --git a/internal/core/src/query/visitors/VerifyExprVisitor.cpp b/internal/core/src/query/visitors/VerifyExprVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b3326c74a60b9f604de92a24fb71c4156a60cad --- /dev/null +++ b/internal/core/src/query/visitors/VerifyExprVisitor.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// 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 + +#include "query/generated/VerifyExprVisitor.h" + +namespace milvus::query { +void +VerifyExprVisitor::visit(BoolUnaryExpr& expr) { + // TODO +} + +void +VerifyExprVisitor::visit(BoolBinaryExpr& expr) { + // TODO +} + +void +VerifyExprVisitor::visit(TermExpr& expr) { + // TODO +} + +void +VerifyExprVisitor::visit(RangeExpr& expr) { + // TODO +} + +} // namespace milvus::query diff --git a/internal/core/src/query/visitors/VerifyPlanNodeVisitor.cpp b/internal/core/src/query/visitors/VerifyPlanNodeVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..263390a39a52831c0eb7f1bd71f8dc0cc1cecdb5 --- /dev/null +++ b/internal/core/src/query/visitors/VerifyPlanNodeVisitor.cpp @@ -0,0 +1,85 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// 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 + +#include "query/generated/VerifyPlanNodeVisitor.h" +#include "knowhere/index/vector_index/ConfAdapterMgr.h" +#include "segcore/SegmentSmallIndex.h" +#include "knowhere/index/vector_index/ConfAdapter.h" +#include "knowhere/index/vector_index/helpers/IndexParameter.h" + +namespace milvus::query { + +#if 1 +namespace impl { +// THIS CONTAINS EXTRA BODY FOR VISITOR +// WILL BE USED BY GENERATOR UNDER suvlim/core_gen/ +class VerifyPlanNodeVisitor : PlanNodeVisitor { + public: + using RetType = QueryResult; + VerifyPlanNodeVisitor() = default; + + private: + std::optional<RetType> ret_; +}; +} // namespace impl +#endif + +static knowhere::IndexType +InferIndexType(const Json& search_params) { + // ivf -> nprobe + // nsg -> search_length + // hnsw/rhnsw/*pq/*sq -> ef + // annoy -> search_k + // ngtpanng / ngtonng -> max_search_edges / epsilon + static const std::map<std::string, knowhere::IndexType> key_list = [] { + std::map<std::string, knowhere::IndexType> list; + namespace ip = knowhere::IndexParams; + namespace ie = knowhere::IndexEnum; + list.emplace(ip::nprobe, ie::INDEX_FAISS_IVFFLAT); + list.emplace(ip::search_length, ie::INDEX_NSG); + list.emplace(ip::ef, ie::INDEX_HNSW); + list.emplace(ip::search_k, ie::INDEX_ANNOY); + list.emplace(ip::max_search_edges, ie::INDEX_NGTONNG); + list.emplace(ip::epsilon, ie::INDEX_NGTONNG); + return list; + }(); + auto dbg_str = search_params.dump(); + for (auto& kv : search_params.items()) { + std::string key = kv.key(); + if (key_list.count(key)) { + return key_list.at(key); + } + } + PanicInfo("failed to infer index type"); +} + +void +VerifyPlanNodeVisitor::visit(FloatVectorANNS& node) { + auto& search_params = node.query_info_.search_params_; + auto inferred_type = InferIndexType(search_params); + auto adapter = knowhere::AdapterMgr::GetInstance().GetAdapter(inferred_type); + auto index_mode = knowhere::IndexMode::MODE_CPU; + + // mock the api, topk will be passed from placeholder + auto params_copy = search_params; + params_copy[knowhere::meta::TOPK] = 10; + + // NOTE: the second parameter is not checked in knowhere, may be redundant + auto passed = adapter->CheckSearch(params_copy, inferred_type, index_mode); + AssertInfo(passed, "invalid search params"); +} + +void +VerifyPlanNodeVisitor::visit(BinaryVectorANNS& node) { + // TODO +} + +} // namespace milvus::query diff --git a/internal/core/src/segcore/CMakeLists.txt b/internal/core/src/segcore/CMakeLists.txt index 709a983c977aceacc0c049b070887044701840f6..1a011c984b690d92e62dc6bb172aa245cbfd140f 100644 --- a/internal/core/src/segcore/CMakeLists.txt +++ b/internal/core/src/segcore/CMakeLists.txt @@ -24,5 +24,6 @@ target_link_libraries(milvus_segcore dl backtrace milvus_common milvus_query + milvus_utils ) diff --git a/internal/core/src/segcore/SegmentBase.h b/internal/core/src/segcore/SegmentBase.h index 57918d96a1203c6565be5446a22f457ea7d05f04..a7d427d66517e3ea10451e1c70d5130b9acbb33e 100644 --- a/internal/core/src/segcore/SegmentBase.h +++ b/internal/core/src/segcore/SegmentBase.h @@ -13,7 +13,7 @@ #include <vector> #include "IndexMeta.h" -#include "utils/Types.h" +#include "common/Types.h" #include "common/Schema.h" #include <memory> diff --git a/internal/core/src/segcore/SegmentNaive.cpp b/internal/core/src/segcore/SegmentNaive.cpp index f20c0ce4ab5d4e1e9b4c606708d4b7f634775786..b513247c7bc4c01e9d2a00080668c0fc4caf7784 100644 --- a/internal/core/src/segcore/SegmentNaive.cpp +++ b/internal/core/src/segcore/SegmentNaive.cpp @@ -274,19 +274,14 @@ SegmentNaive::QueryImpl(query::QueryDeprecatedPtr query_info, Timestamp timestam auto distances = final->Get<float*>(knowhere::meta::DISTANCE); auto total_num = num_queries * topK; - result.result_ids_.resize(total_num); result.result_distances_.resize(total_num); result.num_queries_ = num_queries; result.topK_ = topK; - std::copy_n(ids, total_num, result.result_ids_.data()); + std::copy_n(ids, total_num, result.internal_seg_offsets_.data()); std::copy_n(distances, total_num, result.result_distances_.data()); - for (auto& id : result.result_ids_) { - id = record_.uids_[id]; - } - return Status::OK(); } @@ -347,7 +342,7 @@ SegmentNaive::QuerySlowImpl(query::QueryDeprecatedPtr query_info, Timestamp time result.topK_ = topK; auto row_num = topK * num_queries; - result.result_ids_.resize(row_num); + result.internal_seg_offsets_.resize(row_num); result.result_distances_.resize(row_num); for (int q_id = 0; q_id < num_queries; ++q_id) { @@ -356,7 +351,7 @@ SegmentNaive::QuerySlowImpl(query::QueryDeprecatedPtr query_info, Timestamp time auto dst_id = topK - 1 - i + q_id * topK; auto [dis, offset] = records[q_id].top(); records[q_id].pop(); - result.result_ids_[dst_id] = record_.uids_[offset]; + result.internal_seg_offsets_[dst_id] = offset; result.result_distances_[dst_id] = dis; } } diff --git a/internal/core/src/segcore/SegmentSmallIndex.cpp b/internal/core/src/segcore/SegmentSmallIndex.cpp index a6eccb9440440062a17d83956e68d876f200540a..fa7b0a3164cf0f4f17a785f8d1d181c8be88af6f 100644 --- a/internal/core/src/segcore/SegmentSmallIndex.cpp +++ b/internal/core/src/segcore/SegmentSmallIndex.cpp @@ -349,19 +349,12 @@ SegmentSmallIndex::FillTargetEntry(const query::Plan* plan, QueryResult& results Assert(results.result_offsets_.size() == size); Assert(results.row_data_.size() == 0); - // TODO: deprecate - results.result_ids_.clear(); - results.result_ids_.resize(size); - if (plan->schema_.get_is_auto_id()) { auto& uids = record_.uids_; for (int64_t i = 0; i < size; ++i) { auto seg_offset = results.internal_seg_offsets_[i]; auto row_id = seg_offset == -1 ? -1 : uids[seg_offset]; - // TODO: deprecate - results.result_ids_[i] = row_id; - std::vector<char> blob(sizeof(row_id)); memcpy(blob.data(), &row_id, sizeof(row_id)); results.row_data_.emplace_back(std::move(blob)); @@ -377,9 +370,6 @@ SegmentSmallIndex::FillTargetEntry(const query::Plan* plan, QueryResult& results auto seg_offset = results.internal_seg_offsets_[i]; auto row_id = seg_offset == -1 ? -1 : uids->operator[](seg_offset); - // TODO: deprecate - results.result_ids_[i] = row_id; - std::vector<char> blob(sizeof(row_id)); memcpy(blob.data(), &row_id, sizeof(row_id)); results.row_data_.emplace_back(std::move(blob)); diff --git a/internal/core/src/segcore/reduce_c.cpp b/internal/core/src/segcore/reduce_c.cpp index 68af9246497b11dab63e56a121f877f6a8e35da3..cbd88a13b9b0209cbeb8d247857fc9b3a9c847d9 100644 --- a/internal/core/src/segcore/reduce_c.cpp +++ b/internal/core/src/segcore/reduce_c.cpp @@ -14,10 +14,10 @@ #include "segcore/reduce_c.h" #include "segcore/Reduce.h" -#include "utils/Types.h" +#include "common/Types.h" #include "pb/service_msg.pb.h" -using SearchResult = milvus::engine::QueryResult; +using SearchResult = milvus::QueryResult; int MergeInto(int64_t num_queries, int64_t topk, float* distances, int64_t* uids, float* new_distances, int64_t* new_uids) { diff --git a/internal/core/src/segcore/segment_c.cpp b/internal/core/src/segcore/segment_c.cpp index 2bed416f9397cb3f8e86acb2af7f56d8f8c4fa1f..c38b3921be18f2e8240320389b43d31ec56ad75d 100644 --- a/internal/core/src/segcore/segment_c.cpp +++ b/internal/core/src/segcore/segment_c.cpp @@ -165,7 +165,7 @@ CStatus FillTargetEntry(CSegmentBase c_segment, CPlan c_plan, CQueryResult c_result) { auto segment = (milvus::segcore::SegmentBase*)c_segment; auto plan = (milvus::query::Plan*)c_plan; - auto result = (milvus::engine::QueryResult*)c_result; + auto result = (milvus::QueryResult*)c_result; auto status = CStatus(); try { diff --git a/internal/core/src/utils/Types.h b/internal/core/src/utils/Types.h index 33643f32580541180cbc87c865dfd7c6ec5852cc..4e404c8035c3521a71d054edcce48ec5b50438e5 100644 --- a/internal/core/src/utils/Types.h +++ b/internal/core/src/utils/Types.h @@ -136,45 +136,5 @@ struct AttrsData { IDNumbers id_array_; }; -/////////////////////////////////////////////////////////////////////////////////////////////////// -struct QueryResult { - QueryResult() = default; - QueryResult(uint64_t num_queries, uint64_t topK) : topK_(topK), num_queries_(num_queries) { - auto count = get_row_count(); - result_distances_.resize(count); - internal_seg_offsets_.resize(count); - - // TODO: deprecated - result_ids_.resize(count); - } - - [[nodiscard]] uint64_t - get_row_count() const { - return topK_ * num_queries_; - } - - uint64_t num_queries_; - uint64_t topK_; - // uint64_t total_row_count_; // total_row_count_ = topK * num_queries_ - - // vector<tuple<Score, SegId, Offset>> data_reduced; - - // vector<tuple<Score, SegId, Offset, RawData>> - - // map<SegId, vector<tuple<DataOffset, ResLoc>>> - uint64_t seg_id_; - std::vector<float> result_distances_; - - // TODO(gexi): utilize these field - std::vector<int64_t> internal_seg_offsets_; - std::vector<int64_t> result_offsets_; - std::vector<std::vector<char>> row_data_; - - // TODO: deprecated, use row_data directly - std::vector<idx_t> result_ids_; -}; - -using QueryResultPtr = std::shared_ptr<QueryResult>; - } // namespace engine } // namespace milvus diff --git a/internal/core/unittest/CMakeLists.txt b/internal/core/unittest/CMakeLists.txt index a69690728065e26d373ed961c0fd4ccd4817e10b..ce3f8ab702d9a08c64fcba79d77af77a728cc960 100644 --- a/internal/core/unittest/CMakeLists.txt +++ b/internal/core/unittest/CMakeLists.txt @@ -14,6 +14,7 @@ set(MILVUS_TEST_FILES test_binary.cpp test_index_wrapper.cpp test_sealed.cpp + test_reduce.cpp ) add_executable(all_tests ${MILVUS_TEST_FILES} @@ -24,10 +25,8 @@ target_link_libraries(all_tests gtest_main milvus_segcore milvus_indexbuilder - knowhere log pthread - milvus_utils ) install (TARGETS all_tests DESTINATION unittest) diff --git a/internal/core/unittest/test_c_api.cpp b/internal/core/unittest/test_c_api.cpp index 0c5dbe83a8ca657cfef829b5767577312124f416..65866a60b7f39538dcb5ef2cba8a872f23da8689 100644 --- a/internal/core/unittest/test_c_api.cpp +++ b/internal/core/unittest/test_c_api.cpp @@ -137,7 +137,7 @@ TEST(CApiTest, SearchTest) { auto offset = PreInsert(segment, N); auto ins_res = Insert(segment, offset, N, uids.data(), timestamps.data(), raw_data.data(), (int)line_sizeof, N); - assert(ins_res.error_code == Success); + ASSERT_EQ(ins_res.error_code, Success); const char* dsl_string = R"( { @@ -176,11 +176,11 @@ TEST(CApiTest, SearchTest) { void* plan = nullptr; auto status = CreatePlan(collection, dsl_string, &plan); - assert(status.error_code == Success); + ASSERT_EQ(status.error_code, Success); void* placeholderGroup = nullptr; status = ParsePlaceholderGroup(plan, blob.data(), blob.length(), &placeholderGroup); - assert(status.error_code == Success); + ASSERT_EQ(status.error_code, Success); std::vector<CPlaceholderGroup> placeholderGroups; placeholderGroups.push_back(placeholderGroup); @@ -189,7 +189,7 @@ TEST(CApiTest, SearchTest) { CQueryResult search_result; auto res = Search(segment, plan, placeholderGroups.data(), timestamps.data(), 1, &search_result); - assert(res.error_code == Success); + ASSERT_EQ(res.error_code, Success); DeletePlan(plan); DeletePlaceholderGroup(placeholderGroup); diff --git a/internal/core/unittest/test_index_wrapper.cpp b/internal/core/unittest/test_index_wrapper.cpp index a885c837a096b9558da69ec74c73d2c9c019e510..bd335951f8053029f720e368d7907cb0d65d451d 100644 --- a/internal/core/unittest/test_index_wrapper.cpp +++ b/internal/core/unittest/test_index_wrapper.cpp @@ -11,6 +11,8 @@ #include <tuple> #include <map> +#include <limits> +#include <math.h> #include <gtest/gtest.h> #include <google/protobuf/text_format.h> @@ -41,16 +43,16 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh if (index_type == milvus::knowhere::IndexEnum::INDEX_FAISS_IDMAP) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, K}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::Metric::TYPE, metric_type}, {milvus::knowhere::INDEX_FILE_SLICE_SIZE_IN_MEGABYTE, 4}, }; } else if (index_type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFPQ) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, K}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::IndexParams::nlist, 100}, - // {milvus::knowhere::IndexParams::nprobe, 4}, + {milvus::knowhere::IndexParams::nprobe, 4}, {milvus::knowhere::IndexParams::m, 4}, {milvus::knowhere::IndexParams::nbits, 8}, {milvus::knowhere::Metric::TYPE, metric_type}, @@ -59,9 +61,9 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh } else if (index_type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, K}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::IndexParams::nlist, 100}, - // {milvus::knowhere::IndexParams::nprobe, 4}, + {milvus::knowhere::IndexParams::nprobe, 4}, {milvus::knowhere::Metric::TYPE, metric_type}, {milvus::knowhere::INDEX_FILE_SLICE_SIZE_IN_MEGABYTE, 4}, #ifdef MILVUS_GPU_VERSION @@ -71,9 +73,9 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh } else if (index_type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, K}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::IndexParams::nlist, 100}, - // {milvus::knowhere::IndexParams::nprobe, 4}, + {milvus::knowhere::IndexParams::nprobe, 4}, {milvus::knowhere::IndexParams::nbits, 8}, {milvus::knowhere::Metric::TYPE, metric_type}, {milvus::knowhere::INDEX_FILE_SLICE_SIZE_IN_MEGABYTE, 4}, @@ -84,9 +86,9 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh } else if (index_type == milvus::knowhere::IndexEnum::INDEX_FAISS_BIN_IVFFLAT) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, K}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::IndexParams::nlist, 100}, - // {milvus::knowhere::IndexParams::nprobe, 4}, + {milvus::knowhere::IndexParams::nprobe, 4}, {milvus::knowhere::IndexParams::m, 4}, {milvus::knowhere::IndexParams::nbits, 8}, {milvus::knowhere::Metric::TYPE, metric_type}, @@ -95,13 +97,14 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh } else if (index_type == milvus::knowhere::IndexEnum::INDEX_FAISS_BIN_IDMAP) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, K}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::Metric::TYPE, metric_type}, }; } else if (index_type == milvus::knowhere::IndexEnum::INDEX_NSG) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, {milvus::knowhere::IndexParams::nlist, 163}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::IndexParams::nprobe, 8}, {milvus::knowhere::IndexParams::knng, 20}, {milvus::knowhere::IndexParams::search_length, 40}, @@ -127,17 +130,14 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh #endif } else if (index_type == milvus::knowhere::IndexEnum::INDEX_HNSW) { return milvus::knowhere::Config{ - {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, 10}, - {milvus::knowhere::IndexParams::M, 16}, - {milvus::knowhere::IndexParams::efConstruction, 200}, - {milvus::knowhere::IndexParams::ef, 200}, - {milvus::knowhere::Metric::TYPE, metric_type}, + {milvus::knowhere::meta::DIM, DIM}, {milvus::knowhere::meta::TOPK, K}, + {milvus::knowhere::IndexParams::M, 16}, {milvus::knowhere::IndexParams::efConstruction, 200}, + {milvus::knowhere::IndexParams::ef, 200}, {milvus::knowhere::Metric::TYPE, metric_type}, }; } else if (index_type == milvus::knowhere::IndexEnum::INDEX_ANNOY) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, 10}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::IndexParams::n_trees, 4}, {milvus::knowhere::IndexParams::search_k, 100}, {milvus::knowhere::Metric::TYPE, metric_type}, @@ -146,7 +146,7 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh } else if (index_type == milvus::knowhere::IndexEnum::INDEX_RHNSWFlat) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, 10}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::IndexParams::M, 16}, {milvus::knowhere::IndexParams::efConstruction, 200}, {milvus::knowhere::IndexParams::ef, 200}, @@ -156,7 +156,7 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh } else if (index_type == milvus::knowhere::IndexEnum::INDEX_RHNSWPQ) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, 10}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::IndexParams::M, 16}, {milvus::knowhere::IndexParams::efConstruction, 200}, {milvus::knowhere::IndexParams::ef, 200}, @@ -167,7 +167,7 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh } else if (index_type == milvus::knowhere::IndexEnum::INDEX_RHNSWSQ) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, 10}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::IndexParams::M, 16}, {milvus::knowhere::IndexParams::efConstruction, 200}, {milvus::knowhere::IndexParams::ef, 200}, @@ -177,7 +177,7 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh } else if (index_type == milvus::knowhere::IndexEnum::INDEX_NGTPANNG) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, 10}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::Metric::TYPE, metric_type}, {milvus::knowhere::IndexParams::edge_size, 10}, {milvus::knowhere::IndexParams::epsilon, 0.1}, @@ -189,7 +189,7 @@ generate_conf(const milvus::knowhere::IndexType& index_type, const milvus::knowh } else if (index_type == milvus::knowhere::IndexEnum::INDEX_NGTONNG) { return milvus::knowhere::Config{ {milvus::knowhere::meta::DIM, DIM}, - // {milvus::knowhere::meta::TOPK, 10}, + {milvus::knowhere::meta::TOPK, K}, {milvus::knowhere::Metric::TYPE, metric_type}, {milvus::knowhere::IndexParams::edge_size, 20}, {milvus::knowhere::IndexParams::epsilon, 0.1}, @@ -234,6 +234,99 @@ GenDataset(int64_t N, const milvus::knowhere::MetricType& metric_type, bool is_b return milvus::segcore::DataGen(schema, N); } } + +using QueryResultPtr = std::unique_ptr<milvus::indexbuilder::IndexWrapper::QueryResult>; +void +PrintQueryResult(const QueryResultPtr& result) { + auto nq = result->nq; + auto k = result->topk; + + std::stringstream ss_id; + std::stringstream ss_dist; + + for (auto i = 0; i < nq; i++) { + for (auto j = 0; j < k; ++j) { + ss_id << result->ids[i * k + j] << " "; + ss_dist << result->distances[i * k + j] << " "; + } + ss_id << std::endl; + ss_dist << std::endl; + } + std::cout << "id\n" << ss_id.str() << std::endl; + std::cout << "dist\n" << ss_dist.str() << std::endl; +} + +float +L2(const float* point_a, const float* point_b, int dim) { + float dis = 0; + for (auto i = 0; i < dim; i++) { + auto c_a = point_a[i]; + auto c_b = point_b[i]; + dis += pow(c_b - c_a, 2); + } + return dis; +} + +int hamming_weight(uint8_t n) { + int count=0; + while(n != 0){ + count += n&1; + n >>= 1; + } + return count; +} +float +Jaccard(const uint8_t* point_a, const uint8_t* point_b, int dim) { + float dis; + int len = dim / 8; + float intersection = 0; + float union_num = 0; + for (int i = 0; i < len; i++) { + intersection += hamming_weight(point_a[i] & point_b[i]); + union_num += hamming_weight(point_a[i] | point_b[i]); + } + dis = 1 - (intersection / union_num); + return dis; +} + +float +CountDistance(const void* point_a, + const void* point_b, + int dim, + const milvus::knowhere::MetricType& metric, + bool is_binary = false) { + if (point_a == nullptr || point_b == nullptr) { + return std::numeric_limits<float>::max(); + } + if (metric == milvus::knowhere::Metric::L2) { + return L2(static_cast<const float*>(point_a), static_cast<const float*>(point_b), dim); + } else if (metric == milvus::knowhere::Metric::JACCARD) { + return Jaccard(static_cast<const uint8_t*>(point_a), static_cast<const uint8_t*>(point_b), dim); + } else { + return std::numeric_limits<float>::max(); + } +} + +void +CheckDistances(const QueryResultPtr& result, + const milvus::knowhere::DatasetPtr& base_dataset, + const milvus::knowhere::DatasetPtr& query_dataset, + const milvus::knowhere::MetricType& metric, + const float threshold = 1.0e-5) { + auto base_vecs = base_dataset->Get<float*>(milvus::knowhere::meta::TENSOR); + auto query_vecs = query_dataset->Get<float*>(milvus::knowhere::meta::TENSOR); + auto dim = base_dataset->Get<int64_t>(milvus::knowhere::meta::DIM); + auto nq = result->nq; + auto k = result->topk; + for (auto i = 0; i < nq; i++) { + for (auto j = 0; j < k; ++j) { + auto dis = result->distances[i * k + j]; + auto id = result->ids[i * k + j]; + auto count_dis = CountDistance(query_vecs + i * dim, base_vecs + id * dim, dim, metric); + // assert(std::abs(dis - count_dis) < threshold); + } + } +} } // namespace using Param = std::pair<milvus::knowhere::IndexType, milvus::knowhere::MetricType>; @@ -247,8 +340,26 @@ class IndexWrapperTest : public ::testing::TestWithParam<Param> { metric_type = param.second; std::tie(type_params, index_params) = generate_params(index_type, metric_type); - std::map<std::string, bool> is_binary_map = {{milvus::knowhere::IndexEnum::INDEX_FAISS_IVFPQ, false}, - {milvus::knowhere::IndexEnum::INDEX_FAISS_BIN_IVFFLAT, true}}; + std::map<std::string, bool> is_binary_map = { + {milvus::knowhere::IndexEnum::INDEX_FAISS_IDMAP, false}, + {milvus::knowhere::IndexEnum::INDEX_FAISS_IVFPQ, false}, + {milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT, false}, + {milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8, false}, + {milvus::knowhere::IndexEnum::INDEX_FAISS_BIN_IVFFLAT, true}, + {milvus::knowhere::IndexEnum::INDEX_FAISS_BIN_IDMAP, true}, +#ifdef MILVUS_SUPPORT_SPTAG + {milvus::knowhere::IndexEnum::INDEX_SPTAG_KDT_RNT, false}, + {milvus::knowhere::IndexEnum::INDEX_SPTAG_BKT_RNT, false}, +#endif + {milvus::knowhere::IndexEnum::INDEX_HNSW, false}, + {milvus::knowhere::IndexEnum::INDEX_ANNOY, false}, + {milvus::knowhere::IndexEnum::INDEX_RHNSWFlat, false}, + {milvus::knowhere::IndexEnum::INDEX_RHNSWPQ, false}, + {milvus::knowhere::IndexEnum::INDEX_RHNSWSQ, false}, + {milvus::knowhere::IndexEnum::INDEX_NGTPANNG, false}, + {milvus::knowhere::IndexEnum::INDEX_NGTONNG, false}, + {milvus::knowhere::IndexEnum::INDEX_NSG, false}, + }; is_binary = is_binary_map[index_type]; @@ -262,9 +373,13 @@ class IndexWrapperTest : public ::testing::TestWithParam<Param> { if (!is_binary) { xb_data = dataset.get_col<float>(0); xb_dataset = milvus::knowhere::GenDataset(NB, DIM, xb_data.data()); + xq_data = dataset.get_col<float>(0); + xq_dataset = milvus::knowhere::GenDataset(NQ, DIM, xq_data.data()); } else { xb_bin_data = dataset.get_col<uint8_t>(0); xb_dataset = milvus::knowhere::GenDataset(NB, DIM, xb_bin_data.data()); + xq_bin_data = dataset.get_col<uint8_t>(0); + xq_dataset = milvus::knowhere::GenDataset(NQ, DIM, xq_bin_data.data()); } } @@ -282,6 +397,9 @@ class IndexWrapperTest : public ::testing::TestWithParam<Param> { std::vector<float> xb_data; std::vector<uint8_t> xb_bin_data; std::vector<milvus::knowhere::IDType> ids; + milvus::knowhere::DatasetPtr xq_dataset; + std::vector<float> xq_data; + std::vector<uint8_t> xq_bin_data; }; TEST(PQ, Build) { @@ -308,6 +426,47 @@ TEST(IVFFLATNM, Build) { ASSERT_NO_THROW(index->AddWithoutIds(xb_dataset, conf)); } +TEST(IVFFLATNM, Query) { + auto index_type = milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT; + auto metric_type = milvus::knowhere::Metric::L2; + auto conf = generate_conf(index_type, metric_type); + auto index = milvus::knowhere::VecIndexFactory::GetInstance().CreateVecIndex(index_type); + auto dataset = GenDataset(NB, metric_type, false); + auto xb_data = dataset.get_col<float>(0); + auto xb_dataset = milvus::knowhere::GenDataset(NB, DIM, xb_data.data()); + ASSERT_NO_THROW(index->Train(xb_dataset, conf)); + ASSERT_NO_THROW(index->AddWithoutIds(xb_dataset, conf)); + auto bs = index->Serialize(conf); + auto bptr = std::make_shared<milvus::knowhere::Binary>(); + bptr->data = std::shared_ptr<uint8_t[]>((uint8_t*)xb_data.data(), [&](uint8_t*) {}); + bptr->size = DIM * NB * sizeof(float); + bs.Append(RAW_DATA, bptr); + index->Load(bs); + auto xq_data = dataset.get_col<float>(0); + auto xq_dataset = milvus::knowhere::GenDataset(NQ, DIM, xq_data.data()); + auto result = index->Query(xq_dataset, conf, nullptr); +} + +TEST(NSG, Query) { + auto index_type = milvus::knowhere::IndexEnum::INDEX_NSG; + auto metric_type = milvus::knowhere::Metric::L2; + auto conf = generate_conf(index_type, metric_type); + auto index = milvus::knowhere::VecIndexFactory::GetInstance().CreateVecIndex(index_type); + auto dataset = GenDataset(NB, metric_type, false); + auto xb_data = dataset.get_col<float>(0); + auto xb_dataset = milvus::knowhere::GenDataset(NB, DIM, xb_data.data()); + index->BuildAll(xb_dataset, conf); + auto bs = index->Serialize(conf); + auto bptr = std::make_shared<milvus::knowhere::Binary>(); + bptr->data = std::shared_ptr<uint8_t[]>((uint8_t*)xb_data.data(), [&](uint8_t*) {}); + bptr->size = DIM * NB * sizeof(float); + bs.Append(RAW_DATA, bptr); + index->Load(bs); + auto xq_data = dataset.get_col<float>(0); + auto xq_dataset = milvus::knowhere::GenDataset(NQ, DIM, xq_data.data()); + auto result = index->Query(xq_dataset, conf, nullptr); +} + TEST(BINFLAT, Build) { auto index_type = milvus::knowhere::IndexEnum::INDEX_FAISS_BIN_IVFFLAT; auto metric_type = milvus::knowhere::Metric::JACCARD; @@ -485,12 +644,7 @@ TEST_P(IndexWrapperTest, Dim) { TEST_P(IndexWrapperTest, BuildWithoutIds) { auto index = std::make_unique<milvus::indexbuilder::IndexWrapper>(type_params_str.c_str(), index_params_str.c_str()); - - if (milvus::indexbuilder::is_in_need_id_list(index_type)) { - ASSERT_ANY_THROW(index->BuildWithoutIds(xb_dataset)); - } else { - ASSERT_NO_THROW(index->BuildWithoutIds(xb_dataset)); - } + ASSERT_NO_THROW(index->BuildWithoutIds(xb_dataset)); } TEST_P(IndexWrapperTest, Codec) { @@ -511,3 +665,16 @@ TEST_P(IndexWrapperTest, Codec) { ASSERT_EQ(strcmp(binary.data, copy_binary.data), 0); } } + +TEST_P(IndexWrapperTest, Query) { + auto index_wrapper = + std::make_unique<milvus::indexbuilder::IndexWrapper>(type_params_str.c_str(), index_params_str.c_str()); + + index_wrapper->BuildWithoutIds(xb_dataset); + + std::unique_ptr<milvus::indexbuilder::IndexWrapper::QueryResult> query_result = index_wrapper->Query(xq_dataset); + ASSERT_EQ(query_result->topk, K); + ASSERT_EQ(query_result->nq, NQ); + ASSERT_EQ(query_result->distances.size(), query_result->topk * query_result->nq); + ASSERT_EQ(query_result->ids.size(), query_result->topk * query_result->nq); +} diff --git a/internal/core/unittest/test_indexing.cpp b/internal/core/unittest/test_indexing.cpp index 1030938cefe2414223b7b51870f679743061b4d0..d5712c1ae680f1bed9b2c49e180905efae646f45 100644 --- a/internal/core/unittest/test_indexing.cpp +++ b/internal/core/unittest/test_indexing.cpp @@ -33,7 +33,7 @@ #include "test_utils/Timer.h" #include "segcore/Reduce.h" #include "test_utils/DataGen.h" -#include "query/BruteForceSearch.h" +#include "query/SearchBruteForce.h" using std::cin; using std::cout; @@ -245,8 +245,6 @@ TEST(Indexing, BinaryBruteForce) { schema->AddField("vecbin", DataType::VECTOR_BINARY, dim, MetricType::METRIC_Jaccard); schema->AddField("age", DataType::INT64); auto dataset = DataGen(schema, N, 10); - vector<float> distances(result_count); - vector<int64_t> ids(result_count); auto bin_vec = dataset.get_col<uint8_t>(0); auto line_sizeof = schema->operator[](0).get_sizeof(); auto query_data = 1024 * line_sizeof + bin_vec.data(); @@ -258,13 +256,13 @@ TEST(Indexing, BinaryBruteForce) { query_data // }; - query::BinarySearchBruteForce(query_dataset, bin_vec.data(), N, distances.data(), ids.data()); + auto sub_result = query::BinarySearchBruteForce(query_dataset, bin_vec.data(), N); QueryResult qr; qr.num_queries_ = num_queries; qr.topK_ = topk; - qr.internal_seg_offsets_ = ids; - qr.result_distances_ = distances; + qr.internal_seg_offsets_ = std::move(sub_result.mutable_labels()); + qr.result_distances_ = std::move(sub_result.mutable_values()); auto json = QueryResultToJson(qr); auto ref = json::parse(R"( diff --git a/internal/core/unittest/test_query.cpp b/internal/core/unittest/test_query.cpp index ce914a5c9eb7c2afc6de8ccf43132685311b4aa2..121f5932ae2e2fdc5d35a87361c8bbcd2b854808 100644 --- a/internal/core/unittest/test_query.cpp +++ b/internal/core/unittest/test_query.cpp @@ -402,7 +402,7 @@ TEST(Query, FillSegment) { pb::schema::CollectionSchema proto; proto.set_name("col"); proto.set_description("asdfhsalkgfhsadg"); - proto.set_autoid(true); + proto.set_autoid(false); { auto field = proto.add_fields(); @@ -425,7 +425,7 @@ TEST(Query, FillSegment) { field->set_fieldid(101); field->set_is_primary_key(true); field->set_description("asdgfsagf"); - field->set_data_type(pb::schema::DataType::INT32); + field->set_data_type(pb::schema::DataType::INT64); } auto schema = Schema::ParseFrom(proto); @@ -466,18 +466,17 @@ TEST(Query, FillSegment) { result.result_offsets_.resize(topk * num_queries); segment->FillTargetEntry(plan.get(), result); - // TODO: deprecated result_ids_ - ASSERT_EQ(result.result_ids_, result.internal_seg_offsets_); - auto ans = result.row_data_; ASSERT_EQ(ans.size(), topk * num_queries); int64_t std_index = 0; + auto std_vec = dataset.get_col<int64_t>(1); for (auto& vec : ans) { ASSERT_EQ(vec.size(), sizeof(int64_t)); int64_t val; memcpy(&val, vec.data(), sizeof(int64_t)); - auto std_val = result.result_ids_[std_index]; - ASSERT_EQ(val, std_val); + auto internal_offset = result.internal_seg_offsets_[std_index]; + auto std_val = std_vec[internal_offset]; + ASSERT_EQ(val, std_val) << "io:" << internal_offset; ++std_index; } } diff --git a/internal/core/unittest/test_reduce.cpp b/internal/core/unittest/test_reduce.cpp new file mode 100644 index 0000000000000000000000000000000000000000..985586128c2bab385497d723b6c334de0483cf94 --- /dev/null +++ b/internal/core/unittest/test_reduce.cpp @@ -0,0 +1,120 @@ +// Copyright (C) 2019-2020 Zilliz. All rights reserved. +// +// 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 + +#include <gtest/gtest.h> +#include "query/SubQueryResult.h" +#include <vector> +#include <queue> +#include <random> + +using namespace milvus; +using namespace milvus::query; + +TEST(Reduce, SubQueryResult) { + int64_t num_queries = 512; + int64_t topk = 32; + int64_t iteration = 50; + constexpr int64_t limit = 100000000L; + auto metric_type = MetricType::METRIC_L2; + using queue_type = std::priority_queue<int64_t>; + + std::vector<queue_type> ref_results(num_queries); + for (auto& ref_result : ref_results) { + for (int i = 0; i < topk; ++i) { + ref_result.push(limit); + } + } + std::default_random_engine e(42); + SubQueryResult final_result(num_queries, topk, metric_type); + for (int i = 0; i < iteration; ++i) { + std::vector<int64_t> labels; + std::vector<float> values; + for (int n = 0; n < num_queries; ++n) { + for (int k = 0; k < topk; ++k) { + auto gen_x = e() % limit; + ref_results[n].push(gen_x); + ref_results[n].pop(); + labels.push_back(gen_x); + values.push_back(gen_x); + } + std::sort(labels.begin() + n * topk, labels.begin() + n * topk + topk); + std::sort(values.begin() + n * topk, values.begin() + n * topk + topk); + } + SubQueryResult sub_result(num_queries, topk, metric_type); + sub_result.mutable_values() = values; + sub_result.mutable_labels() = labels; + final_result.merge(sub_result); + } + + for (int n = 0; n < num_queries; ++n) { + ASSERT_EQ(ref_results[n].size(), topk); + for (int k = 0; k < topk; ++k) { + auto ref_x = ref_results[n].top(); + ref_results[n].pop(); + auto index = n * topk + topk - 1 - k; + auto label = final_result.get_labels()[index]; + auto value = final_result.get_values()[index]; + ASSERT_EQ(label, ref_x); + ASSERT_EQ(value, ref_x); + } + } +} + +TEST(Reduce, SubQueryResultDesc) { + int64_t num_queries = 512; + int64_t topk = 32; + int64_t iteration = 50; + constexpr int64_t limit = 100000000L; + constexpr int64_t init_value = 0; + auto metric_type = MetricType::METRIC_INNER_PRODUCT; + using queue_type = std::priority_queue<int64_t, std::vector<int64_t>, std::greater<int64_t>>; + + std::vector<queue_type> ref_results(num_queries); + for (auto& ref_result : ref_results) { + for (int i = 0; i < topk; ++i) { + ref_result.push(init_value); + } + } + std::default_random_engine e(42); + SubQueryResult final_result(num_queries, topk, metric_type); + for (int i = 0; i < iteration; ++i) { + std::vector<int64_t> labels; + std::vector<float> values; + for (int n = 0; n < num_queries; ++n) { + for (int k = 0; k < topk; ++k) { + auto gen_x = e() % limit; + ref_results[n].push(gen_x); + ref_results[n].pop(); + labels.push_back(gen_x); + values.push_back(gen_x); + } + std::sort(labels.begin() + n * topk, labels.begin() + n * topk + topk, std::greater<int64_t>()); + std::sort(values.begin() + n * topk, values.begin() + n * topk + topk, std::greater<float>()); + } + SubQueryResult sub_result(num_queries, topk, metric_type); + sub_result.mutable_values() = values; + sub_result.mutable_labels() = labels; + final_result.merge(sub_result); + } + + for (int n = 0; n < num_queries; ++n) { + ASSERT_EQ(ref_results[n].size(), topk); + for (int k = 0; k < topk; ++k) { + auto ref_x = ref_results[n].top(); + ref_results[n].pop(); + auto index = n * topk + topk - 1 - k; + auto label = final_result.get_labels()[index]; + auto value = final_result.get_values()[index]; + ASSERT_EQ(label, ref_x); + ASSERT_EQ(value, ref_x); + } + } +} \ No newline at end of file diff --git a/internal/indexbuilder/indexbuilder.go b/internal/indexbuilder/indexbuilder.go index 5b21e68dd4eebd11079784d799aaf63679a28359..4acfffc3d284665158d9f3c237682a5792257d50 100644 --- a/internal/indexbuilder/indexbuilder.go +++ b/internal/indexbuilder/indexbuilder.go @@ -11,9 +11,6 @@ import ( miniokv "github.com/zilliztech/milvus-distributed/internal/kv/minio" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "go.etcd.io/etcd/clientv3" "github.com/zilliztech/milvus-distributed/internal/allocator" @@ -71,19 +68,16 @@ func CreateBuilder(ctx context.Context) (*Builder, error) { idAllocator, err := allocator.NewIDAllocator(b.loopCtx, Params.MasterAddress) - minIOEndPoint := Params.MinIOAddress - minIOAccessKeyID := Params.MinIOAccessKeyID - minIOSecretAccessKey := Params.MinIOSecretAccessKey - minIOUseSSL := Params.MinIOUseSSL - minIOClient, err := minio.New(minIOEndPoint, &minio.Options{ - Creds: credentials.NewStaticV4(minIOAccessKeyID, minIOSecretAccessKey, ""), - Secure: minIOUseSSL, - }) - if err != nil { - return nil, err + option := &miniokv.Option{ + Address: Params.MinIOAddress, + AccessKeyID: Params.MinIOAccessKeyID, + SecretAccessKeyID: Params.MinIOSecretAccessKey, + UseSSL: Params.MinIOUseSSL, + BucketName: Params.MinioBucketName, + CreateBucket: true, } - b.kv, err = miniokv.NewMinIOKV(b.loopCtx, minIOClient, Params.MinioBucketName) + b.kv, err = miniokv.NewMinIOKV(b.loopCtx, option) if err != nil { return nil, err } diff --git a/internal/kv/minio/minio_kv.go b/internal/kv/minio/minio_kv.go index 68bb3a3438bbd771a2d5afc98c23988703db72ea..6b3522fb454273f24a08e11d83f4f64e43a40381 100644 --- a/internal/kv/minio/minio_kv.go +++ b/internal/kv/minio/minio_kv.go @@ -2,11 +2,15 @@ package miniokv import ( "context" + "fmt" + "io" "log" "strings" "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/zilliztech/milvus-distributed/internal/errors" ) type MinIOKV struct { @@ -15,24 +19,46 @@ type MinIOKV struct { bucketName string } -// NewMinIOKV creates a new MinIO kv. -func NewMinIOKV(ctx context.Context, client *minio.Client, bucketName string) (*MinIOKV, error) { +type Option struct { + Address string + AccessKeyID string + BucketName string + SecretAccessKeyID string + UseSSL bool + CreateBucket bool // when bucket not existed, create it +} - bucketExists, err := client.BucketExists(ctx, bucketName) +func NewMinIOKV(ctx context.Context, option *Option) (*MinIOKV, error) { + minIOClient, err := minio.New(option.Address, &minio.Options{ + Creds: credentials.NewStaticV4(option.AccessKeyID, option.SecretAccessKeyID, ""), + Secure: option.UseSSL, + }) if err != nil { return nil, err } - if !bucketExists { - err = client.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{}) - if err != nil { - return nil, err + bucketExists, err := minIOClient.BucketExists(ctx, option.BucketName) + if err != nil { + return nil, err + } + + if option.CreateBucket { + if !bucketExists { + err = minIOClient.MakeBucket(ctx, option.BucketName, minio.MakeBucketOptions{}) + if err != nil { + return nil, err + } + } + } else { + if !bucketExists { + return nil, errors.New(fmt.Sprintf("Bucket %s not Existed.", option.BucketName)) } } + return &MinIOKV{ ctx: ctx, - minioClient: client, - bucketName: bucketName, + minioClient: minIOClient, + bucketName: option.BucketName, }, nil } diff --git a/internal/kv/minio/minio_kv_test.go b/internal/kv/minio/minio_kv_test.go index ac2a3180b2966bbdc09158ff70ec8baaef23dedb..2e50545b40a83516aa16cb0991bbc64cf34360cc 100644 --- a/internal/kv/minio/minio_kv_test.go +++ b/internal/kv/minio/minio_kv_test.go @@ -5,8 +5,6 @@ import ( "strconv" "testing" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" miniokv "github.com/zilliztech/milvus-distributed/internal/kv/minio" "github.com/zilliztech/milvus-distributed/internal/util/paramtable" @@ -15,24 +13,31 @@ import ( var Params paramtable.BaseTable -func TestMinIOKV_Load(t *testing.T) { - Params.Init() +func newMinIOKVClient(ctx context.Context, bucketName string) (*miniokv.MinIOKV, error) { endPoint, _ := Params.Load("_MinioAddress") accessKeyID, _ := Params.Load("minio.accessKeyID") secretAccessKey, _ := Params.Load("minio.secretAccessKey") useSSLStr, _ := Params.Load("minio.useSSL") - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() useSSL, _ := strconv.ParseBool(useSSLStr) + option := &miniokv.Option{ + Address: endPoint, + AccessKeyID: accessKeyID, + SecretAccessKeyID: secretAccessKey, + UseSSL: useSSL, + BucketName: bucketName, + CreateBucket: true, + } + client, err := miniokv.NewMinIOKV(ctx, option) + return client, err +} - minioClient, err := minio.New(endPoint, &minio.Options{ - Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), - Secure: useSSL, - }) - assert.Nil(t, err) +func TestMinIOKV_Load(t *testing.T) { + Params.Init() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() bucketName := "fantastic-tech-test" - MinIOKV, err := miniokv.NewMinIOKV(ctx, minioClient, bucketName) + MinIOKV, err := newMinIOKVClient(ctx, bucketName) assert.Nil(t, err) defer MinIOKV.RemoveWithPrefix("") @@ -79,25 +84,14 @@ func TestMinIOKV_Load(t *testing.T) { } func TestMinIOKV_MultiSave(t *testing.T) { + Params.Init() ctx, cancel := context.WithCancel(context.Background()) defer cancel() - Params.Init() - endPoint, _ := Params.Load("_MinioAddress") - accessKeyID, _ := Params.Load("minio.accessKeyID") - secretAccessKey, _ := Params.Load("minio.secretAccessKey") - useSSLStr, _ := Params.Load("minio.useSSL") - useSSL, _ := strconv.ParseBool(useSSLStr) - - minioClient, err := minio.New(endPoint, &minio.Options{ - Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), - Secure: useSSL, - }) - assert.Nil(t, err) - bucketName := "fantastic-tech-test" - MinIOKV, err := miniokv.NewMinIOKV(ctx, minioClient, bucketName) + MinIOKV, err := newMinIOKVClient(ctx, bucketName) assert.Nil(t, err) + defer MinIOKV.RemoveWithPrefix("") err = MinIOKV.Save("key_1", "111") @@ -117,25 +111,13 @@ func TestMinIOKV_MultiSave(t *testing.T) { } func TestMinIOKV_Remove(t *testing.T) { + Params.Init() ctx, cancel := context.WithCancel(context.Background()) defer cancel() - Params.Init() - endPoint, _ := Params.Load("_MinioAddress") - accessKeyID, _ := Params.Load("minio.accessKeyID") - secretAccessKey, _ := Params.Load("minio.secretAccessKey") - useSSLStr, _ := Params.Load("minio.useSSL") - useSSL, _ := strconv.ParseBool(useSSLStr) - - minioClient, err := minio.New(endPoint, &minio.Options{ - Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), - Secure: useSSL, - }) - assert.Nil(t, err) - bucketName := "fantastic-tech-test" - MinIOKV, err := miniokv.NewMinIOKV(ctx, minioClient, bucketName) + MinIOKV, err := newMinIOKVClient(ctx, bucketName) assert.Nil(t, err) defer MinIOKV.RemoveWithPrefix("") diff --git a/internal/master/client.go b/internal/master/client.go index 7c644e02acddf72292636baac9d011272bc19574..88e44d8f70a02aba66217094d8f2327c6b06c814 100644 --- a/internal/master/client.go +++ b/internal/master/client.go @@ -9,19 +9,25 @@ import ( ) type WriteNodeClient interface { - FlushSegment(segmentID UniqueID) error + FlushSegment(segmentID UniqueID, collectionID UniqueID, partitionTag string, timestamp Timestamp) error DescribeSegment(segmentID UniqueID) (*writerclient.SegmentDescription, error) GetInsertBinlogPaths(segmentID UniqueID) (map[UniqueID][]string, error) } type MockWriteNodeClient struct { - segmentID UniqueID - flushTime time.Time + segmentID UniqueID + flushTime time.Time + partitionTag string + timestamp Timestamp + collectionID UniqueID } -func (m *MockWriteNodeClient) FlushSegment(segmentID UniqueID) error { +func (m *MockWriteNodeClient) FlushSegment(segmentID UniqueID, collectionID UniqueID, partitionTag string, timestamp Timestamp) error { m.flushTime = time.Now() m.segmentID = segmentID + m.collectionID = collectionID + m.partitionTag = partitionTag + m.timestamp = timestamp return nil } diff --git a/internal/master/flush_scheduler.go b/internal/master/flush_scheduler.go index d27511c2c868907be6fb3e551d04695455dbf1b4..7ed3d9a5689383f05d10a9ab598f9e4f293540c5 100644 --- a/internal/master/flush_scheduler.go +++ b/internal/master/flush_scheduler.go @@ -17,11 +17,12 @@ type FlushScheduler struct { segmentDescribeChan chan UniqueID indexBuilderSch persistenceScheduler - ctx context.Context - cancel context.CancelFunc + ctx context.Context + cancel context.CancelFunc + globalTSOAllocator func() (Timestamp, error) } -func NewFlushScheduler(ctx context.Context, client WriteNodeClient, metaTable *metaTable, buildScheduler *IndexBuildScheduler) *FlushScheduler { +func NewFlushScheduler(ctx context.Context, client WriteNodeClient, metaTable *metaTable, buildScheduler *IndexBuildScheduler, globalTSOAllocator func() (Timestamp, error)) *FlushScheduler { ctx2, cancel := context.WithCancel(ctx) return &FlushScheduler{ @@ -32,12 +33,23 @@ func NewFlushScheduler(ctx context.Context, client WriteNodeClient, metaTable *m segmentDescribeChan: make(chan UniqueID, 100), ctx: ctx2, cancel: cancel, + globalTSOAllocator: globalTSOAllocator, } } func (scheduler *FlushScheduler) schedule(id interface{}) error { segmentID := id.(UniqueID) - err := scheduler.client.FlushSegment(segmentID) + segmentMeta, err := scheduler.metaTable.GetSegmentByID(segmentID) + if err != nil { + return err + } + + ts, err := scheduler.globalTSOAllocator() + if err != nil { + return err + } + // todo set corrent timestamp + err = scheduler.client.FlushSegment(segmentID, segmentMeta.CollectionID, segmentMeta.PartitionTag, ts) log.Printf("flush segment %d", segmentID) if err != nil { return err diff --git a/internal/master/master.go b/internal/master/master.go index 5476a7c8dc177e249c6cca4103a264f8c83d1e17..12f6e53086c74854e747c5969376260de5130f41 100644 --- a/internal/master/master.go +++ b/internal/master/master.go @@ -193,7 +193,7 @@ func CreateServer(ctx context.Context) (*Master, error) { m.indexLoadSch = NewIndexLoadScheduler(ctx, loadIndexClient, m.metaTable) m.indexBuildSch = NewIndexBuildScheduler(ctx, buildIndexClient, m.metaTable, m.indexLoadSch) - m.flushSch = NewFlushScheduler(ctx, flushClient, m.metaTable, m.indexBuildSch) + m.flushSch = NewFlushScheduler(ctx, flushClient, m.metaTable, m.indexBuildSch, func() (Timestamp, error) { return m.tsoAllocator.AllocOne() }) m.segmentAssigner = NewSegmentAssigner(ctx, metakv, func() (Timestamp, error) { return m.tsoAllocator.AllocOne() }, @@ -218,6 +218,7 @@ func CreateServer(ctx context.Context) (*Master, error) { m.grpcServer = grpc.NewServer() masterpb.RegisterMasterServer(m.grpcServer, m) + return m, nil } diff --git a/internal/master/master_test.go b/internal/master/master_test.go index 0a44ed90e886b55d6b7bd5bec9e2d1842041fd2c..a605e73aa76127c24918cdd3826fae0d0d186ad8 100644 --- a/internal/master/master_test.go +++ b/internal/master/master_test.go @@ -110,6 +110,7 @@ func TestMaster(t *testing.T) { conn, err := grpc.DialContext(ctx, Params.Address, grpc.WithInsecure(), grpc.WithBlock()) require.Nil(t, err) + cli := masterpb.NewMasterClient(conn) t.Run("TestConfigTask", func(t *testing.T) { @@ -886,12 +887,6 @@ func TestMaster(t *testing.T) { var k2sMsgstream ms.MsgStream = k2sMs assert.True(t, receiveTimeTickMsg(&k2sMsgstream)) - conn, err := grpc.DialContext(ctx, Params.Address, grpc.WithInsecure(), grpc.WithBlock()) - assert.Nil(t, err) - defer conn.Close() - - cli := masterpb.NewMasterClient(conn) - sch := schemapb.CollectionSchema{ Name: "name" + strconv.FormatUint(rand.Uint64(), 10), Description: "test collection", diff --git a/internal/master/persistence_scheduler_test.go b/internal/master/persistence_scheduler_test.go index 44110f3a0b94a4f29ba5dc7036bf42bf41616ab5..917e520f29757d91e3b5ada6993b5d28860373bd 100644 --- a/internal/master/persistence_scheduler_test.go +++ b/internal/master/persistence_scheduler_test.go @@ -59,7 +59,11 @@ func TestPersistenceScheduler(t *testing.T) { //Init scheduler indexLoadSch := NewIndexLoadScheduler(ctx, loadIndexClient, meta) indexBuildSch := NewIndexBuildScheduler(ctx, buildIndexClient, meta, indexLoadSch) - flushSch := NewFlushScheduler(ctx, flushClient, meta, indexBuildSch) + cnt := 0 + flushSch := NewFlushScheduler(ctx, flushClient, meta, indexBuildSch, func() (Timestamp, error) { + cnt++ + return Timestamp(cnt), nil + }) //scheduler start err = indexLoadSch.Start() diff --git a/internal/msgstream/msg.go b/internal/msgstream/msg.go index 518bcfa7afe56a34cf86ff1464eb309a42560a9c..a71d1cabfe89745cfdfaec2d3411c4e3521c8b19 100644 --- a/internal/msgstream/msg.go +++ b/internal/msgstream/msg.go @@ -1,6 +1,8 @@ package msgstream import ( + "context" + "github.com/golang/protobuf/proto" internalPb "github.com/zilliztech/milvus-distributed/internal/proto/internalpb" ) @@ -8,6 +10,8 @@ import ( type MsgType = internalPb.MsgType type TsMsg interface { + GetContext() context.Context + SetContext(context.Context) BeginTs() Timestamp EndTs() Timestamp Type() MsgType @@ -17,6 +21,7 @@ type TsMsg interface { } type BaseMsg struct { + ctx context.Context BeginTimestamp Timestamp EndTimestamp Timestamp HashValues []uint32 @@ -44,6 +49,14 @@ func (it *InsertMsg) Type() MsgType { return it.MsgType } +func (it *InsertMsg) GetContext() context.Context { + return it.ctx +} + +func (it *InsertMsg) SetContext(ctx context.Context) { + it.ctx = ctx +} + func (it *InsertMsg) Marshal(input TsMsg) ([]byte, error) { insertMsg := input.(*InsertMsg) insertRequest := &insertMsg.InsertRequest @@ -88,6 +101,13 @@ func (fl *FlushMsg) Type() MsgType { return fl.GetMsgType() } +func (fl *FlushMsg) GetContext() context.Context { + return fl.ctx +} +func (fl *FlushMsg) SetContext(ctx context.Context) { + fl.ctx = ctx +} + func (fl *FlushMsg) Marshal(input TsMsg) ([]byte, error) { flushMsgTask := input.(*FlushMsg) flushMsg := &flushMsgTask.FlushMsg @@ -121,6 +141,14 @@ func (dt *DeleteMsg) Type() MsgType { return dt.MsgType } +func (dt *DeleteMsg) GetContext() context.Context { + return dt.ctx +} + +func (dt *DeleteMsg) SetContext(ctx context.Context) { + dt.ctx = ctx +} + func (dt *DeleteMsg) Marshal(input TsMsg) ([]byte, error) { deleteTask := input.(*DeleteMsg) deleteRequest := &deleteTask.DeleteRequest @@ -165,6 +193,14 @@ func (st *SearchMsg) Type() MsgType { return st.MsgType } +func (st *SearchMsg) GetContext() context.Context { + return st.ctx +} + +func (st *SearchMsg) SetContext(ctx context.Context) { + st.ctx = ctx +} + func (st *SearchMsg) Marshal(input TsMsg) ([]byte, error) { searchTask := input.(*SearchMsg) searchRequest := &searchTask.SearchRequest @@ -198,6 +234,14 @@ func (srt *SearchResultMsg) Type() MsgType { return srt.MsgType } +func (srt *SearchResultMsg) GetContext() context.Context { + return srt.ctx +} + +func (srt *SearchResultMsg) SetContext(ctx context.Context) { + srt.ctx = ctx +} + func (srt *SearchResultMsg) Marshal(input TsMsg) ([]byte, error) { searchResultTask := input.(*SearchResultMsg) searchResultRequest := &searchResultTask.SearchResult @@ -231,6 +275,14 @@ func (tst *TimeTickMsg) Type() MsgType { return tst.MsgType } +func (tst *TimeTickMsg) GetContext() context.Context { + return tst.ctx +} + +func (tst *TimeTickMsg) SetContext(ctx context.Context) { + tst.ctx = ctx +} + func (tst *TimeTickMsg) Marshal(input TsMsg) ([]byte, error) { timeTickTask := input.(*TimeTickMsg) timeTick := &timeTickTask.TimeTickMsg @@ -264,6 +316,14 @@ func (qs *QueryNodeStatsMsg) Type() MsgType { return qs.MsgType } +func (qs *QueryNodeStatsMsg) GetContext() context.Context { + return qs.ctx +} + +func (qs *QueryNodeStatsMsg) SetContext(ctx context.Context) { + qs.ctx = ctx +} + func (qs *QueryNodeStatsMsg) Marshal(input TsMsg) ([]byte, error) { queryNodeSegStatsTask := input.(*QueryNodeStatsMsg) queryNodeSegStats := &queryNodeSegStatsTask.QueryNodeStats @@ -305,6 +365,14 @@ func (cc *CreateCollectionMsg) Type() MsgType { return cc.MsgType } +func (cc *CreateCollectionMsg) GetContext() context.Context { + return cc.ctx +} + +func (cc *CreateCollectionMsg) SetContext(ctx context.Context) { + cc.ctx = ctx +} + func (cc *CreateCollectionMsg) Marshal(input TsMsg) ([]byte, error) { createCollectionMsg := input.(*CreateCollectionMsg) createCollectionRequest := &createCollectionMsg.CreateCollectionRequest @@ -337,6 +405,13 @@ type DropCollectionMsg struct { func (dc *DropCollectionMsg) Type() MsgType { return dc.MsgType } +func (dc *DropCollectionMsg) GetContext() context.Context { + return dc.ctx +} + +func (dc *DropCollectionMsg) SetContext(ctx context.Context) { + dc.ctx = ctx +} func (dc *DropCollectionMsg) Marshal(input TsMsg) ([]byte, error) { dropCollectionMsg := input.(*DropCollectionMsg) @@ -361,109 +436,18 @@ func (dc *DropCollectionMsg) Unmarshal(input []byte) (TsMsg, error) { return dropCollectionMsg, nil } -/////////////////////////////////////////HasCollection////////////////////////////////////////// -type HasCollectionMsg struct { - BaseMsg - internalPb.HasCollectionRequest -} - -func (hc *HasCollectionMsg) Type() MsgType { - return hc.MsgType -} - -func (hc *HasCollectionMsg) Marshal(input TsMsg) ([]byte, error) { - hasCollectionMsg := input.(*HasCollectionMsg) - hasCollectionRequest := &hasCollectionMsg.HasCollectionRequest - mb, err := proto.Marshal(hasCollectionRequest) - if err != nil { - return nil, err - } - return mb, nil -} - -func (hc *HasCollectionMsg) Unmarshal(input []byte) (TsMsg, error) { - hasCollectionRequest := internalPb.HasCollectionRequest{} - err := proto.Unmarshal(input, &hasCollectionRequest) - if err != nil { - return nil, err - } - hasCollectionMsg := &HasCollectionMsg{HasCollectionRequest: hasCollectionRequest} - hasCollectionMsg.BeginTimestamp = hasCollectionMsg.Timestamp - hasCollectionMsg.EndTimestamp = hasCollectionMsg.Timestamp - - return hasCollectionMsg, nil -} - -/////////////////////////////////////////DescribeCollection////////////////////////////////////////// -type DescribeCollectionMsg struct { - BaseMsg - internalPb.DescribeCollectionRequest -} - -func (dc *DescribeCollectionMsg) Type() MsgType { - return dc.MsgType -} - -func (dc *DescribeCollectionMsg) Marshal(input TsMsg) ([]byte, error) { - describeCollectionMsg := input.(*DescribeCollectionMsg) - describeCollectionRequest := &describeCollectionMsg.DescribeCollectionRequest - mb, err := proto.Marshal(describeCollectionRequest) - if err != nil { - return nil, err - } - return mb, nil -} - -func (dc *DescribeCollectionMsg) Unmarshal(input []byte) (TsMsg, error) { - describeCollectionRequest := internalPb.DescribeCollectionRequest{} - err := proto.Unmarshal(input, &describeCollectionRequest) - if err != nil { - return nil, err - } - describeCollectionMsg := &DescribeCollectionMsg{DescribeCollectionRequest: describeCollectionRequest} - describeCollectionMsg.BeginTimestamp = describeCollectionMsg.Timestamp - describeCollectionMsg.EndTimestamp = describeCollectionMsg.Timestamp - - return describeCollectionMsg, nil -} - -/////////////////////////////////////////ShowCollection////////////////////////////////////////// -type ShowCollectionMsg struct { +/////////////////////////////////////////CreatePartition////////////////////////////////////////// +type CreatePartitionMsg struct { BaseMsg - internalPb.ShowCollectionRequest -} - -func (sc *ShowCollectionMsg) Type() MsgType { - return sc.MsgType -} - -func (sc *ShowCollectionMsg) Marshal(input TsMsg) ([]byte, error) { - showCollectionMsg := input.(*ShowCollectionMsg) - showCollectionRequest := &showCollectionMsg.ShowCollectionRequest - mb, err := proto.Marshal(showCollectionRequest) - if err != nil { - return nil, err - } - return mb, nil + internalPb.CreatePartitionRequest } -func (sc *ShowCollectionMsg) Unmarshal(input []byte) (TsMsg, error) { - showCollectionRequest := internalPb.ShowCollectionRequest{} - err := proto.Unmarshal(input, &showCollectionRequest) - if err != nil { - return nil, err - } - showCollectionMsg := &ShowCollectionMsg{ShowCollectionRequest: showCollectionRequest} - showCollectionMsg.BeginTimestamp = showCollectionMsg.Timestamp - showCollectionMsg.EndTimestamp = showCollectionMsg.Timestamp - - return showCollectionMsg, nil +func (cc *CreatePartitionMsg) GetContext() context.Context { + return cc.ctx } -/////////////////////////////////////////CreatePartition////////////////////////////////////////// -type CreatePartitionMsg struct { - BaseMsg - internalPb.CreatePartitionRequest +func (cc *CreatePartitionMsg) SetContext(ctx context.Context) { + cc.ctx = ctx } func (cc *CreatePartitionMsg) Type() MsgType { @@ -499,6 +483,14 @@ type DropPartitionMsg struct { internalPb.DropPartitionRequest } +func (dc *DropPartitionMsg) GetContext() context.Context { + return dc.ctx +} + +func (dc *DropPartitionMsg) SetContext(ctx context.Context) { + dc.ctx = ctx +} + func (dc *DropPartitionMsg) Type() MsgType { return dc.MsgType } @@ -526,105 +518,6 @@ func (dc *DropPartitionMsg) Unmarshal(input []byte) (TsMsg, error) { return dropPartitionMsg, nil } -/////////////////////////////////////////HasPartition////////////////////////////////////////// -type HasPartitionMsg struct { - BaseMsg - internalPb.HasPartitionRequest -} - -func (hc *HasPartitionMsg) Type() MsgType { - return hc.MsgType -} - -func (hc *HasPartitionMsg) Marshal(input TsMsg) ([]byte, error) { - hasPartitionMsg := input.(*HasPartitionMsg) - hasPartitionRequest := &hasPartitionMsg.HasPartitionRequest - mb, err := proto.Marshal(hasPartitionRequest) - if err != nil { - return nil, err - } - return mb, nil -} - -func (hc *HasPartitionMsg) Unmarshal(input []byte) (TsMsg, error) { - hasPartitionRequest := internalPb.HasPartitionRequest{} - err := proto.Unmarshal(input, &hasPartitionRequest) - if err != nil { - return nil, err - } - hasPartitionMsg := &HasPartitionMsg{HasPartitionRequest: hasPartitionRequest} - hasPartitionMsg.BeginTimestamp = hasPartitionMsg.Timestamp - hasPartitionMsg.EndTimestamp = hasPartitionMsg.Timestamp - - return hasPartitionMsg, nil -} - -/////////////////////////////////////////DescribePartition////////////////////////////////////////// -type DescribePartitionMsg struct { - BaseMsg - internalPb.DescribePartitionRequest -} - -func (dc *DescribePartitionMsg) Type() MsgType { - return dc.MsgType -} - -func (dc *DescribePartitionMsg) Marshal(input TsMsg) ([]byte, error) { - describePartitionMsg := input.(*DescribePartitionMsg) - describePartitionRequest := &describePartitionMsg.DescribePartitionRequest - mb, err := proto.Marshal(describePartitionRequest) - if err != nil { - return nil, err - } - return mb, nil -} - -func (dc *DescribePartitionMsg) Unmarshal(input []byte) (TsMsg, error) { - describePartitionRequest := internalPb.DescribePartitionRequest{} - err := proto.Unmarshal(input, &describePartitionRequest) - if err != nil { - return nil, err - } - describePartitionMsg := &DescribePartitionMsg{DescribePartitionRequest: describePartitionRequest} - describePartitionMsg.BeginTimestamp = describePartitionMsg.Timestamp - describePartitionMsg.EndTimestamp = describePartitionMsg.Timestamp - - return describePartitionMsg, nil -} - -/////////////////////////////////////////ShowPartition////////////////////////////////////////// -type ShowPartitionMsg struct { - BaseMsg - internalPb.ShowPartitionRequest -} - -func (sc *ShowPartitionMsg) Type() MsgType { - return sc.MsgType -} - -func (sc *ShowPartitionMsg) Marshal(input TsMsg) ([]byte, error) { - showPartitionMsg := input.(*ShowPartitionMsg) - showPartitionRequest := &showPartitionMsg.ShowPartitionRequest - mb, err := proto.Marshal(showPartitionRequest) - if err != nil { - return nil, err - } - return mb, nil -} - -func (sc *ShowPartitionMsg) Unmarshal(input []byte) (TsMsg, error) { - showPartitionRequest := internalPb.ShowPartitionRequest{} - err := proto.Unmarshal(input, &showPartitionRequest) - if err != nil { - return nil, err - } - showPartitionMsg := &ShowPartitionMsg{ShowPartitionRequest: showPartitionRequest} - showPartitionMsg.BeginTimestamp = showPartitionMsg.Timestamp - showPartitionMsg.EndTimestamp = showPartitionMsg.Timestamp - - return showPartitionMsg, nil -} - /////////////////////////////////////////LoadIndex////////////////////////////////////////// type LoadIndexMsg struct { BaseMsg @@ -635,6 +528,14 @@ func (lim *LoadIndexMsg) Type() MsgType { return lim.MsgType } +func (lim *LoadIndexMsg) GetContext() context.Context { + return lim.ctx +} + +func (lim *LoadIndexMsg) SetContext(ctx context.Context) { + lim.ctx = ctx +} + func (lim *LoadIndexMsg) Marshal(input TsMsg) ([]byte, error) { loadIndexMsg := input.(*LoadIndexMsg) loadIndexRequest := &loadIndexMsg.LoadIndex diff --git a/internal/msgstream/msgstream.go b/internal/msgstream/msgstream.go index 37dd71c053441673334cbda1c60adac9fdc5fc5f..969755feb322a3ac33619500a061dd9a0f984c00 100644 --- a/internal/msgstream/msgstream.go +++ b/internal/msgstream/msgstream.go @@ -4,9 +4,13 @@ import ( "context" "log" "reflect" + "strings" "sync" "time" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/apache/pulsar-client-go/pulsar" "github.com/golang/protobuf/proto" @@ -151,6 +155,29 @@ func (ms *PulsarMsgStream) Close() { } } +type propertiesReaderWriter struct { + ppMap map[string]string +} + +func (ppRW *propertiesReaderWriter) Set(key, val string) { + // The GRPC HPACK implementation rejects any uppercase keys here. + // + // As such, since the HTTP_HEADERS format is case-insensitive anyway, we + // blindly lowercase the key (which is guaranteed to work in the + // Inject/Extract sense per the OpenTracing spec). + key = strings.ToLower(key) + ppRW.ppMap[key] = val +} + +func (ppRW *propertiesReaderWriter) ForeachKey(handler func(key, val string) error) error { + for k, val := range ppRW.ppMap { + if err := handler(k, val); err != nil { + return err + } + } + return nil +} + func (ms *PulsarMsgStream) Produce(msgPack *MsgPack) error { tsMsgs := msgPack.Msgs if len(tsMsgs) <= 0 { @@ -200,12 +227,41 @@ func (ms *PulsarMsgStream) Produce(msgPack *MsgPack) error { if err != nil { return err } + + msg := &pulsar.ProducerMessage{Payload: mb} + var child opentracing.Span + if v.Msgs[i].Type() == internalPb.MsgType_kInsert || v.Msgs[i].Type() == internalPb.MsgType_kSearch { + tracer := opentracing.GlobalTracer() + ctx := v.Msgs[i].GetContext() + if ctx == nil { + ctx = context.Background() + } + + if parent := opentracing.SpanFromContext(ctx); parent != nil { + child = tracer.StartSpan("start send pulsar msg", + opentracing.FollowsFrom(parent.Context())) + } else { + child = tracer.StartSpan("start send pulsar msg") + } + child.SetTag("hash keys", v.Msgs[i].HashKeys()) + child.SetTag("start time", v.Msgs[i].BeginTs()) + child.SetTag("end time", v.Msgs[i].EndTs()) + msg.Properties = make(map[string]string) + err = tracer.Inject(child.Context(), opentracing.TextMap, &propertiesReaderWriter{msg.Properties}) + if err != nil { + return err + } + } + if _, err := (*ms.producers[k]).Send( context.Background(), - &pulsar.ProducerMessage{Payload: mb}, + msg, ); err != nil { return err } + if child != nil { + child.Finish() + } } } return nil @@ -218,10 +274,34 @@ func (ms *PulsarMsgStream) Broadcast(msgPack *MsgPack) error { if err != nil { return err } + msg := &pulsar.ProducerMessage{Payload: mb} + if v.Type() == internalPb.MsgType_kInsert || v.Type() == internalPb.MsgType_kSearch { + tracer := opentracing.GlobalTracer() + ctx := v.GetContext() + if ctx == nil { + ctx = context.Background() + } + var child opentracing.Span + if parent := opentracing.SpanFromContext(ctx); parent != nil { + child = tracer.StartSpan("start send pulsar msg", + opentracing.FollowsFrom(parent.Context())) + } else { + child = tracer.StartSpan("start send pulsar msg, start time: %d") + } + child.SetTag("hash keys", v.HashKeys()) + child.SetTag("start time", v.BeginTs()) + child.SetTag("end time", v.EndTs()) + msg.Properties = make(map[string]string) + err = tracer.Inject(child.Context(), opentracing.TextMap, &propertiesReaderWriter{msg.Properties}) + if err != nil { + return err + } + child.Finish() + } for i := 0; i < producerLen; i++ { if _, err := (*ms.producers[i]).Send( context.Background(), - &pulsar.ProducerMessage{Payload: mb}, + msg, ); err != nil { return err } @@ -258,6 +338,7 @@ func (ms *PulsarMsgStream) bufMsgPackToChannel() { for { select { case <-ms.ctx.Done(): + log.Println("done") return default: tsMsgList := make([]TsMsg, 0) @@ -270,6 +351,7 @@ func (ms *PulsarMsgStream) bufMsgPackToChannel() { } pulsarMsg, ok := value.Interface().(pulsar.ConsumerMessage) + if !ok { log.Printf("type assertion failed, not consumer message type") continue @@ -283,6 +365,21 @@ func (ms *PulsarMsgStream) bufMsgPackToChannel() { continue } tsMsg, err := ms.unmarshal.Unmarshal(pulsarMsg.Payload(), headerMsg.MsgType) + if tsMsg.Type() == internalPb.MsgType_kInsert || tsMsg.Type() == internalPb.MsgType_kSearch { + tracer := opentracing.GlobalTracer() + spanContext, err := tracer.Extract(opentracing.HTTPHeaders, &propertiesReaderWriter{pulsarMsg.Properties()}) + if err != nil { + log.Println("extract message err") + log.Println(err.Error()) + } + span := opentracing.StartSpan("pulsar msg received", + ext.RPCServerOption(spanContext)) + span.SetTag("hash keys", tsMsg.HashKeys()) + span.SetTag("start time", tsMsg.BeginTs()) + span.SetTag("end time", tsMsg.EndTs()) + tsMsg.SetContext(opentracing.ContextWithSpan(context.Background(), span)) + span.Finish() + } if err != nil { log.Printf("Failed to unmarshal tsMsg, error = %v", err) continue @@ -420,6 +517,23 @@ func (ms *PulsarTtMsgStream) findTimeTick(channelIndex int, if err != nil { log.Printf("Failed to unmarshal, error = %v", err) } + + if tsMsg.Type() == internalPb.MsgType_kInsert || tsMsg.Type() == internalPb.MsgType_kSearch { + tracer := opentracing.GlobalTracer() + spanContext, err := tracer.Extract(opentracing.HTTPHeaders, &propertiesReaderWriter{pulsarMsg.Properties()}) + if err != nil { + log.Println("extract message err") + log.Println(err.Error()) + } + span := opentracing.StartSpan("pulsar msg received", + ext.RPCServerOption(spanContext)) + span.SetTag("hash keys", tsMsg.HashKeys()) + span.SetTag("start time", tsMsg.BeginTs()) + span.SetTag("end time", tsMsg.EndTs()) + tsMsg.SetContext(opentracing.ContextWithSpan(context.Background(), span)) + span.Finish() + } + if headerMsg.MsgType == internalPb.MsgType_kTimeTick { eofMsgMap[channelIndex] = tsMsg.(*TimeTickMsg).Timestamp return @@ -500,7 +614,7 @@ func insertRepackFunc(tsMsgs []TsMsg, hashKeys [][]int32) (map[int32]*MsgPack, e result := make(map[int32]*MsgPack) for i, request := range tsMsgs { if request.Type() != internalPb.MsgType_kInsert { - return nil, errors.New(string("msg's must be Insert")) + return nil, errors.New("msg's must be Insert") } insertRequest := request.(*InsertMsg) keys := hashKeys[i] @@ -511,7 +625,7 @@ func insertRepackFunc(tsMsgs []TsMsg, hashKeys [][]int32) (map[int32]*MsgPack, e keysLen := len(keys) if keysLen != timestampLen || keysLen != rowIDLen || keysLen != rowDataLen { - return nil, errors.New(string("the length of hashValue, timestamps, rowIDs, RowData are not equal")) + return nil, errors.New("the length of hashValue, timestamps, rowIDs, RowData are not equal") } for index, key := range keys { _, ok := result[key] @@ -534,6 +648,9 @@ func insertRepackFunc(tsMsgs []TsMsg, hashKeys [][]int32) (map[int32]*MsgPack, e } insertMsg := &InsertMsg{ + BaseMsg: BaseMsg{ + ctx: request.GetContext(), + }, InsertRequest: sliceRequest, } result[key].Msgs = append(result[key].Msgs, insertMsg) @@ -546,7 +663,7 @@ func deleteRepackFunc(tsMsgs []TsMsg, hashKeys [][]int32) (map[int32]*MsgPack, e result := make(map[int32]*MsgPack) for i, request := range tsMsgs { if request.Type() != internalPb.MsgType_kDelete { - return nil, errors.New(string("msg's must be Delete")) + return nil, errors.New("msg's must be Delete") } deleteRequest := request.(*DeleteMsg) keys := hashKeys[i] @@ -556,7 +673,7 @@ func deleteRepackFunc(tsMsgs []TsMsg, hashKeys [][]int32) (map[int32]*MsgPack, e keysLen := len(keys) if keysLen != timestampLen || keysLen != primaryKeysLen { - return nil, errors.New(string("the length of hashValue, timestamps, primaryKeys are not equal")) + return nil, errors.New("the length of hashValue, timestamps, primaryKeys are not equal") } for index, key := range keys { @@ -590,7 +707,7 @@ func defaultRepackFunc(tsMsgs []TsMsg, hashKeys [][]int32) (map[int32]*MsgPack, for i, request := range tsMsgs { keys := hashKeys[i] if len(keys) != 1 { - return nil, errors.New(string("len(msg.hashValue) must equal 1")) + return nil, errors.New("len(msg.hashValue) must equal 1") } key := keys[0] _, ok := result[key] diff --git a/internal/proto/internal_msg.proto b/internal/proto/internal_msg.proto index cb934231a2acdce48430f9355f4707dea9ccfd82..6dbfb483536c716f3c07338e26293da2ae14208d 100644 --- a/internal/proto/internal_msg.proto +++ b/internal/proto/internal_msg.proto @@ -272,6 +272,8 @@ message FlushMsg { MsgType msg_type = 1; int64 segmentID = 2; uint64 timestamp = 3; + int64 collectionID = 4; + string partitionTag = 5; } diff --git a/internal/proto/internalpb/internal_msg.pb.go b/internal/proto/internalpb/internal_msg.pb.go index d159b04dde1455ca0828481abee5a287c49a72c4..d290d5e2745d21ae359f5dd5760be813515d1c24 100644 --- a/internal/proto/internalpb/internal_msg.pb.go +++ b/internal/proto/internalpb/internal_msg.pb.go @@ -1881,6 +1881,8 @@ type FlushMsg struct { MsgType MsgType `protobuf:"varint,1,opt,name=msg_type,json=msgType,proto3,enum=milvus.proto.internal.MsgType" json:"msg_type,omitempty"` SegmentID int64 `protobuf:"varint,2,opt,name=segmentID,proto3" json:"segmentID,omitempty"` Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + CollectionID int64 `protobuf:"varint,4,opt,name=collectionID,proto3" json:"collectionID,omitempty"` + PartitionTag string `protobuf:"bytes,5,opt,name=partitionTag,proto3" json:"partitionTag,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1932,6 +1934,20 @@ func (m *FlushMsg) GetTimestamp() uint64 { return 0 } +func (m *FlushMsg) GetCollectionID() int64 { + if m != nil { + return m.CollectionID + } + return 0 +} + +func (m *FlushMsg) GetPartitionTag() string { + if m != nil { + return m.PartitionTag + } + return "" +} + type Key2Seg struct { RowID int64 `protobuf:"varint,1,opt,name=rowID,proto3" json:"rowID,omitempty"` PrimaryKey int64 `protobuf:"varint,2,opt,name=primary_key,json=primaryKey,proto3" json:"primary_key,omitempty"` @@ -2661,121 +2677,122 @@ func init() { func init() { proto.RegisterFile("internal_msg.proto", fileDescriptor_7eb37f6b80b23116) } var fileDescriptor_7eb37f6b80b23116 = []byte{ - // 1852 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x99, 0xcd, 0x6f, 0x23, 0x49, - 0x15, 0xc0, 0xb7, 0xbb, 0xfd, 0x11, 0x3f, 0x3b, 0x4e, 0x4f, 0x25, 0x99, 0xf1, 0xec, 0x2c, 0x3b, - 0x99, 0x1e, 0xc4, 0x86, 0x45, 0x24, 0x90, 0xe1, 0xc0, 0xde, 0x20, 0xb1, 0x96, 0x35, 0x43, 0x46, - 0xa1, 0x13, 0x2d, 0x12, 0x5a, 0xa9, 0xd5, 0xb1, 0x5f, 0xec, 0x52, 0x7f, 0x39, 0x55, 0xed, 0x49, - 0x3c, 0x07, 0x24, 0xc4, 0x9c, 0x11, 0x1f, 0xe2, 0xc0, 0x8d, 0x3b, 0xec, 0x8a, 0x05, 0xf1, 0x3f, - 0xf0, 0x29, 0x24, 0xfe, 0x0b, 0x38, 0x80, 0xc4, 0x2e, 0x07, 0x6e, 0xa8, 0xaa, 0xfa, 0xc3, 0x9d, - 0xd8, 0x4e, 0xb4, 0xc9, 0x2c, 0xb3, 0xda, 0xb9, 0x75, 0x3d, 0x57, 0x57, 0xbf, 0xf7, 0x7b, 0x1f, - 0x55, 0xf5, 0x0c, 0x84, 0x86, 0x31, 0xb2, 0xd0, 0xf5, 0x9d, 0x80, 0xf7, 0x37, 0x86, 0x2c, 0x8a, - 0x23, 0xb2, 0x1a, 0x50, 0xff, 0xf1, 0x88, 0xab, 0xd1, 0x46, 0x3a, 0xe1, 0xe5, 0x46, 0x37, 0x0a, - 0x82, 0x28, 0x54, 0xe2, 0x97, 0x6f, 0x70, 0x64, 0x8f, 0x69, 0x17, 0xf3, 0xf7, 0xac, 0x10, 0x6a, - 0x9d, 0xb6, 0x8d, 0xc7, 0x23, 0xe4, 0x31, 0xb9, 0x09, 0x95, 0x21, 0x22, 0xeb, 0xb4, 0x5b, 0xda, - 0x9a, 0xb6, 0x6e, 0xd8, 0xc9, 0x88, 0x3c, 0x80, 0x12, 0x8b, 0x7c, 0x6c, 0xe9, 0x6b, 0xda, 0x7a, - 0x73, 0xeb, 0xee, 0xc6, 0xd4, 0x6f, 0x6d, 0xec, 0x21, 0x32, 0x3b, 0xf2, 0xd1, 0x96, 0x93, 0xc9, - 0x0a, 0x94, 0xbb, 0xd1, 0x28, 0x8c, 0x5b, 0xc6, 0x9a, 0xb6, 0xbe, 0x68, 0xab, 0x81, 0xd5, 0x07, - 0x10, 0xdf, 0xe3, 0xc3, 0x28, 0xe4, 0x48, 0x1e, 0x40, 0x85, 0xc7, 0x6e, 0x3c, 0xe2, 0xf2, 0x83, - 0xf5, 0xad, 0x3b, 0xc5, 0xa5, 0x13, 0xe5, 0xf7, 0xe5, 0x14, 0x3b, 0x99, 0x4a, 0x9a, 0xa0, 0x77, - 0xda, 0x52, 0x17, 0xc3, 0xd6, 0x3b, 0xed, 0x19, 0x1f, 0x8a, 0x00, 0x0e, 0x78, 0xf4, 0x31, 0x5a, - 0xf6, 0x18, 0xea, 0xf2, 0x83, 0x57, 0x31, 0xed, 0x15, 0xa8, 0xc5, 0x34, 0x40, 0x1e, 0xbb, 0xc1, - 0x50, 0xea, 0x54, 0xb2, 0x73, 0xc1, 0x8c, 0xef, 0x3e, 0xd5, 0xa0, 0xb1, 0x8f, 0xfd, 0xdc, 0x8b, - 0xd9, 0x34, 0x6d, 0x62, 0x9a, 0x58, 0xba, 0x3b, 0x70, 0xc3, 0x10, 0xfd, 0x04, 0x5e, 0xd9, 0xce, - 0x05, 0xe4, 0x0e, 0xd4, 0xba, 0x91, 0xef, 0x3b, 0xa1, 0x1b, 0xa0, 0x5c, 0xbe, 0x66, 0x2f, 0x08, - 0xc1, 0x23, 0x37, 0x40, 0x72, 0x1f, 0x16, 0x87, 0x2e, 0x8b, 0x69, 0x4c, 0xa3, 0xd0, 0x89, 0xdd, - 0x7e, 0xab, 0x24, 0x27, 0x34, 0x32, 0xe1, 0x81, 0xdb, 0xb7, 0xde, 0xd3, 0x80, 0x7c, 0x9d, 0x73, - 0xda, 0x0f, 0x0b, 0xca, 0x5c, 0x2b, 0xf8, 0x87, 0xb0, 0x34, 0x44, 0xe6, 0x24, 0x6a, 0x3b, 0x0c, - 0x8f, 0x5b, 0xc6, 0x9a, 0xb1, 0x5e, 0xdf, 0xba, 0x3f, 0xe3, 0xfd, 0x49, 0x55, 0xec, 0xc5, 0x21, - 0xb2, 0x1d, 0xf5, 0xaa, 0x8d, 0xc7, 0xd6, 0x07, 0x1a, 0x2c, 0xc9, 0xdf, 0x95, 0xd6, 0x01, 0x86, - 0x12, 0x1d, 0x17, 0xa2, 0x44, 0x59, 0x35, 0xb8, 0x00, 0xdd, 0x54, 0xaf, 0x14, 0x81, 0x96, 0x2e, - 0x02, 0x5a, 0x3e, 0x0f, 0x94, 0xdc, 0x85, 0x3a, 0x9e, 0x0e, 0x29, 0x43, 0x47, 0x44, 0x40, 0xab, - 0x22, 0xa3, 0x01, 0x94, 0xe8, 0x80, 0x06, 0x93, 0x11, 0x56, 0xbd, 0x74, 0x84, 0x59, 0x1c, 0x96, - 0x0b, 0x5e, 0x4a, 0xa2, 0xf5, 0x1d, 0xb8, 0x39, 0x49, 0xd6, 0xcd, 0x90, 0xb4, 0x34, 0x09, 0xf8, - 0x73, 0xf3, 0x00, 0xe7, 0x00, 0xed, 0x95, 0x9c, 0x71, 0x2e, 0xb5, 0xfe, 0xab, 0xc1, 0xad, 0x1d, - 0x86, 0x6e, 0x8c, 0x3b, 0x91, 0xef, 0x63, 0x57, 0x98, 0x98, 0x06, 0xc8, 0x1b, 0xb0, 0x10, 0xf0, - 0xbe, 0x13, 0x8f, 0x87, 0x28, 0xa9, 0x37, 0xb7, 0x5e, 0x9d, 0xf1, 0xad, 0x5d, 0xde, 0x3f, 0x18, - 0x0f, 0xd1, 0xae, 0x06, 0xea, 0x81, 0x58, 0xd0, 0xe8, 0x66, 0xeb, 0x65, 0x25, 0xa1, 0x20, 0x13, - 0xde, 0x61, 0x78, 0xdc, 0x69, 0x4b, 0xef, 0x18, 0xb6, 0x1a, 0x14, 0xf3, 0xac, 0x74, 0x36, 0xcf, - 0x5a, 0x50, 0x1d, 0xb2, 0xe8, 0x74, 0xdc, 0x69, 0x4b, 0xc7, 0x18, 0x76, 0x3a, 0x24, 0x5f, 0x86, - 0x0a, 0xef, 0x0e, 0x30, 0x70, 0xa5, 0x3b, 0xea, 0x5b, 0xb7, 0xa7, 0x22, 0xdf, 0xf6, 0xa3, 0x43, - 0x3b, 0x99, 0x68, 0xfd, 0x54, 0x87, 0xd5, 0x36, 0x8b, 0x86, 0x9f, 0x70, 0xcb, 0x77, 0x61, 0x29, - 0x5f, 0x5d, 0x45, 0xb5, 0x42, 0xf0, 0xd9, 0xa2, 0xce, 0xc9, 0x0e, 0xb3, 0x91, 0x9b, 0x2b, 0x22, - 0xde, 0x6e, 0x76, 0x0b, 0x63, 0xeb, 0x9f, 0x1a, 0xac, 0xbc, 0xe5, 0xf2, 0x6b, 0x85, 0x92, 0x19, - 0xac, 0xcf, 0x34, 0xd8, 0x98, 0x63, 0x70, 0xe9, 0x42, 0x83, 0xcb, 0x57, 0x30, 0xf8, 0x03, 0x0d, - 0x6e, 0xb7, 0x91, 0x77, 0x19, 0x3d, 0xc4, 0x4f, 0x8f, 0xd5, 0xbf, 0xd0, 0x60, 0x75, 0x7f, 0x10, - 0x9d, 0x3c, 0xbf, 0x16, 0x5b, 0xbf, 0xd5, 0xe1, 0xa6, 0xaa, 0x4d, 0x7b, 0x69, 0xf5, 0xfd, 0x98, - 0x12, 0x74, 0x0d, 0xea, 0x59, 0xc1, 0xcf, 0xd2, 0x74, 0x52, 0x94, 0x5b, 0x5a, 0x9a, 0x69, 0x69, - 0x79, 0x8e, 0xa5, 0x95, 0xa2, 0x6f, 0xbf, 0x09, 0xcd, 0x7c, 0xd7, 0x91, 0xae, 0x55, 0xfb, 0xc6, - 0xfd, 0xe9, 0xae, 0xcd, 0x70, 0x48, 0xcf, 0xe6, 0x1b, 0x96, 0x74, 0xec, 0xfb, 0x3a, 0xac, 0x88, - 0xaa, 0xf6, 0x82, 0xd9, 0xe5, 0x99, 0xfd, 0x43, 0x83, 0xe5, 0xb7, 0x5c, 0x7e, 0x9d, 0xc8, 0xae, - 0x37, 0xf9, 0xcf, 0x1b, 0x5b, 0xfe, 0xc8, 0xc6, 0xfe, 0x4b, 0x83, 0x56, 0x5a, 0xef, 0x3e, 0x1d, - 0x16, 0x8b, 0x2d, 0x4d, 0xd4, 0xba, 0xe7, 0xd7, 0xda, 0x6b, 0x2e, 0xee, 0xff, 0xd6, 0x61, 0xb1, - 0x13, 0x72, 0x64, 0xf1, 0x33, 0xb3, 0xf4, 0xb5, 0xf3, 0x1a, 0xab, 0xcb, 0xc9, 0x19, 0x5d, 0x2e, - 0x75, 0x45, 0x11, 0xdc, 0x38, 0xf6, 0xc5, 0x89, 0x34, 0x3b, 0xdf, 0xe4, 0x82, 0xe2, 0x29, 0x5f, - 0x95, 0x81, 0x89, 0x53, 0xfe, 0x04, 0xd5, 0x6a, 0x91, 0xea, 0xab, 0x00, 0x19, 0x7c, 0xde, 0x5a, - 0x58, 0x33, 0xc4, 0x31, 0x3d, 0x97, 0x88, 0x1b, 0x10, 0x8b, 0x4e, 0x3a, 0x6d, 0xde, 0xaa, 0xad, - 0x19, 0xe2, 0x06, 0xa4, 0x46, 0xe4, 0x2b, 0xb0, 0xc0, 0xa2, 0x13, 0xa7, 0xe7, 0xc6, 0x6e, 0x0b, - 0xe4, 0x21, 0x7b, 0xce, 0x69, 0xb2, 0xca, 0xa2, 0x93, 0xb6, 0x1b, 0xbb, 0xd6, 0x53, 0x1d, 0x16, - 0xdb, 0xe8, 0x63, 0x8c, 0xff, 0x7f, 0xe8, 0x05, 0x62, 0xa5, 0x39, 0xc4, 0xca, 0xf3, 0x88, 0x55, - 0xce, 0x11, 0xbb, 0x07, 0x8d, 0x21, 0xa3, 0x81, 0xcb, 0xc6, 0x8e, 0x87, 0x63, 0x71, 0xbd, 0x31, - 0x64, 0x95, 0x57, 0xb2, 0x87, 0x38, 0xe6, 0xd6, 0x87, 0x1a, 0x2c, 0xee, 0xa3, 0xcb, 0xba, 0x83, - 0x67, 0x86, 0x61, 0x42, 0x7f, 0xa3, 0xa8, 0xff, 0xfc, 0x33, 0xf4, 0xe7, 0xc1, 0x64, 0xc8, 0x47, - 0x7e, 0xec, 0xe4, 0x70, 0x14, 0x80, 0x25, 0x25, 0xdf, 0xc9, 0x10, 0x6d, 0x42, 0xf9, 0x78, 0x84, - 0x6c, 0x7c, 0xf1, 0x6d, 0x42, 0xcd, 0xb3, 0xfe, 0xa6, 0x81, 0xb9, 0x3f, 0xe6, 0x3b, 0x51, 0x78, - 0x44, 0xfb, 0xcf, 0x9d, 0xe5, 0x04, 0x4a, 0xd2, 0x5f, 0xe5, 0x35, 0x63, 0xbd, 0x66, 0xcb, 0x67, - 0xe1, 0x4b, 0x0f, 0xc7, 0xce, 0x90, 0xe1, 0x11, 0x3d, 0x45, 0xe5, 0xed, 0x9a, 0x5d, 0xf7, 0x70, - 0xbc, 0x97, 0x88, 0xac, 0xbf, 0xea, 0xd0, 0x48, 0x7d, 0x29, 0xf8, 0x5c, 0xc5, 0xa0, 0xfc, 0x4e, - 0xac, 0x5f, 0xbe, 0xeb, 0x32, 0xfd, 0xa6, 0x34, 0xbb, 0x8e, 0xde, 0x83, 0x86, 0x74, 0x87, 0x13, - 0x46, 0x3d, 0xcc, 0xbc, 0x5b, 0x97, 0xb2, 0x47, 0x52, 0x54, 0x04, 0x55, 0xb9, 0x4c, 0x88, 0x54, - 0xa7, 0x87, 0x08, 0x81, 0xd2, 0x80, 0xc6, 0xaa, 0xae, 0x34, 0x6c, 0xf9, 0x4c, 0xee, 0x42, 0x3d, - 0xc0, 0x98, 0xd1, 0xae, 0x42, 0x54, 0x93, 0xc9, 0x09, 0x4a, 0x24, 0x28, 0x58, 0xdf, 0x83, 0xfa, - 0x01, 0x0d, 0xf0, 0x80, 0x76, 0xbd, 0x5d, 0xde, 0xbf, 0x0a, 0xcf, 0xbc, 0x7d, 0xa3, 0x17, 0xda, - 0x37, 0x73, 0xb7, 0x20, 0xeb, 0xfb, 0x1a, 0x2c, 0xbc, 0xe9, 0x8f, 0xf8, 0xe0, 0x8a, 0x5f, 0x2f, - 0x14, 0x6c, 0x7d, 0x4a, 0xc1, 0x9e, 0xa3, 0xc3, 0xcf, 0x35, 0xa8, 0x3e, 0xc4, 0xf1, 0xd6, 0x3e, - 0xf6, 0xa5, 0x83, 0x45, 0xd1, 0x4d, 0xdb, 0x3a, 0x72, 0x20, 0x30, 0x4e, 0x94, 0x99, 0x64, 0x7d, - 0xc8, 0xab, 0xcc, 0x05, 0xfb, 0xec, 0x6d, 0x58, 0xa0, 0xdc, 0x79, 0xec, 0xfa, 0xb4, 0x27, 0x03, - 0x64, 0xc1, 0xae, 0x52, 0xfe, 0xb6, 0x18, 0x8a, 0x02, 0x97, 0xa9, 0xa9, 0xd2, 0xc1, 0xb0, 0x27, - 0x24, 0xd6, 0x3b, 0x00, 0x89, 0x6a, 0x02, 0x50, 0x16, 0x7e, 0xda, 0x64, 0xf8, 0x7d, 0x15, 0xaa, - 0x1e, 0x8e, 0xb7, 0x38, 0xf6, 0x5b, 0xba, 0xdc, 0x1d, 0x66, 0x51, 0x4b, 0x56, 0xb2, 0xd3, 0xe9, - 0xd6, 0x0f, 0x74, 0xa8, 0x7d, 0x2b, 0x72, 0x7b, 0x9d, 0xb0, 0x87, 0xa7, 0xcf, 0x14, 0xff, 0x11, - 0x45, 0xbf, 0xf7, 0x28, 0xdf, 0x20, 0x72, 0x81, 0xc8, 0x1e, 0x39, 0xc8, 0xb3, 0x27, 0x19, 0x0a, - 0xec, 0x54, 0x68, 0xe6, 0x0c, 0xdd, 0x78, 0x90, 0x16, 0x0b, 0x90, 0xa2, 0x3d, 0x21, 0x21, 0x6d, - 0x68, 0xa4, 0x13, 0x98, 0x1b, 0xa8, 0x92, 0x51, 0xdf, 0xba, 0x37, 0x35, 0x93, 0x1f, 0xe2, 0xf8, - 0x6d, 0xd7, 0x1f, 0xe1, 0x9e, 0x4b, 0x99, 0x5d, 0x4f, 0x16, 0x11, 0x6f, 0x59, 0x4f, 0x35, 0x00, - 0x49, 0x40, 0x24, 0xfb, 0xf9, 0x45, 0xb5, 0x8f, 0xb2, 0x28, 0xf9, 0x12, 0xac, 0x84, 0xa3, 0xc0, - 0x61, 0xe8, 0xbb, 0x31, 0xf6, 0x9c, 0x04, 0x06, 0x4f, 0xe0, 0x90, 0x70, 0x14, 0xd8, 0xea, 0xa7, - 0xfd, 0xe4, 0x17, 0xeb, 0x87, 0x1a, 0xc0, 0x9b, 0xc2, 0x72, 0xa5, 0xc6, 0xd9, 0x3b, 0x8e, 0x36, - 0xe5, 0x8e, 0x33, 0x81, 0x4e, 0x2f, 0xa2, 0xdb, 0x4e, 0xd1, 0x89, 0xc2, 0xc5, 0x93, 0xde, 0xe7, - 0xbd, 0x19, 0xee, 0xcc, 0x8d, 0x4f, 0xe8, 0xca, 0x67, 0xeb, 0x67, 0xaa, 0x5d, 0x2c, 0xb4, 0x53, - 0x2a, 0x15, 0xbc, 0xac, 0x9d, 0xf5, 0xb2, 0xac, 0x35, 0x41, 0xc4, 0xc6, 0x0e, 0xa7, 0x4f, 0x30, - 0x4d, 0x12, 0x25, 0xda, 0xa7, 0x4f, 0x50, 0xa4, 0x81, 0x44, 0x12, 0x9d, 0xf0, 0x74, 0xb7, 0x10, - 0x18, 0xa2, 0x13, 0x4e, 0xbe, 0x00, 0x37, 0x18, 0x76, 0x31, 0x8c, 0xfd, 0xb1, 0x13, 0x44, 0x3d, - 0x7a, 0x44, 0x31, 0x4d, 0x15, 0x33, 0xfd, 0x61, 0x37, 0x91, 0x5b, 0x7f, 0xd7, 0xa0, 0xf9, 0xed, - 0xb4, 0x82, 0x2a, 0xcd, 0x9e, 0x41, 0xdd, 0xfa, 0x9a, 0x34, 0xb6, 0xc0, 0x6f, 0x4e, 0xef, 0x38, - 0x83, 0x64, 0x2f, 0x70, 0xec, 0x2b, 0xa5, 0xb6, 0xa1, 0x2e, 0xdd, 0x91, 0xac, 0x51, 0x9a, 0xeb, - 0x83, 0xdc, 0xf3, 0x36, 0x1c, 0x65, 0xcf, 0xd6, 0x2f, 0x75, 0x20, 0xaa, 0xe7, 0x20, 0x9d, 0xf4, - 0xdc, 0x5d, 0x14, 0x5e, 0x9b, 0x7e, 0x51, 0x38, 0x7f, 0x02, 0xfc, 0x0c, 0x28, 0xb3, 0xf2, 0x86, - 0x60, 0xa1, 0x08, 0xb4, 0xa1, 0x81, 0xa7, 0x31, 0x73, 0xd3, 0xa4, 0xab, 0x5e, 0x3a, 0xe9, 0xe4, - 0x6b, 0x49, 0x26, 0xbf, 0xab, 0xc3, 0x4a, 0x7a, 0x95, 0x7c, 0xc1, 0xeb, 0x62, 0x5e, 0xbf, 0xd3, - 0xe1, 0x95, 0x02, 0xaf, 0x3d, 0x16, 0xf5, 0x19, 0x72, 0xfe, 0x82, 0xdb, 0x3c, 0x6e, 0xaf, 0xff, + // 1867 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0xcd, 0x6f, 0x24, 0x47, + 0x15, 0x4f, 0x77, 0xcf, 0x87, 0xe7, 0xcd, 0x78, 0xdc, 0x5b, 0xb6, 0x77, 0x67, 0xb3, 0x21, 0xeb, + 0xed, 0x45, 0xc4, 0x04, 0x61, 0x83, 0x97, 0x03, 0xb9, 0x81, 0x3d, 0x0a, 0x19, 0x16, 0xaf, 0x4c, + 0xdb, 0x0a, 0x12, 0x8a, 0xd4, 0x6a, 0xcf, 0x3c, 0xcf, 0x94, 0xfa, 0x6b, 0x5c, 0xd5, 0xb3, 0xf6, + 0xec, 0x81, 0x0b, 0x7b, 0x46, 0x7c, 0x88, 0x03, 0x37, 0xee, 0x90, 0x88, 0x80, 0xf8, 0x1f, 0x08, + 0x1f, 0x42, 0xe2, 0xbf, 0x80, 0x03, 0x48, 0x24, 0x1c, 0xb8, 0xa1, 0xaa, 0xea, 0x8f, 0x69, 0x7b, + 0x66, 0x6c, 0xc5, 0xde, 0xb0, 0x51, 0xf6, 0xd6, 0xf5, 0xba, 0xba, 0xea, 0xbd, 0xdf, 0x7b, 0xef, + 0x57, 0xf5, 0x5e, 0x03, 0xa1, 0x61, 0x8c, 0x2c, 0x74, 0x7d, 0x27, 0xe0, 0xfd, 0x8d, 0x21, 0x8b, + 0xe2, 0x88, 0xac, 0x06, 0xd4, 0x7f, 0x3c, 0xe2, 0x6a, 0xb4, 0x91, 0x4e, 0x78, 0xb9, 0xd1, 0x8d, + 0x82, 0x20, 0x0a, 0x95, 0xf8, 0xe5, 0x1b, 0x1c, 0xd9, 0x63, 0xda, 0xc5, 0xfc, 0x3b, 0x2b, 0x84, + 0x5a, 0xa7, 0x6d, 0xe3, 0xf1, 0x08, 0x79, 0x4c, 0x6e, 0x42, 0x65, 0x88, 0xc8, 0x3a, 0xed, 0x96, + 0xb6, 0xa6, 0xad, 0x1b, 0x76, 0x32, 0x22, 0x0f, 0xa0, 0xc4, 0x22, 0x1f, 0x5b, 0xfa, 0x9a, 0xb6, + 0xde, 0xdc, 0xba, 0xbb, 0x31, 0x75, 0xaf, 0x8d, 0x3d, 0x44, 0x66, 0x47, 0x3e, 0xda, 0x72, 0x32, + 0x59, 0x81, 0x72, 0x37, 0x1a, 0x85, 0x71, 0xcb, 0x58, 0xd3, 0xd6, 0x17, 0x6d, 0x35, 0xb0, 0xfa, + 0x00, 0x62, 0x3f, 0x3e, 0x8c, 0x42, 0x8e, 0xe4, 0x01, 0x54, 0x78, 0xec, 0xc6, 0x23, 0x2e, 0x37, + 0xac, 0x6f, 0xdd, 0x29, 0x2e, 0x9d, 0x28, 0xbf, 0x2f, 0xa7, 0xd8, 0xc9, 0x54, 0xd2, 0x04, 0xbd, + 0xd3, 0x96, 0xba, 0x18, 0xb6, 0xde, 0x69, 0xcf, 0xd8, 0x28, 0x02, 0x38, 0xe0, 0xd1, 0x27, 0x68, + 0xd9, 0x63, 0xa8, 0xcb, 0x0d, 0xaf, 0x62, 0xda, 0x2b, 0x50, 0x8b, 0x69, 0x80, 0x3c, 0x76, 0x83, + 0xa1, 0xd4, 0xa9, 0x64, 0xe7, 0x82, 0x19, 0xfb, 0x3e, 0xd5, 0xa0, 0xb1, 0x8f, 0xfd, 0xdc, 0x8b, + 0xd9, 0x34, 0x6d, 0x62, 0x9a, 0x58, 0xba, 0x3b, 0x70, 0xc3, 0x10, 0xfd, 0x04, 0xbc, 0xb2, 0x9d, + 0x0b, 0xc8, 0x1d, 0xa8, 0x75, 0x23, 0xdf, 0x77, 0x42, 0x37, 0x40, 0xb9, 0x7c, 0xcd, 0x5e, 0x10, + 0x82, 0x47, 0x6e, 0x80, 0xe4, 0x3e, 0x2c, 0x0e, 0x5d, 0x16, 0xd3, 0x98, 0x46, 0xa1, 0x13, 0xbb, + 0xfd, 0x56, 0x49, 0x4e, 0x68, 0x64, 0xc2, 0x03, 0xb7, 0x6f, 0xbd, 0xa7, 0x01, 0xf9, 0x26, 0xe7, + 0xb4, 0x1f, 0x16, 0x94, 0xb9, 0x56, 0xe0, 0x1f, 0xc2, 0xd2, 0x10, 0x99, 0x93, 0xa8, 0xed, 0x30, + 0x3c, 0x6e, 0x19, 0x6b, 0xc6, 0x7a, 0x7d, 0xeb, 0xfe, 0x8c, 0xef, 0x27, 0x55, 0xb1, 0x17, 0x87, + 0xc8, 0x76, 0xd4, 0xa7, 0x36, 0x1e, 0x5b, 0x1f, 0x6a, 0xb0, 0x24, 0xdf, 0x2b, 0xad, 0x03, 0x0c, + 0x25, 0x74, 0x5c, 0x88, 0x12, 0x65, 0xd5, 0xe0, 0x02, 0xe8, 0xa6, 0x7a, 0xa5, 0x08, 0x68, 0xe9, + 0x22, 0x40, 0xcb, 0xe7, 0x01, 0x25, 0x77, 0xa1, 0x8e, 0xa7, 0x43, 0xca, 0xd0, 0x11, 0x11, 0xd0, + 0xaa, 0xc8, 0x68, 0x00, 0x25, 0x3a, 0xa0, 0xc1, 0x64, 0x84, 0x55, 0x2f, 0x1d, 0x61, 0x16, 0x87, + 0xe5, 0x82, 0x97, 0x92, 0x68, 0x7d, 0x07, 0x6e, 0x4e, 0x22, 0xeb, 0x66, 0x90, 0xb4, 0x34, 0x09, + 0xf0, 0x17, 0xe6, 0x01, 0x9c, 0x03, 0x68, 0xaf, 0xe4, 0x18, 0xe7, 0x52, 0xeb, 0xbf, 0x1a, 0xdc, + 0xda, 0x61, 0xe8, 0xc6, 0xb8, 0x13, 0xf9, 0x3e, 0x76, 0x85, 0x89, 0x69, 0x80, 0xbc, 0x01, 0x0b, + 0x01, 0xef, 0x3b, 0xf1, 0x78, 0x88, 0x12, 0xf5, 0xe6, 0xd6, 0xab, 0x33, 0xf6, 0xda, 0xe5, 0xfd, + 0x83, 0xf1, 0x10, 0xed, 0x6a, 0xa0, 0x1e, 0x88, 0x05, 0x8d, 0x6e, 0xb6, 0x5e, 0x46, 0x09, 0x05, + 0x99, 0xf0, 0x0e, 0xc3, 0xe3, 0x4e, 0x5b, 0x7a, 0xc7, 0xb0, 0xd5, 0xa0, 0x98, 0x67, 0xa5, 0xb3, + 0x79, 0xd6, 0x82, 0xea, 0x90, 0x45, 0xa7, 0xe3, 0x4e, 0x5b, 0x3a, 0xc6, 0xb0, 0xd3, 0x21, 0xf9, + 0x2a, 0x54, 0x78, 0x77, 0x80, 0x81, 0x2b, 0xdd, 0x51, 0xdf, 0xba, 0x3d, 0x15, 0xf2, 0x6d, 0x3f, + 0x3a, 0xb4, 0x93, 0x89, 0xd6, 0xcf, 0x74, 0x58, 0x6d, 0xb3, 0x68, 0xf8, 0x29, 0xb7, 0x7c, 0x17, + 0x96, 0xf2, 0xd5, 0x55, 0x54, 0x2b, 0x08, 0x3e, 0x5f, 0xd4, 0x39, 0x39, 0x61, 0x36, 0x72, 0x73, + 0x45, 0xc4, 0xdb, 0xcd, 0x6e, 0x61, 0x6c, 0xfd, 0x53, 0x83, 0x95, 0xb7, 0x5c, 0x7e, 0xad, 0xa0, + 0x64, 0x06, 0xeb, 0x33, 0x0d, 0x36, 0xe6, 0x18, 0x5c, 0xba, 0xd0, 0xe0, 0xf2, 0x15, 0x0c, 0xfe, + 0x50, 0x83, 0xdb, 0x6d, 0xe4, 0x5d, 0x46, 0x0f, 0xf1, 0xb3, 0x63, 0xf5, 0x2f, 0x35, 0x58, 0xdd, + 0x1f, 0x44, 0x27, 0xcf, 0xaf, 0xc5, 0xd6, 0xef, 0x74, 0xb8, 0xa9, 0xb8, 0x69, 0x2f, 0x65, 0xdf, + 0x4f, 0x28, 0x41, 0xd7, 0xa0, 0x9e, 0x11, 0x7e, 0x96, 0xa6, 0x93, 0xa2, 0xdc, 0xd2, 0xd2, 0x4c, + 0x4b, 0xcb, 0x73, 0x2c, 0xad, 0x14, 0x7d, 0xfb, 0x6d, 0x68, 0xe6, 0xa7, 0x8e, 0x74, 0xad, 0x3a, + 0x37, 0xee, 0x4f, 0x77, 0x6d, 0x06, 0x87, 0xf4, 0x6c, 0x7e, 0x60, 0x49, 0xc7, 0xbe, 0xaf, 0xc3, + 0x8a, 0x60, 0xb5, 0x17, 0x98, 0x5d, 0x1e, 0xb3, 0x7f, 0x68, 0xb0, 0xfc, 0x96, 0xcb, 0xaf, 0x13, + 0xb2, 0xeb, 0x4d, 0xfe, 0xf3, 0xc6, 0x96, 0x3f, 0xb6, 0xb1, 0xff, 0xd2, 0xa0, 0x95, 0xf2, 0xdd, + 0x67, 0xc3, 0x62, 0x71, 0xa4, 0x09, 0xae, 0x7b, 0x7e, 0xad, 0xbd, 0x66, 0x72, 0xff, 0xb7, 0x0e, + 0x8b, 0x9d, 0x90, 0x23, 0x8b, 0x9f, 0x99, 0xa5, 0xaf, 0x9d, 0xd7, 0x58, 0x15, 0x27, 0x67, 0x74, + 0xb9, 0x54, 0x89, 0x22, 0x70, 0xe3, 0xd8, 0x17, 0x37, 0xd2, 0xec, 0x7e, 0x93, 0x0b, 0x8a, 0xb7, + 0x7c, 0x45, 0x03, 0x13, 0xb7, 0xfc, 0x09, 0x54, 0xab, 0x45, 0x54, 0x5f, 0x05, 0xc8, 0xc0, 0xe7, + 0xad, 0x85, 0x35, 0x43, 0x5c, 0xd3, 0x73, 0x89, 0xa8, 0x80, 0x58, 0x74, 0xd2, 0x69, 0xf3, 0x56, + 0x6d, 0xcd, 0x10, 0x15, 0x90, 0x1a, 0x91, 0xaf, 0xc1, 0x02, 0x8b, 0x4e, 0x9c, 0x9e, 0x1b, 0xbb, + 0x2d, 0x90, 0x97, 0xec, 0x39, 0xb7, 0xc9, 0x2a, 0x8b, 0x4e, 0xda, 0x6e, 0xec, 0x5a, 0x4f, 0x75, + 0x58, 0x6c, 0xa3, 0x8f, 0x31, 0xfe, 0xff, 0x41, 0x2f, 0x20, 0x56, 0x9a, 0x83, 0x58, 0x79, 0x1e, + 0x62, 0x95, 0x73, 0x88, 0xdd, 0x83, 0xc6, 0x90, 0xd1, 0xc0, 0x65, 0x63, 0xc7, 0xc3, 0xb1, 0x28, + 0x6f, 0x0c, 0xc9, 0xf2, 0x4a, 0xf6, 0x10, 0xc7, 0xdc, 0xfa, 0x48, 0x83, 0xc5, 0x7d, 0x74, 0x59, + 0x77, 0xf0, 0xcc, 0x60, 0x98, 0xd0, 0xdf, 0x28, 0xea, 0x3f, 0xff, 0x0e, 0xfd, 0x45, 0x30, 0x19, + 0xf2, 0x91, 0x1f, 0x3b, 0x39, 0x38, 0x0a, 0x80, 0x25, 0x25, 0xdf, 0xc9, 0x20, 0xda, 0x84, 0xf2, + 0xf1, 0x08, 0xd9, 0xf8, 0xe2, 0x6a, 0x42, 0xcd, 0xb3, 0xfe, 0xa6, 0x81, 0xb9, 0x3f, 0xe6, 0x3b, + 0x51, 0x78, 0x44, 0xfb, 0xcf, 0x9d, 0xe5, 0x04, 0x4a, 0xd2, 0x5f, 0xe5, 0x35, 0x63, 0xbd, 0x66, + 0xcb, 0x67, 0xe1, 0x4b, 0x0f, 0xc7, 0xce, 0x90, 0xe1, 0x11, 0x3d, 0x45, 0xe5, 0xed, 0x9a, 0x5d, + 0xf7, 0x70, 0xbc, 0x97, 0x88, 0xac, 0xbf, 0xea, 0xd0, 0x48, 0x7d, 0x29, 0xf0, 0xb9, 0x8a, 0x41, + 0x79, 0x4d, 0xac, 0x5f, 0xbe, 0xeb, 0x32, 0xbd, 0x52, 0x9a, 0xcd, 0xa3, 0xf7, 0xa0, 0x21, 0xdd, + 0xe1, 0x84, 0x51, 0x0f, 0x33, 0xef, 0xd6, 0xa5, 0xec, 0x91, 0x14, 0x15, 0x81, 0xaa, 0x5c, 0x26, + 0x44, 0xaa, 0xd3, 0x43, 0x84, 0x40, 0x69, 0x40, 0x63, 0xc5, 0x2b, 0x0d, 0x5b, 0x3e, 0x93, 0xbb, + 0x50, 0x0f, 0x30, 0x66, 0xb4, 0xab, 0x20, 0xaa, 0xc9, 0xe4, 0x04, 0x25, 0x12, 0x28, 0x58, 0x3f, + 0x80, 0xfa, 0x01, 0x0d, 0xf0, 0x80, 0x76, 0xbd, 0x5d, 0xde, 0xbf, 0x0a, 0x9e, 0x79, 0xfb, 0x46, + 0x2f, 0xb4, 0x6f, 0xe6, 0x1e, 0x41, 0xd6, 0x07, 0x1a, 0x2c, 0xbc, 0xe9, 0x8f, 0xf8, 0xe0, 0x8a, + 0xbb, 0x17, 0x08, 0x5b, 0x9f, 0x42, 0xd8, 0x73, 0x8e, 0xc1, 0xb3, 0xb7, 0xc9, 0xd2, 0x94, 0xdb, + 0xa4, 0x05, 0x85, 0x03, 0x62, 0x5a, 0x1b, 0xc6, 0xfa, 0x85, 0x06, 0xd5, 0x87, 0x38, 0xde, 0xda, + 0xc7, 0xbe, 0x0c, 0x14, 0x41, 0xde, 0x69, 0x7b, 0x48, 0x0e, 0x84, 0x3b, 0x26, 0xe8, 0x2a, 0xd1, + 0x13, 0x72, 0xb6, 0xba, 0x40, 0xd1, 0xdb, 0xb0, 0x40, 0xb9, 0xf3, 0xd8, 0xf5, 0x69, 0x4f, 0x2a, + 0xb9, 0x60, 0x57, 0x29, 0x7f, 0x5b, 0x0c, 0x05, 0x51, 0x66, 0xe6, 0xaa, 0xb4, 0x32, 0xec, 0x09, + 0x89, 0xf5, 0x0e, 0x40, 0xa2, 0x9a, 0x00, 0x3a, 0x0b, 0x63, 0x6d, 0x32, 0x8c, 0xbf, 0x0e, 0x55, + 0x0f, 0xc7, 0x5b, 0x1c, 0xfb, 0x2d, 0x5d, 0x9e, 0x32, 0xb3, 0xd0, 0x4f, 0x56, 0xb2, 0xd3, 0xe9, + 0xd6, 0x0f, 0x75, 0xa8, 0x7d, 0x27, 0x72, 0x7b, 0x9d, 0xb0, 0x87, 0xa7, 0xcf, 0xd4, 0x8d, 0x47, + 0x14, 0xfd, 0xde, 0xa3, 0xfc, 0xa0, 0xc9, 0x05, 0x22, 0x0b, 0xe5, 0x20, 0xcf, 0xc2, 0x64, 0x28, + 0x60, 0xa7, 0x42, 0x33, 0x67, 0xe8, 0xc6, 0x83, 0x94, 0x74, 0x40, 0x8a, 0xf6, 0x84, 0x84, 0xb4, + 0xa1, 0x91, 0x4e, 0x60, 0x6e, 0xa0, 0xa8, 0xa7, 0xbe, 0x75, 0x6f, 0x2a, 0x23, 0x3c, 0xc4, 0xf1, + 0xdb, 0xae, 0x3f, 0xc2, 0x3d, 0x97, 0x32, 0xbb, 0x9e, 0x2c, 0x22, 0xbe, 0xb2, 0x9e, 0x6a, 0x00, + 0x12, 0x01, 0x41, 0x1a, 0xe7, 0x17, 0xd5, 0x3e, 0xce, 0xa2, 0xe4, 0x2b, 0xb0, 0x12, 0x8e, 0x02, + 0x87, 0xa1, 0xef, 0xc6, 0xd8, 0x73, 0x12, 0x30, 0x78, 0x02, 0x0e, 0x09, 0x47, 0x81, 0xad, 0x5e, + 0xed, 0x27, 0x6f, 0xac, 0x1f, 0x69, 0x00, 0x6f, 0x0a, 0xcb, 0x95, 0x1a, 0x67, 0xa3, 0x5b, 0x9b, + 0x12, 0xdd, 0x13, 0xd0, 0xe9, 0x45, 0xe8, 0xb6, 0x53, 0xe8, 0x04, 0x01, 0xf2, 0xa4, 0x87, 0x7a, + 0x6f, 0x86, 0x3b, 0x73, 0xe3, 0x13, 0x74, 0xe5, 0xb3, 0xf5, 0x73, 0xd5, 0x76, 0x16, 0xda, 0x29, + 0x95, 0x0a, 0x5e, 0xd6, 0xce, 0x7a, 0x59, 0x72, 0x56, 0x10, 0xb1, 0xb1, 0xc3, 0xe9, 0x13, 0x4c, + 0x93, 0x44, 0x89, 0xf6, 0xe9, 0x13, 0x14, 0x69, 0x20, 0x21, 0x89, 0x4e, 0x78, 0x7a, 0xea, 0x08, + 0x18, 0xa2, 0x13, 0x4e, 0xbe, 0x04, 0x37, 0x18, 0x76, 0x31, 0x8c, 0xfd, 0xb1, 0x13, 0x44, 0x3d, + 0x7a, 0x44, 0x31, 0x4d, 0x15, 0x33, 0x7d, 0xb1, 0x9b, 0xc8, 0xad, 0xbf, 0x6b, 0xd0, 0xfc, 0x6e, + 0xca, 0xc4, 0x4a, 0xb3, 0x67, 0xc0, 0x7f, 0xdf, 0x90, 0xc6, 0x16, 0xf0, 0x9b, 0xd3, 0x83, 0xce, + 0x40, 0xb2, 0x17, 0x38, 0xf6, 0x95, 0x52, 0xdb, 0x50, 0x97, 0xee, 0x48, 0xd6, 0x28, 0xcd, 0xf5, + 0x41, 0xee, 0x79, 0x1b, 0x8e, 0xb2, 0x67, 0xeb, 0x57, 0x3a, 0x10, 0xd5, 0xbb, 0x90, 0x4e, 0x7a, + 0xee, 0x0a, 0x8e, 0xd7, 0xa6, 0x17, 0x1c, 0xe7, 0x6f, 0x92, 0x9f, 0x03, 0x65, 0x56, 0xde, 0x58, + 0x2c, 0x90, 0x40, 0x1b, 0x1a, 0x78, 0x1a, 0x33, 0x37, 0x4d, 0xba, 0xea, 0xa5, 0x93, 0x4e, 0x7e, + 0x96, 0x64, 0xf2, 0xbb, 0x3a, 0xac, 0xa4, 0x25, 0xe9, 0x0b, 0xbc, 0x2e, 0xc6, 0xeb, 0xf7, 0x3a, + 0xbc, 0x52, 0xc0, 0x6b, 0x8f, 0x45, 0x7d, 0x86, 0x9c, 0xbf, 0xc0, 0x6d, 0x1e, 0x6e, 0xaf, 0xff, 0xc5, 0x80, 0x6a, 0x62, 0x30, 0xa9, 0x41, 0xd9, 0x7b, 0x14, 0x85, 0x68, 0xbe, 0x44, 0x56, 0xe1, - 0x86, 0x77, 0xf6, 0xcf, 0x0b, 0xb3, 0x47, 0x96, 0x61, 0xc9, 0x2b, 0xf6, 0xf5, 0x4d, 0x24, 0x04, - 0x9a, 0x5e, 0xa1, 0xad, 0x6d, 0x1e, 0x91, 0x5b, 0xb0, 0xec, 0x9d, 0xef, 0xfc, 0x9a, 0x62, 0xdf, - 0x37, 0xbd, 0x62, 0x73, 0x94, 0x9b, 0x03, 0xb9, 0xc4, 0x37, 0x30, 0xce, 0x4e, 0xf9, 0xdc, 0xa4, - 0x64, 0x15, 0x4c, 0xef, 0x4c, 0x8f, 0xd2, 0xfc, 0xbd, 0x46, 0x96, 0xa1, 0xe9, 0x15, 0x9a, 0x70, - 0xe6, 0x1f, 0x34, 0x42, 0x60, 0xd1, 0x9b, 0xec, 0x32, 0x99, 0x7f, 0xd4, 0xc8, 0x2d, 0x20, 0xde, - 0xb9, 0x66, 0x8c, 0xf9, 0x27, 0x8d, 0xac, 0xc0, 0x92, 0x57, 0xe8, 0x59, 0x70, 0xf3, 0xcf, 0x1a, - 0xb9, 0x01, 0x0d, 0x6f, 0xa2, 0x3c, 0x99, 0xbf, 0xd2, 0xd5, 0xa7, 0x26, 0x63, 0xca, 0x7c, 0x57, - 0x27, 0x77, 0xe0, 0xa6, 0x37, 0x35, 0xd0, 0xcc, 0xf7, 0x74, 0xd2, 0x80, 0xaa, 0xa7, 0xba, 0x03, - 0xe6, 0x8f, 0x0c, 0x39, 0x52, 0xd7, 0x56, 0xf3, 0xc7, 0x06, 0xa9, 0x43, 0xc5, 0x93, 0xe7, 0x43, - 0xf3, 0x27, 0xea, 0x27, 0x75, 0xfc, 0x37, 0x3f, 0x34, 0xa4, 0xfa, 0x93, 0x97, 0x01, 0xf3, 0x3f, - 0x06, 0x69, 0x42, 0xcd, 0x4b, 0xcf, 0xb3, 0xe6, 0xaf, 0x6b, 0x52, 0xeb, 0xe2, 0x56, 0x61, 0xbe, - 0x5f, 0x23, 0x4b, 0x00, 0x5e, 0x76, 0xec, 0x31, 0x7f, 0x53, 0x7b, 0xfd, 0x0d, 0x58, 0x48, 0xff, - 0x3f, 0x24, 0x00, 0x95, 0x5d, 0x97, 0xc7, 0xc8, 0xcc, 0x97, 0xc4, 0xb3, 0x8d, 0x6e, 0x0f, 0x99, - 0xa9, 0x89, 0xe7, 0xef, 0x30, 0x2a, 0xe4, 0xba, 0xf0, 0xf9, 0x9e, 0x08, 0x4c, 0xd3, 0xd8, 0x6e, - 0x7f, 0x77, 0xbb, 0x4f, 0xe3, 0xc1, 0xe8, 0x50, 0x44, 0xcd, 0xe6, 0x13, 0xea, 0xfb, 0xf4, 0x49, - 0x8c, 0xdd, 0xc1, 0xa6, 0x8a, 0xa8, 0x2f, 0xf6, 0x28, 0x8f, 0x19, 0x3d, 0x1c, 0xc5, 0xd8, 0xdb, - 0x4c, 0x93, 0x65, 0x53, 0x86, 0x59, 0x36, 0x1c, 0x1e, 0x1e, 0x56, 0xa4, 0xe4, 0xc1, 0xff, 0x02, - 0x00, 0x00, 0xff, 0xff, 0xc1, 0x89, 0xef, 0x28, 0xb0, 0x1f, 0x00, 0x00, + 0x86, 0x77, 0xf6, 0x27, 0x88, 0xd9, 0x23, 0xcb, 0xb0, 0xe4, 0x15, 0xff, 0x0f, 0x98, 0x48, 0x08, + 0x34, 0xbd, 0x42, 0x7b, 0xdc, 0x3c, 0x22, 0xb7, 0x60, 0xd9, 0x3b, 0xdf, 0x41, 0x36, 0xc5, 0xb9, + 0x6f, 0x7a, 0xc5, 0x26, 0x2b, 0x37, 0x07, 0x72, 0x89, 0x6f, 0x61, 0x9c, 0x55, 0x0b, 0xdc, 0xa4, + 0x64, 0x15, 0x4c, 0xef, 0x4c, 0xaf, 0xd3, 0xfc, 0x83, 0x46, 0x96, 0xa1, 0xe9, 0x15, 0x9a, 0x79, + 0xe6, 0x07, 0x1a, 0x21, 0xb0, 0xe8, 0x4d, 0x76, 0xab, 0xcc, 0x3f, 0x6a, 0xe4, 0x16, 0x10, 0xef, + 0x5c, 0x53, 0xc7, 0xfc, 0x93, 0x46, 0x56, 0x60, 0xc9, 0x2b, 0xf4, 0x3e, 0xb8, 0xf9, 0x67, 0x8d, + 0xdc, 0x80, 0x86, 0x37, 0x41, 0x4f, 0xe6, 0xaf, 0x75, 0xb5, 0xd5, 0x64, 0x4c, 0x99, 0xef, 0xea, + 0xe4, 0x0e, 0xdc, 0xf4, 0xa6, 0x06, 0x9a, 0xf9, 0x9e, 0x4e, 0x1a, 0x50, 0xf5, 0x54, 0x97, 0xc1, + 0xfc, 0xb1, 0x21, 0x47, 0xaa, 0xfc, 0x35, 0x7f, 0x62, 0x90, 0x3a, 0x54, 0x3c, 0x79, 0xcf, 0x34, + 0x7f, 0xaa, 0x5e, 0xa9, 0x32, 0xc2, 0xfc, 0xc8, 0x90, 0xea, 0x4f, 0x16, 0x15, 0xe6, 0x7f, 0x0c, + 0xd2, 0x84, 0x9a, 0x97, 0xde, 0x8b, 0xcd, 0xdf, 0xd4, 0xa4, 0xd6, 0xc5, 0xa3, 0xc2, 0x7c, 0xbf, + 0x46, 0x96, 0x00, 0xbc, 0xec, 0xda, 0x63, 0xfe, 0xb6, 0xf6, 0xfa, 0x1b, 0xb0, 0x90, 0xfe, 0x87, + 0x24, 0x00, 0x95, 0x5d, 0x97, 0xc7, 0xc8, 0xcc, 0x97, 0xc4, 0xb3, 0x8d, 0x6e, 0x0f, 0x99, 0xa9, + 0x89, 0xe7, 0xef, 0x31, 0x2a, 0xe4, 0xba, 0xf0, 0xf9, 0x9e, 0x08, 0x4c, 0xd3, 0xd8, 0x6e, 0x7f, + 0x7f, 0xbb, 0x4f, 0xe3, 0xc1, 0xe8, 0x50, 0x44, 0xcd, 0xe6, 0x13, 0xea, 0xfb, 0xf4, 0x49, 0x8c, + 0xdd, 0xc1, 0xa6, 0x8a, 0xa8, 0x2f, 0xf7, 0x28, 0x8f, 0x19, 0x3d, 0x1c, 0xc5, 0xd8, 0xdb, 0x4c, + 0x93, 0x65, 0x53, 0x86, 0x59, 0x36, 0x1c, 0x1e, 0x1e, 0x56, 0xa4, 0xe4, 0xc1, 0xff, 0x02, 0x00, + 0x00, 0xff, 0xff, 0xc4, 0x5d, 0xb1, 0xba, 0xf8, 0x1f, 0x00, 0x00, } diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index f4232bc82234f07d54d713772e89d70f77b5586f..6a312c2bc04169e53915820fd400c177c7f48811 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -2,6 +2,8 @@ package proxy import ( "context" + "fmt" + "io" "log" "math/rand" "net" @@ -9,6 +11,10 @@ import ( "sync" "time" + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + "google.golang.org/grpc" "github.com/zilliztech/milvus-distributed/internal/allocator" @@ -39,6 +45,9 @@ type Proxy struct { manipulationMsgStream *msgstream.PulsarMsgStream queryMsgStream *msgstream.PulsarMsgStream + tracer opentracing.Tracer + closer io.Closer + // Add callback functions at different stages startCallbacks []func() closeCallbacks []func() @@ -51,11 +60,28 @@ func Init() { func CreateProxy(ctx context.Context) (*Proxy, error) { rand.Seed(time.Now().UnixNano()) ctx1, cancel := context.WithCancel(ctx) + var err error p := &Proxy{ proxyLoopCtx: ctx1, proxyLoopCancel: cancel, } + cfg := &config.Configuration{ + ServiceName: "tracing", + Sampler: &config.SamplerConfig{ + Type: "const", + Param: 1, + }, + Reporter: &config.ReporterConfig{ + LogSpans: true, + }, + } + p.tracer, p.closer, err = cfg.NewTracer(config.Logger(jaeger.StdLogger)) + if err != nil { + panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err)) + } + opentracing.SetGlobalTracer(p.tracer) + pulsarAddress := Params.PulsarAddress() p.queryMsgStream = msgstream.NewPulsarMsgStream(p.proxyLoopCtx, Params.MsgStreamSearchBufSize()) @@ -198,6 +224,8 @@ func (p *Proxy) stopProxyLoop() { p.tick.Close() p.proxyLoopWg.Wait() + + p.closer.Close() } // Close closes the server. diff --git a/internal/proxy/repack_func.go b/internal/proxy/repack_func.go index 44139999e0403719ca9eaf141f110980b808b6e1..f8873fe12f27bcae72473bd9b5eb252a9eac1aee 100644 --- a/internal/proxy/repack_func.go +++ b/internal/proxy/repack_func.go @@ -182,6 +182,7 @@ func insertRepackFunc(tsMsgs []msgstream.TsMsg, insertMsg := &msgstream.InsertMsg{ InsertRequest: sliceRequest, } + insertMsg.SetContext(request.GetContext()) if together { // all rows with same hash value are accumulated to only one message if len(result[key].Msgs) <= 0 { result[key].Msgs = append(result[key].Msgs, insertMsg) diff --git a/internal/proxy/task.go b/internal/proxy/task.go index 425cae75cfb3e4de24b55cf4a24cf3cc5aa55dbe..d01c45f0632545feca63a477eec4df35de3cf85c 100644 --- a/internal/proxy/task.go +++ b/internal/proxy/task.go @@ -7,6 +7,9 @@ import ( "math" "strconv" + "github.com/opentracing/opentracing-go" + oplog "github.com/opentracing/opentracing-go/log" + "github.com/golang/protobuf/proto" "github.com/zilliztech/milvus-distributed/internal/allocator" "github.com/zilliztech/milvus-distributed/internal/msgstream" @@ -74,12 +77,21 @@ func (it *InsertTask) Type() internalpb.MsgType { } func (it *InsertTask) PreExecute() error { + span := opentracing.StartSpan("InsertTask preExecute") + defer span.Finish() + it.ctx = opentracing.ContextWithSpan(it.ctx, span) + span.SetTag("hash keys", it.ReqID) + span.SetTag("start time", it.BeginTs()) collectionName := it.BaseInsertTask.CollectionName if err := ValidateCollectionName(collectionName); err != nil { + span.LogFields(oplog.Error(err)) + span.Finish() return err } partitionTag := it.BaseInsertTask.PartitionTag if err := ValidatePartitionTag(partitionTag, true); err != nil { + span.LogFields(oplog.Error(err)) + span.Finish() return err } @@ -87,22 +99,36 @@ func (it *InsertTask) PreExecute() error { } func (it *InsertTask) Execute() error { + span, ctx := opentracing.StartSpanFromContext(it.ctx, "InsertTask Execute") + defer span.Finish() + it.ctx = ctx + span.SetTag("hash keys", it.ReqID) + span.SetTag("start time", it.BeginTs()) collectionName := it.BaseInsertTask.CollectionName + span.LogFields(oplog.String("collection_name", collectionName)) if !globalMetaCache.Hit(collectionName) { err := globalMetaCache.Sync(collectionName) if err != nil { + span.LogFields(oplog.Error(err)) + span.Finish() return err } } description, err := globalMetaCache.Get(collectionName) if err != nil || description == nil { + span.LogFields(oplog.Error(err)) + span.Finish() return err } autoID := description.Schema.AutoID + span.LogFields(oplog.Bool("auto_id", autoID)) var rowIDBegin UniqueID var rowIDEnd UniqueID rowNums := len(it.BaseInsertTask.RowData) rowIDBegin, rowIDEnd, _ = it.rowIDAllocator.Alloc(uint32(rowNums)) + span.LogFields(oplog.Int("rowNums", rowNums), + oplog.Int("rowIDBegin", int(rowIDBegin)), + oplog.Int("rowIDEnd", int(rowIDEnd))) it.BaseInsertTask.RowIDs = make([]UniqueID, rowNums) for i := rowIDBegin; i < rowIDEnd; i++ { offset := i - rowIDBegin @@ -125,6 +151,8 @@ func (it *InsertTask) Execute() error { EndTs: it.EndTs(), Msgs: make([]msgstream.TsMsg, 1), } + tsMsg.SetContext(it.ctx) + span.LogFields(oplog.String("send msg", "send msg")) msgPack.Msgs[0] = tsMsg err = it.manipulationMsgStream.Produce(msgPack) @@ -138,11 +166,14 @@ func (it *InsertTask) Execute() error { if err != nil { it.result.Status.ErrorCode = commonpb.ErrorCode_UNEXPECTED_ERROR it.result.Status.Reason = err.Error() + span.LogFields(oplog.Error(err)) } return nil } func (it *InsertTask) PostExecute() error { + span, _ := opentracing.StartSpanFromContext(it.ctx, "InsertTask postExecute") + defer span.Finish() return nil } @@ -352,24 +383,38 @@ func (qt *QueryTask) SetTs(ts Timestamp) { } func (qt *QueryTask) PreExecute() error { + span := opentracing.StartSpan("InsertTask preExecute") + defer span.Finish() + qt.ctx = opentracing.ContextWithSpan(qt.ctx, span) + span.SetTag("hash keys", qt.ReqID) + span.SetTag("start time", qt.BeginTs()) + collectionName := qt.query.CollectionName if !globalMetaCache.Hit(collectionName) { err := globalMetaCache.Sync(collectionName) if err != nil { + span.LogFields(oplog.Error(err)) + span.Finish() return err } } _, err := globalMetaCache.Get(collectionName) if err != nil { // err is not nil if collection not exists + span.LogFields(oplog.Error(err)) + span.Finish() return err } if err := ValidateCollectionName(qt.query.CollectionName); err != nil { + span.LogFields(oplog.Error(err)) + span.Finish() return err } for _, tag := range qt.query.PartitionTags { if err := ValidatePartitionTag(tag, false); err != nil { + span.LogFields(oplog.Error(err)) + span.Finish() return err } } @@ -379,6 +424,8 @@ func (qt *QueryTask) PreExecute() error { } queryBytes, err := proto.Marshal(qt.query) if err != nil { + span.LogFields(oplog.Error(err)) + span.Finish() return err } qt.Query = &commonpb.Blob{ @@ -388,6 +435,10 @@ func (qt *QueryTask) PreExecute() error { } func (qt *QueryTask) Execute() error { + span, ctx := opentracing.StartSpanFromContext(qt.ctx, "InsertTask Execute") + defer span.Finish() + span.SetTag("hash keys", qt.ReqID) + span.SetTag("start time", qt.BeginTs()) var tsMsg msgstream.TsMsg = &msgstream.SearchMsg{ SearchRequest: qt.SearchRequest, BaseMsg: msgstream.BaseMsg{ @@ -401,20 +452,28 @@ func (qt *QueryTask) Execute() error { EndTs: qt.Timestamp, Msgs: make([]msgstream.TsMsg, 1), } + tsMsg.SetContext(ctx) msgPack.Msgs[0] = tsMsg err := qt.queryMsgStream.Produce(msgPack) log.Printf("[Proxy] length of searchMsg: %v", len(msgPack.Msgs)) if err != nil { + span.LogFields(oplog.Error(err)) + span.Finish() log.Printf("[Proxy] send search request failed: %v", err) } return err } func (qt *QueryTask) PostExecute() error { + span, _ := opentracing.StartSpanFromContext(qt.ctx, "InsertTask postExecute") + span.SetTag("hash keys", qt.ReqID) + span.SetTag("start time", qt.BeginTs()) for { select { case <-qt.ctx.Done(): log.Print("wait to finish failed, timeout!") + span.LogFields(oplog.String("wait to finish failed, timeout", "wait to finish failed, timeout")) + span.Finish() return errors.New("wait to finish failed, timeout") case searchResults := <-qt.resultBuf: filterSearchResult := make([]*internalpb.SearchResult, 0) @@ -435,6 +494,8 @@ func (qt *QueryTask) PostExecute() error { Reason: filterReason, }, } + span.LogFields(oplog.Error(errors.New(filterReason))) + span.Finish() return errors.New(filterReason) } @@ -465,6 +526,7 @@ func (qt *QueryTask) PostExecute() error { Reason: filterReason, }, } + span.Finish() return nil } @@ -476,6 +538,7 @@ func (qt *QueryTask) PostExecute() error { Reason: filterReason, }, } + span.Finish() return nil } @@ -526,10 +589,13 @@ func (qt *QueryTask) PostExecute() error { reducedHitsBs, err := proto.Marshal(reducedHits) if err != nil { log.Println("marshal error") + span.LogFields(oplog.Error(err)) + span.Finish() return err } qt.result.Hits = append(qt.result.Hits, reducedHitsBs) } + span.Finish() return nil } } @@ -637,7 +703,10 @@ func (dct *DescribeCollectionTask) PreExecute() error { func (dct *DescribeCollectionTask) Execute() error { var err error dct.result, err = dct.masterClient.DescribeCollection(dct.ctx, &dct.DescribeCollectionRequest) - globalMetaCache.Update(dct.CollectionName.CollectionName, dct.result) + if err != nil { + return err + } + err = globalMetaCache.Update(dct.CollectionName.CollectionName, dct.result) return err } diff --git a/internal/querynode/data_sync_service.go b/internal/querynode/data_sync_service.go index 5ed2caa0675b53c4aa9a895a6983cd3f98dc0057..44a595caf5679969e4455b592e70cdb1f9f34012 100644 --- a/internal/querynode/data_sync_service.go +++ b/internal/querynode/data_sync_service.go @@ -48,7 +48,6 @@ func (dsService *dataSyncService) initNodes() { var insertNode Node = newInsertNode(dsService.replica) var serviceTimeNode Node = newServiceTimeNode(dsService.replica) - var gcNode Node = newGCNode(dsService.replica) dsService.fg.AddNode(&dmStreamNode) dsService.fg.AddNode(&ddStreamNode) @@ -58,7 +57,6 @@ func (dsService *dataSyncService) initNodes() { dsService.fg.AddNode(&insertNode) dsService.fg.AddNode(&serviceTimeNode) - dsService.fg.AddNode(&gcNode) // dmStreamNode var err = dsService.fg.SetEdges(dmStreamNode.Name(), @@ -108,17 +106,9 @@ func (dsService *dataSyncService) initNodes() { // serviceTimeNode err = dsService.fg.SetEdges(serviceTimeNode.Name(), []string{insertNode.Name()}, - []string{gcNode.Name()}, + []string{}, ) if err != nil { log.Fatal("set edges failed in node:", serviceTimeNode.Name()) } - - // gcNode - err = dsService.fg.SetEdges(gcNode.Name(), - []string{serviceTimeNode.Name()}, - []string{}) - if err != nil { - log.Fatal("set edges failed in node:", gcNode.Name()) - } } diff --git a/internal/querynode/flow_graph_dd_node.go b/internal/querynode/flow_graph_dd_node.go index a7a2ac73201ff48439090ad0ef5f58d9c1b38e1d..f4a5e1136946450de3a25f206f64a2b6ef7e0ec1 100644 --- a/internal/querynode/flow_graph_dd_node.go +++ b/internal/querynode/flow_graph_dd_node.go @@ -44,11 +44,6 @@ func (ddNode *ddNode) Operate(in []*Msg) []*Msg { }, } ddNode.ddMsg = &ddMsg - gcRecord := gcRecord{ - collections: make([]UniqueID, 0), - partitions: make([]partitionWithID, 0), - } - ddNode.ddMsg.gcRecord = &gcRecord // sort tsMessages tsMessages := msMsg.TsMessages() @@ -120,11 +115,11 @@ func (ddNode *ddNode) createCollection(msg *msgstream.CreateCollectionMsg) { func (ddNode *ddNode) dropCollection(msg *msgstream.DropCollectionMsg) { collectionID := msg.CollectionID - //err := ddNode.replica.removeCollection(collectionID) - //if err != nil { - // log.Println(err) - // return - //} + err := ddNode.replica.removeCollection(collectionID) + if err != nil { + log.Println(err) + return + } collectionName := msg.CollectionName.CollectionName ddNode.ddMsg.collectionRecords[collectionName] = append(ddNode.ddMsg.collectionRecords[collectionName], @@ -132,8 +127,6 @@ func (ddNode *ddNode) dropCollection(msg *msgstream.DropCollectionMsg) { createOrDrop: false, timestamp: msg.Timestamp, }) - - ddNode.ddMsg.gcRecord.collections = append(ddNode.ddMsg.gcRecord.collections, collectionID) } func (ddNode *ddNode) createPartition(msg *msgstream.CreatePartitionMsg) { @@ -157,22 +150,17 @@ func (ddNode *ddNode) dropPartition(msg *msgstream.DropPartitionMsg) { collectionID := msg.CollectionID partitionTag := msg.PartitionName.Tag - //err := ddNode.replica.removePartition(collectionID, partitionTag) - //if err != nil { - // log.Println(err) - // return - //} + err := ddNode.replica.removePartition(collectionID, partitionTag) + if err != nil { + log.Println(err) + return + } ddNode.ddMsg.partitionRecords[partitionTag] = append(ddNode.ddMsg.partitionRecords[partitionTag], metaOperateRecord{ createOrDrop: false, timestamp: msg.Timestamp, }) - - ddNode.ddMsg.gcRecord.partitions = append(ddNode.ddMsg.gcRecord.partitions, partitionWithID{ - partitionTag: partitionTag, - collectionID: collectionID, - }) } func newDDNode(replica collectionReplica) *ddNode { diff --git a/internal/querynode/flow_graph_filter_dm_node.go b/internal/querynode/flow_graph_filter_dm_node.go index fbc8eedb5c82b00e868561b6e5971a9af3f78468..ceddaeab0b95a1a1b1880def7689ee3278a20e3a 100644 --- a/internal/querynode/flow_graph_filter_dm_node.go +++ b/internal/querynode/flow_graph_filter_dm_node.go @@ -2,7 +2,6 @@ package querynode import ( "log" - "math" "github.com/zilliztech/milvus-distributed/internal/msgstream" "github.com/zilliztech/milvus-distributed/internal/proto/commonpb" @@ -60,7 +59,6 @@ func (fdmNode *filterDmNode) Operate(in []*Msg) []*Msg { } } - iMsg.gcRecord = ddMsg.gcRecord var res Msg = &iMsg return []*Msg{&res} } @@ -83,35 +81,17 @@ func (fdmNode *filterDmNode) filterInvalidInsertMessage(msg *msgstream.InsertMsg log.Println("Error, misaligned messages detected") return nil } - tmpTimestamps := make([]Timestamp, 0) tmpRowIDs := make([]int64, 0) tmpRowData := make([]*commonpb.Blob, 0) - - // calculate valid time range - timeBegin := Timestamp(0) - timeEnd := Timestamp(math.MaxUint64) - for _, record := range records { - if record.createOrDrop && timeBegin < record.timestamp { - timeBegin = record.timestamp - } - if !record.createOrDrop && timeEnd > record.timestamp { - timeEnd = record.timestamp - } - } - + targetTimestamp := records[len(records)-1].timestamp for i, t := range msg.Timestamps { - if t >= timeBegin && t <= timeEnd { + if t >= targetTimestamp { tmpTimestamps = append(tmpTimestamps, t) tmpRowIDs = append(tmpRowIDs, msg.RowIDs[i]) tmpRowData = append(tmpRowData, msg.RowData[i]) } } - - if len(tmpRowIDs) <= 0 { - return nil - } - msg.Timestamps = tmpTimestamps msg.RowIDs = tmpRowIDs msg.RowData = tmpRowData diff --git a/internal/querynode/flow_graph_gc_node.go b/internal/querynode/flow_graph_gc_node.go deleted file mode 100644 index cd0a9b984e7cf991436f3a3195935133e45c4c9a..0000000000000000000000000000000000000000 --- a/internal/querynode/flow_graph_gc_node.go +++ /dev/null @@ -1,61 +0,0 @@ -package querynode - -import ( - "log" -) - -type gcNode struct { - BaseNode - replica collectionReplica -} - -func (gcNode *gcNode) Name() string { - return "gcNode" -} - -func (gcNode *gcNode) Operate(in []*Msg) []*Msg { - //fmt.Println("Do gcNode operation") - - if len(in) != 1 { - log.Println("Invalid operate message input in gcNode, input length = ", len(in)) - // TODO: add error handling - } - - gcMsg, ok := (*in[0]).(*gcMsg) - if !ok { - log.Println("type assertion failed for gcMsg") - // TODO: add error handling - } - - // drop collections - for _, collectionID := range gcMsg.gcRecord.collections { - err := gcNode.replica.removeCollection(collectionID) - if err != nil { - log.Println(err) - } - } - - // drop partitions - for _, partition := range gcMsg.gcRecord.partitions { - err := gcNode.replica.removePartition(partition.collectionID, partition.partitionTag) - if err != nil { - log.Println(err) - } - } - - return nil -} - -func newGCNode(replica collectionReplica) *gcNode { - maxQueueLength := Params.FlowGraphMaxQueueLength - maxParallelism := Params.FlowGraphMaxParallelism - - baseNode := BaseNode{} - baseNode.SetMaxQueueLength(maxQueueLength) - baseNode.SetMaxParallelism(maxParallelism) - - return &gcNode{ - BaseNode: baseNode, - replica: replica, - } -} diff --git a/internal/querynode/flow_graph_insert_node.go b/internal/querynode/flow_graph_insert_node.go index 9a2c8ca1f11e34738dfbfae93eaeb8e715b70ef3..f60369521967aea4714a6099959fdcbadecb5883 100644 --- a/internal/querynode/flow_graph_insert_node.go +++ b/internal/querynode/flow_graph_insert_node.go @@ -90,7 +90,6 @@ func (iNode *insertNode) Operate(in []*Msg) []*Msg { wg.Wait() var res Msg = &serviceTimeMsg{ - gcRecord: iMsg.gcRecord, timeRange: iMsg.timeRange, } return []*Msg{&res} diff --git a/internal/querynode/flow_graph_message.go b/internal/querynode/flow_graph_message.go index 451f9b6952ad003a3f687ee44550808cce6bc481..88a133fab34a84d21da9f635103c1d8f7466f5d0 100644 --- a/internal/querynode/flow_graph_message.go +++ b/internal/querynode/flow_graph_message.go @@ -16,7 +16,6 @@ type key2SegMsg struct { type ddMsg struct { collectionRecords map[string][]metaOperateRecord partitionRecords map[string][]metaOperateRecord - gcRecord *gcRecord timeRange TimeRange } @@ -27,7 +26,6 @@ type metaOperateRecord struct { type insertMsg struct { insertMessages []*msgstream.InsertMsg - gcRecord *gcRecord timeRange TimeRange } @@ -37,12 +35,6 @@ type deleteMsg struct { } type serviceTimeMsg struct { - gcRecord *gcRecord - timeRange TimeRange -} - -type gcMsg struct { - gcRecord *gcRecord timeRange TimeRange } @@ -63,39 +55,42 @@ type DeletePreprocessData struct { count int32 } -// TODO: replace partitionWithID by partition id -type partitionWithID struct { - partitionTag string - collectionID UniqueID -} - -type gcRecord struct { - // collections and partitions to be dropped - collections []UniqueID - // TODO: use partition id - partitions []partitionWithID -} - func (ksMsg *key2SegMsg) TimeTick() Timestamp { return ksMsg.timeRange.timestampMax } +func (ksMsg *key2SegMsg) DownStreamNodeIdx() int { + return 0 +} + func (suMsg *ddMsg) TimeTick() Timestamp { return suMsg.timeRange.timestampMax } +func (suMsg *ddMsg) DownStreamNodeIdx() int { + return 0 +} + func (iMsg *insertMsg) TimeTick() Timestamp { return iMsg.timeRange.timestampMax } +func (iMsg *insertMsg) DownStreamNodeIdx() int { + return 0 +} + func (dMsg *deleteMsg) TimeTick() Timestamp { return dMsg.timeRange.timestampMax } +func (dMsg *deleteMsg) DownStreamNodeIdx() int { + return 0 +} + func (stMsg *serviceTimeMsg) TimeTick() Timestamp { return stMsg.timeRange.timestampMax } -func (gcMsg *gcMsg) TimeTick() Timestamp { - return gcMsg.timeRange.timestampMax +func (stMsg *serviceTimeMsg) DownStreamNodeIdx() int { + return 0 } diff --git a/internal/querynode/flow_graph_service_time_node.go b/internal/querynode/flow_graph_service_time_node.go index 275666560d589c95f57b1f13ddefa90b5eb76ee6..38feb029c8af67745dbedca59af89ee25b9b9023 100644 --- a/internal/querynode/flow_graph_service_time_node.go +++ b/internal/querynode/flow_graph_service_time_node.go @@ -30,12 +30,7 @@ func (stNode *serviceTimeNode) Operate(in []*Msg) []*Msg { // update service time stNode.replica.getTSafe().set(serviceTimeMsg.timeRange.timestampMax) //fmt.Println("update tSafe to:", getPhysicalTime(serviceTimeMsg.timeRange.timestampMax)) - - var res Msg = &gcMsg{ - gcRecord: serviceTimeMsg.gcRecord, - timeRange: serviceTimeMsg.timeRange, - } - return []*Msg{&res} + return nil } func newServiceTimeNode(replica collectionReplica) *serviceTimeNode { diff --git a/internal/querynode/load_index_service.go b/internal/querynode/load_index_service.go index cedbd50bb0447e0c535e926eacf208ca1d1e1b29..d8ae759b6731512e1e07df7bf03e63c0b421a0e2 100644 --- a/internal/querynode/load_index_service.go +++ b/internal/querynode/load_index_service.go @@ -11,9 +11,6 @@ import ( "strings" "time" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - minioKV "github.com/zilliztech/milvus-distributed/internal/kv/minio" "github.com/zilliztech/milvus-distributed/internal/msgstream" "github.com/zilliztech/milvus-distributed/internal/proto/commonpb" @@ -38,16 +35,17 @@ type loadIndexService struct { func newLoadIndexService(ctx context.Context, replica collectionReplica) *loadIndexService { ctx1, cancel := context.WithCancel(ctx) - // init minio - minioClient, err := minio.New(Params.MinioEndPoint, &minio.Options{ - Creds: credentials.NewStaticV4(Params.MinioAccessKeyID, Params.MinioSecretAccessKey, ""), - Secure: Params.MinioUseSSLStr, - }) - if err != nil { - panic(err) + option := &minioKV.Option{ + Address: Params.MinioEndPoint, + AccessKeyID: Params.MinioAccessKeyID, + SecretAccessKeyID: Params.MinioSecretAccessKey, + UseSSL: Params.MinioUseSSLStr, + CreateBucket: true, + BucketName: Params.MinioBucketName, } - MinioKV, err := minioKV.NewMinIOKV(ctx1, minioClient, Params.MinioBucketName) + // TODO: load bucketName from config + MinioKV, err := minioKV.NewMinIOKV(ctx1, option) if err != nil { panic(err) } diff --git a/internal/querynode/load_index_service_test.go b/internal/querynode/load_index_service_test.go index cb2fb8504e190345567ee170ecce71846fe2ce45..000edb49df2bf53fddef89b3261c9ceb15f4c5de 100644 --- a/internal/querynode/load_index_service_test.go +++ b/internal/querynode/load_index_service_test.go @@ -5,8 +5,6 @@ import ( "sort" "testing" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" "github.com/stretchr/testify/assert" "github.com/zilliztech/milvus-distributed/internal/indexbuilder" @@ -68,13 +66,16 @@ func TestLoadIndexService(t *testing.T) { binarySet, err := index.Serialize() assert.Equal(t, err, nil) - //save index to minio - minioClient, err := minio.New(Params.MinioEndPoint, &minio.Options{ - Creds: credentials.NewStaticV4(Params.MinioAccessKeyID, Params.MinioSecretAccessKey, ""), - Secure: Params.MinioUseSSLStr, - }) - assert.Equal(t, err, nil) - minioKV, err := minioKV.NewMinIOKV(node.queryNodeLoopCtx, minioClient, Params.MinioBucketName) + option := &minioKV.Option{ + Address: Params.MinioEndPoint, + AccessKeyID: Params.MinioAccessKeyID, + SecretAccessKeyID: Params.MinioSecretAccessKey, + UseSSL: Params.MinioUseSSLStr, + BucketName: Params.MinioBucketName, + CreateBucket: true, + } + + minioKV, err := minioKV.NewMinIOKV(node.queryNodeLoopCtx, option) assert.Equal(t, err, nil) indexPaths := make([]string, 0) for _, index := range binarySet { diff --git a/internal/storage/internal/S3/S3_test.go b/internal/storage/internal/S3/S3_test.go deleted file mode 100644 index c565f4a5a15cf25d109f5dcff642a0a21f2f94d1..0000000000000000000000000000000000000000 --- a/internal/storage/internal/S3/S3_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package s3driver - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - storagetype "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -var option = storagetype.Option{BucketName: "zilliz-hz"} -var ctx = context.Background() -var client, err = NewS3Driver(ctx, option) - -func TestS3Driver_PutRowAndGetRow(t *testing.T) { - err = client.PutRow(ctx, []byte("bar"), []byte("abcdefghijklmnoopqrstuvwxyz"), "SegmentA", 1) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("bar"), []byte("djhfkjsbdfbsdughorsgsdjhgoisdgh"), "SegmentA", 2) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("bar"), []byte("123854676ershdgfsgdfk,sdhfg;sdi8"), "SegmentB", 3) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("bar1"), []byte("testkeybarorbar_1"), "SegmentC", 3) - assert.Nil(t, err) - object, _ := client.GetRow(ctx, []byte("bar"), 1) - assert.Equal(t, "abcdefghijklmnoopqrstuvwxyz", string(object)) - object, _ = client.GetRow(ctx, []byte("bar"), 2) - assert.Equal(t, "djhfkjsbdfbsdughorsgsdjhgoisdgh", string(object)) - object, _ = client.GetRow(ctx, []byte("bar"), 5) - assert.Equal(t, "123854676ershdgfsgdfk,sdhfg;sdi8", string(object)) - object, _ = client.GetRow(ctx, []byte("bar1"), 5) - assert.Equal(t, "testkeybarorbar_1", string(object)) -} - -func TestS3Driver_DeleteRow(t *testing.T) { - err = client.DeleteRow(ctx, []byte("bar"), 5) - assert.Nil(t, err) - object, _ := client.GetRow(ctx, []byte("bar"), 6) - assert.Nil(t, object) - err = client.DeleteRow(ctx, []byte("bar1"), 5) - assert.Nil(t, err) - object2, _ := client.GetRow(ctx, []byte("bar1"), 6) - assert.Nil(t, object2) -} - -func TestS3Driver_GetSegments(t *testing.T) { - err = client.PutRow(ctx, []byte("seg"), []byte("abcdefghijklmnoopqrstuvwxyz"), "SegmentA", 1) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("seg"), []byte("djhfkjsbdfbsdughorsgsdjhgoisdgh"), "SegmentA", 2) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("seg"), []byte("123854676ershdgfsgdfk,sdhfg;sdi8"), "SegmentB", 3) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("seg2"), []byte("testkeybarorbar_1"), "SegmentC", 1) - assert.Nil(t, err) - - segements, err := client.GetSegments(ctx, []byte("seg"), 4) - assert.Nil(t, err) - assert.Equal(t, 2, len(segements)) - if segements[0] == "SegmentA" { - assert.Equal(t, "SegmentA", segements[0]) - assert.Equal(t, "SegmentB", segements[1]) - } else { - assert.Equal(t, "SegmentB", segements[0]) - assert.Equal(t, "SegmentA", segements[1]) - } -} - -func TestS3Driver_PutRowsAndGetRows(t *testing.T) { - keys := [][]byte{[]byte("foo"), []byte("bar")} - values := [][]byte{[]byte("The key is foo!"), []byte("The key is bar!")} - segments := []string{"segmentA", "segmentB"} - timestamps := []uint64{1, 2} - err = client.PutRows(ctx, keys, values, segments, timestamps) - assert.Nil(t, err) - - objects, err := client.GetRows(ctx, keys, timestamps) - assert.Nil(t, err) - assert.Equal(t, "The key is foo!", string(objects[0])) - assert.Equal(t, "The key is bar!", string(objects[1])) -} - -func TestS3Driver_DeleteRows(t *testing.T) { - keys := [][]byte{[]byte("foo"), []byte("bar")} - timestamps := []uint64{3, 3} - err := client.DeleteRows(ctx, keys, timestamps) - assert.Nil(t, err) - - objects, err := client.GetRows(ctx, keys, timestamps) - assert.Nil(t, err) - assert.Nil(t, objects[0]) - assert.Nil(t, objects[1]) -} - -func TestS3Driver_PutLogAndGetLog(t *testing.T) { - err = client.PutLog(ctx, []byte("insert"), []byte("This is insert log!"), 1, 11) - assert.Nil(t, err) - err = client.PutLog(ctx, []byte("delete"), []byte("This is delete log!"), 2, 10) - assert.Nil(t, err) - err = client.PutLog(ctx, []byte("update"), []byte("This is update log!"), 3, 9) - assert.Nil(t, err) - err = client.PutLog(ctx, []byte("select"), []byte("This is select log!"), 4, 8) - assert.Nil(t, err) - - channels := []int{5, 8, 9, 10, 11, 12, 13} - logValues, err := client.GetLog(ctx, 0, 5, channels) - assert.Nil(t, err) - assert.Equal(t, "This is select log!", string(logValues[0])) - assert.Equal(t, "This is update log!", string(logValues[1])) - assert.Equal(t, "This is delete log!", string(logValues[2])) - assert.Equal(t, "This is insert log!", string(logValues[3])) -} - -func TestS3Driver_Segment(t *testing.T) { - err := client.PutSegmentIndex(ctx, "segmentA", []byte("This is segmentA's index!")) - assert.Nil(t, err) - - segmentIndex, err := client.GetSegmentIndex(ctx, "segmentA") - assert.Equal(t, "This is segmentA's index!", string(segmentIndex)) - assert.Nil(t, err) - - err = client.DeleteSegmentIndex(ctx, "segmentA") - assert.Nil(t, err) -} - -func TestS3Driver_SegmentDL(t *testing.T) { - err := client.PutSegmentDL(ctx, "segmentB", []byte("This is segmentB's delete log!")) - assert.Nil(t, err) - - segmentDL, err := client.GetSegmentDL(ctx, "segmentB") - assert.Nil(t, err) - assert.Equal(t, "This is segmentB's delete log!", string(segmentDL)) - - err = client.DeleteSegmentDL(ctx, "segmentB") - assert.Nil(t, err) -} diff --git a/internal/storage/internal/S3/s3_engine.go b/internal/storage/internal/S3/s3_engine.go deleted file mode 100644 index 8034d679e7e01fb45867b7424b6e9e31bc35c294..0000000000000000000000000000000000000000 --- a/internal/storage/internal/S3/s3_engine.go +++ /dev/null @@ -1,173 +0,0 @@ -package s3driver - -import ( - "bytes" - "context" - "io" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" - . "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -type S3Store struct { - client *s3.S3 -} - -func NewS3Store(config aws.Config) (*S3Store, error) { - sess := session.Must(session.NewSession(&config)) - service := s3.New(sess) - - return &S3Store{ - client: service, - }, nil -} - -func (s *S3Store) Put(ctx context.Context, key Key, value Value) error { - _, err := s.client.PutObjectWithContext(ctx, &s3.PutObjectInput{ - Bucket: aws.String(bucketName), - Key: aws.String(string(key)), - Body: bytes.NewReader(value), - }) - - //sess := session.Must(session.NewSessionWithOptions(session.Options{ - // SharedConfigState: session.SharedConfigEnable, - //})) - //uploader := s3manager.NewUploader(sess) - // - //_, err := uploader.Upload(&s3manager.UploadInput{ - // Bucket: aws.String(bucketName), - // Key: aws.String(string(key)), - // Body: bytes.NewReader(value), - //}) - - return err -} - -func (s *S3Store) Get(ctx context.Context, key Key) (Value, error) { - object, err := s.client.GetObjectWithContext(ctx, &s3.GetObjectInput{ - Bucket: aws.String(bucketName), - Key: aws.String(string(key)), - }) - if err != nil { - return nil, err - } - - //TODO: get size - size := 256 * 1024 - buf := make([]byte, size) - n, err := object.Body.Read(buf) - if err != nil && err != io.EOF { - return nil, err - } - return buf[:n], nil -} - -func (s *S3Store) GetByPrefix(ctx context.Context, prefix Key, keyOnly bool) ([]Key, []Value, error) { - objectsOutput, err := s.client.ListObjectsWithContext(ctx, &s3.ListObjectsInput{ - Bucket: aws.String(bucketName), - Prefix: aws.String(string(prefix)), - }) - - var objectsKeys []Key - var objectsValues []Value - - if objectsOutput != nil && err == nil { - for _, object := range objectsOutput.Contents { - objectsKeys = append(objectsKeys, []byte(*object.Key)) - if !keyOnly { - value, err := s.Get(ctx, []byte(*object.Key)) - if err != nil { - return nil, nil, err - } - objectsValues = append(objectsValues, value) - } - } - } else { - return nil, nil, err - } - - return objectsKeys, objectsValues, nil - -} - -func (s *S3Store) Scan(ctx context.Context, keyStart Key, keyEnd Key, limit int, keyOnly bool) ([]Key, []Value, error) { - var keys []Key - var values []Value - limitCount := uint(limit) - objects, err := s.client.ListObjectsWithContext(ctx, &s3.ListObjectsInput{ - Bucket: aws.String(bucketName), - Prefix: aws.String(string(keyStart)), - }) - if err == nil && objects != nil { - for _, object := range objects.Contents { - if *object.Key >= string(keyEnd) { - keys = append(keys, []byte(*object.Key)) - if !keyOnly { - value, err := s.Get(ctx, []byte(*object.Key)) - if err != nil { - return nil, nil, err - } - values = append(values, value) - } - limitCount-- - if limitCount <= 0 { - break - } - } - } - } - - return keys, values, err -} - -func (s *S3Store) Delete(ctx context.Context, key Key) error { - _, err := s.client.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{ - Bucket: aws.String(bucketName), - Key: aws.String(string(key)), - }) - return err -} - -func (s *S3Store) DeleteByPrefix(ctx context.Context, prefix Key) error { - - objects, err := s.client.ListObjectsWithContext(ctx, &s3.ListObjectsInput{ - Bucket: aws.String(bucketName), - Prefix: aws.String(string(prefix)), - }) - - if objects != nil && err == nil { - for _, object := range objects.Contents { - _, err := s.client.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{ - Bucket: aws.String(bucketName), - Key: object.Key, - }) - return err - } - } - - return nil -} - -func (s *S3Store) DeleteRange(ctx context.Context, keyStart Key, keyEnd Key) error { - - objects, err := s.client.ListObjectsWithContext(ctx, &s3.ListObjectsInput{ - Bucket: aws.String(bucketName), - Prefix: aws.String(string(keyStart)), - }) - - if objects != nil && err == nil { - for _, object := range objects.Contents { - if *object.Key > string(keyEnd) { - _, err := s.client.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{ - Bucket: aws.String(bucketName), - Key: object.Key, - }) - return err - } - } - } - - return nil -} diff --git a/internal/storage/internal/S3/s3_store.go b/internal/storage/internal/S3/s3_store.go deleted file mode 100644 index 19199baa54dcdc50d6ae947f899be1068c383642..0000000000000000000000000000000000000000 --- a/internal/storage/internal/S3/s3_store.go +++ /dev/null @@ -1,339 +0,0 @@ -package s3driver - -import ( - "context" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/zilliztech/milvus-distributed/internal/storage/internal/minio/codec" - . "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -type S3Driver struct { - driver *S3Store -} - -var bucketName string - -func NewS3Driver(ctx context.Context, option Option) (*S3Driver, error) { - // to-do read conf - - bucketName = option.BucketName - - S3Client, err := NewS3Store(aws.Config{ - Region: aws.String(endpoints.CnNorthwest1RegionID)}) - - if err != nil { - return nil, err - } - - return &S3Driver{ - S3Client, - }, nil -} - -func (s *S3Driver) put(ctx context.Context, key Key, value Value, timestamp Timestamp, suffix string) error { - minioKey, err := codec.MvccEncode(key, timestamp, suffix) - if err != nil { - return err - } - - err = s.driver.Put(ctx, minioKey, value) - return err -} - -func (s *S3Driver) scanLE(ctx context.Context, key Key, timestamp Timestamp, keyOnly bool) ([]Timestamp, []Key, []Value, error) { - keyEnd, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - return nil, nil, nil, err - } - - keys, values, err := s.driver.Scan(ctx, key, []byte(keyEnd), -1, keyOnly) - if err != nil { - return nil, nil, nil, err - } - - var timestamps []Timestamp - for _, key := range keys { - _, timestamp, _, _ := codec.MvccDecode(key) - timestamps = append(timestamps, timestamp) - } - - return timestamps, keys, values, nil -} - -func (s *S3Driver) scanGE(ctx context.Context, key Key, timestamp Timestamp, keyOnly bool) ([]Timestamp, []Key, []Value, error) { - keyStart, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - return nil, nil, nil, err - } - - keys, values, err := s.driver.Scan(ctx, key, keyStart, -1, keyOnly) - if err != nil { - return nil, nil, nil, err - } - - var timestamps []Timestamp - for _, key := range keys { - _, timestamp, _, _ := codec.MvccDecode(key) - timestamps = append(timestamps, timestamp) - } - - return timestamps, keys, values, nil -} - -//scan(ctx context.Context, key Key, start Timestamp, end Timestamp, withValue bool) ([]Timestamp, []Key, []Value, error) -func (s *S3Driver) deleteLE(ctx context.Context, key Key, timestamp Timestamp) error { - keyEnd, err := codec.MvccEncode(key, timestamp, "delete") - if err != nil { - return err - } - err = s.driver.DeleteRange(ctx, key, keyEnd) - return err -} -func (s *S3Driver) deleteGE(ctx context.Context, key Key, timestamp Timestamp) error { - keys, _, err := s.driver.GetByPrefix(ctx, key, true) - if err != nil { - return err - } - keyStart, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - panic(err) - } - err = s.driver.DeleteRange(ctx, []byte(keyStart), keys[len(keys)-1]) - return err -} -func (s *S3Driver) deleteRange(ctx context.Context, key Key, start Timestamp, end Timestamp) error { - keyStart, err := codec.MvccEncode(key, start, "") - if err != nil { - return err - } - keyEnd, err := codec.MvccEncode(key, end, "") - if err != nil { - return err - } - err = s.driver.DeleteRange(ctx, keyStart, keyEnd) - return err -} - -func (s *S3Driver) GetRow(ctx context.Context, key Key, timestamp Timestamp) (Value, error) { - minioKey, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - return nil, err - } - - keys, values, err := s.driver.Scan(ctx, append(key, byte('_')), minioKey, 1, false) - if values == nil || keys == nil { - return nil, err - } - - _, _, suffix, err := codec.MvccDecode(keys[0]) - if err != nil { - return nil, err - } - if suffix == "delete" { - return nil, nil - } - - return values[0], err -} -func (s *S3Driver) GetRows(ctx context.Context, keys []Key, timestamps []Timestamp) ([]Value, error) { - var values []Value - for i, key := range keys { - value, err := s.GetRow(ctx, key, timestamps[i]) - if err != nil { - return nil, err - } - values = append(values, value) - } - return values, nil -} - -func (s *S3Driver) PutRow(ctx context.Context, key Key, value Value, segment string, timestamp Timestamp) error { - minioKey, err := codec.MvccEncode(key, timestamp, segment) - if err != nil { - return err - } - err = s.driver.Put(ctx, minioKey, value) - return err -} -func (s *S3Driver) PutRows(ctx context.Context, keys []Key, values []Value, segments []string, timestamps []Timestamp) error { - maxThread := 100 - batchSize := 1 - keysLength := len(keys) - - if keysLength/batchSize > maxThread { - batchSize = keysLength / maxThread - } - - batchNums := keysLength / batchSize - - if keysLength%batchSize != 0 { - batchNums = keysLength/batchSize + 1 - } - - errCh := make(chan error) - f := func(ctx2 context.Context, keys2 []Key, values2 []Value, segments2 []string, timestamps2 []Timestamp) { - for i := 0; i < len(keys2); i++ { - err := s.PutRow(ctx2, keys2[i], values2[i], segments2[i], timestamps2[i]) - errCh <- err - } - } - for i := 0; i < batchNums; i++ { - j := i - go func() { - start, end := j*batchSize, (j+1)*batchSize - if len(keys) < end { - end = len(keys) - } - f(ctx, keys[start:end], values[start:end], segments[start:end], timestamps[start:end]) - }() - } - - for i := 0; i < len(keys); i++ { - if err := <-errCh; err != nil { - return err - } - } - return nil -} - -func (s *S3Driver) GetSegments(ctx context.Context, key Key, timestamp Timestamp) ([]string, error) { - keyEnd, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - return nil, err - } - keys, _, err := s.driver.Scan(ctx, append(key, byte('_')), keyEnd, -1, true) - if err != nil { - return nil, err - } - segmentsSet := map[string]bool{} - for _, key := range keys { - _, _, segment, err := codec.MvccDecode(key) - if err != nil { - panic("must no error") - } - if segment != "delete" { - segmentsSet[segment] = true - } - } - - var segments []string - for k, v := range segmentsSet { - if v { - segments = append(segments, k) - } - } - return segments, err -} - -func (s *S3Driver) DeleteRow(ctx context.Context, key Key, timestamp Timestamp) error { - minioKey, err := codec.MvccEncode(key, timestamp, "delete") - if err != nil { - return err - } - value := []byte("0") - err = s.driver.Put(ctx, minioKey, value) - return err -} - -func (s *S3Driver) DeleteRows(ctx context.Context, keys []Key, timestamps []Timestamp) error { - maxThread := 100 - batchSize := 1 - keysLength := len(keys) - - if keysLength/batchSize > maxThread { - batchSize = keysLength / maxThread - } - - batchNums := keysLength / batchSize - - if keysLength%batchSize != 0 { - batchNums = keysLength/batchSize + 1 - } - - errCh := make(chan error) - f := func(ctx2 context.Context, keys2 []Key, timestamps2 []Timestamp) { - for i := 0; i < len(keys2); i++ { - err := s.DeleteRow(ctx2, keys2[i], timestamps2[i]) - errCh <- err - } - } - for i := 0; i < batchNums; i++ { - j := i - go func() { - start, end := j*batchSize, (j+1)*batchSize - if len(keys) < end { - end = len(keys) - } - f(ctx, keys[start:end], timestamps[start:end]) - }() - } - - for i := 0; i < len(keys); i++ { - if err := <-errCh; err != nil { - return err - } - } - return nil -} - -func (s *S3Driver) PutLog(ctx context.Context, key Key, value Value, timestamp Timestamp, channel int) error { - logKey := codec.LogEncode(key, timestamp, channel) - err := s.driver.Put(ctx, logKey, value) - return err -} - -func (s *S3Driver) GetLog(ctx context.Context, start Timestamp, end Timestamp, channels []int) ([]Value, error) { - keys, values, err := s.driver.GetByPrefix(ctx, []byte("log_"), false) - if err != nil { - return nil, err - } - - var resultValues []Value - for i, key := range keys { - _, ts, channel, err := codec.LogDecode(string(key)) - if err != nil { - return nil, err - } - if ts >= start && ts <= end { - for j := 0; j < len(channels); j++ { - if channel == channels[j] { - resultValues = append(resultValues, values[i]) - } - } - } - } - - return resultValues, nil -} - -func (s *S3Driver) GetSegmentIndex(ctx context.Context, segment string) (SegmentIndex, error) { - - return s.driver.Get(ctx, codec.SegmentEncode(segment, "index")) -} - -func (s *S3Driver) PutSegmentIndex(ctx context.Context, segment string, index SegmentIndex) error { - - return s.driver.Put(ctx, codec.SegmentEncode(segment, "index"), index) -} - -func (s *S3Driver) DeleteSegmentIndex(ctx context.Context, segment string) error { - - return s.driver.Delete(ctx, codec.SegmentEncode(segment, "index")) -} - -func (s *S3Driver) GetSegmentDL(ctx context.Context, segment string) (SegmentDL, error) { - - return s.driver.Get(ctx, codec.SegmentEncode(segment, "DL")) -} - -func (s *S3Driver) PutSegmentDL(ctx context.Context, segment string, log SegmentDL) error { - - return s.driver.Put(ctx, codec.SegmentEncode(segment, "DL"), log) -} - -func (s *S3Driver) DeleteSegmentDL(ctx context.Context, segment string) error { - - return s.driver.Delete(ctx, codec.SegmentEncode(segment, "DL")) -} diff --git a/internal/storage/internal/minio/codec/codec.go b/internal/storage/internal/minio/codec/codec.go deleted file mode 100644 index 4d2b76ee2f939b9f221b47d2ef3f38ce8fe0c71e..0000000000000000000000000000000000000000 --- a/internal/storage/internal/minio/codec/codec.go +++ /dev/null @@ -1,101 +0,0 @@ -package codec - -import ( - "errors" - "fmt" -) - -func MvccEncode(key []byte, ts uint64, suffix string) ([]byte, error) { - return []byte(string(key) + "_" + fmt.Sprintf("%016x", ^ts) + "_" + suffix), nil -} - -func MvccDecode(key []byte) (string, uint64, string, error) { - if len(key) < 16 { - return "", 0, "", errors.New("insufficient bytes to decode value") - } - - suffixIndex := 0 - TSIndex := 0 - undersCount := 0 - for i := len(key) - 1; i > 0; i-- { - if key[i] == byte('_') { - undersCount++ - if undersCount == 1 { - suffixIndex = i + 1 - } - if undersCount == 2 { - TSIndex = i + 1 - break - } - } - } - if suffixIndex == 0 || TSIndex == 0 { - return "", 0, "", errors.New("key is wrong formatted") - } - - var TS uint64 - _, err := fmt.Sscanf(string(key[TSIndex:suffixIndex-1]), "%x", &TS) - TS = ^TS - if err != nil { - return "", 0, "", err - } - - return string(key[0 : TSIndex-1]), TS, string(key[suffixIndex:]), nil -} - -func LogEncode(key []byte, ts uint64, channel int) []byte { - suffix := string(key) + "_" + fmt.Sprintf("%d", channel) - logKey, err := MvccEncode([]byte("log"), ts, suffix) - if err != nil { - return nil - } - return logKey -} - -func LogDecode(logKey string) (string, uint64, int, error) { - if len(logKey) < 16 { - return "", 0, 0, errors.New("insufficient bytes to decode value") - } - - channelIndex := 0 - keyIndex := 0 - TSIndex := 0 - undersCount := 0 - - for i := len(logKey) - 1; i > 0; i-- { - if logKey[i] == '_' { - undersCount++ - if undersCount == 1 { - channelIndex = i + 1 - } - if undersCount == 2 { - keyIndex = i + 1 - } - if undersCount == 3 { - TSIndex = i + 1 - break - } - } - } - if channelIndex == 0 || TSIndex == 0 || keyIndex == 0 || logKey[:TSIndex-1] != "log" { - return "", 0, 0, errors.New("key is wrong formatted") - } - - var TS uint64 - var channel int - _, err := fmt.Sscanf(logKey[TSIndex:keyIndex-1], "%x", &TS) - if err != nil { - return "", 0, 0, err - } - TS = ^TS - - _, err = fmt.Sscanf(logKey[channelIndex:], "%d", &channel) - if err != nil { - return "", 0, 0, err - } - return logKey[keyIndex : channelIndex-1], TS, channel, nil -} - -func SegmentEncode(segment string, suffix string) []byte { - return []byte(segment + "_" + suffix) -} diff --git a/internal/storage/internal/minio/minio_store.go b/internal/storage/internal/minio/minio_store.go deleted file mode 100644 index 18e2512401ac94093cd55eb89b016a9be4d92dcf..0000000000000000000000000000000000000000 --- a/internal/storage/internal/minio/minio_store.go +++ /dev/null @@ -1,361 +0,0 @@ -package miniodriver - -import ( - "context" - - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/zilliztech/milvus-distributed/internal/storage/internal/minio/codec" - storageType "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -type MinioDriver struct { - driver *minioStore -} - -var bucketName string - -func NewMinioDriver(ctx context.Context, option storageType.Option) (*MinioDriver, error) { - // to-do read conf - var endPoint = "localhost:9000" - var accessKeyID = "testminio" - var secretAccessKey = "testminio" - var useSSL = false - - bucketName := option.BucketName - - minioClient, err := minio.New(endPoint, &minio.Options{ - Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), - Secure: useSSL, - }) - - if err != nil { - return nil, err - } - - bucketExists, err := minioClient.BucketExists(ctx, bucketName) - if err != nil { - return nil, err - } - - if !bucketExists { - err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{}) - if err != nil { - return nil, err - } - } - return &MinioDriver{ - &minioStore{ - client: minioClient, - }, - }, nil -} - -func (s *MinioDriver) put(ctx context.Context, key storageType.Key, value storageType.Value, timestamp storageType.Timestamp, suffix string) error { - minioKey, err := codec.MvccEncode(key, timestamp, suffix) - if err != nil { - return err - } - - err = s.driver.Put(ctx, minioKey, value) - return err -} - -func (s *MinioDriver) scanLE(ctx context.Context, key storageType.Key, timestamp storageType.Timestamp, keyOnly bool) ([]storageType.Timestamp, []storageType.Key, []storageType.Value, error) { - keyEnd, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - return nil, nil, nil, err - } - - keys, values, err := s.driver.Scan(ctx, key, []byte(keyEnd), -1, keyOnly) - if err != nil { - return nil, nil, nil, err - } - - var timestamps []storageType.Timestamp - for _, key := range keys { - _, timestamp, _, _ := codec.MvccDecode(key) - timestamps = append(timestamps, timestamp) - } - - return timestamps, keys, values, nil -} - -func (s *MinioDriver) scanGE(ctx context.Context, key storageType.Key, timestamp storageType.Timestamp, keyOnly bool) ([]storageType.Timestamp, []storageType.Key, []storageType.Value, error) { - keyStart, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - return nil, nil, nil, err - } - - keys, values, err := s.driver.Scan(ctx, key, keyStart, -1, keyOnly) - if err != nil { - return nil, nil, nil, err - } - - var timestamps []storageType.Timestamp - for _, key := range keys { - _, timestamp, _, _ := codec.MvccDecode(key) - timestamps = append(timestamps, timestamp) - } - - return timestamps, keys, values, nil -} - -//scan(ctx context.Context, key storageType.Key, start storageType.Timestamp, end storageType.Timestamp, withValue bool) ([]storageType.Timestamp, []storageType.Key, []storageType.Value, error) -func (s *MinioDriver) deleteLE(ctx context.Context, key storageType.Key, timestamp storageType.Timestamp) error { - keyEnd, err := codec.MvccEncode(key, timestamp, "delete") - if err != nil { - return err - } - err = s.driver.DeleteRange(ctx, key, keyEnd) - return err -} -func (s *MinioDriver) deleteGE(ctx context.Context, key storageType.Key, timestamp storageType.Timestamp) error { - keys, _, err := s.driver.GetByPrefix(ctx, key, true) - if err != nil { - return err - } - keyStart, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - panic(err) - } - err = s.driver.DeleteRange(ctx, keyStart, keys[len(keys)-1]) - if err != nil { - panic(err) - } - return nil -} -func (s *MinioDriver) deleteRange(ctx context.Context, key storageType.Key, start storageType.Timestamp, end storageType.Timestamp) error { - keyStart, err := codec.MvccEncode(key, start, "") - if err != nil { - return err - } - keyEnd, err := codec.MvccEncode(key, end, "") - if err != nil { - return err - } - err = s.driver.DeleteRange(ctx, keyStart, keyEnd) - return err -} - -func (s *MinioDriver) GetRow(ctx context.Context, key storageType.Key, timestamp storageType.Timestamp) (storageType.Value, error) { - minioKey, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - return nil, err - } - - keys, values, err := s.driver.Scan(ctx, append(key, byte('_')), minioKey, 1, false) - if values == nil || keys == nil { - return nil, err - } - - _, _, suffix, err := codec.MvccDecode(keys[0]) - if err != nil { - return nil, err - } - if suffix == "delete" { - return nil, nil - } - - return values[0], err -} -func (s *MinioDriver) GetRows(ctx context.Context, keys []storageType.Key, timestamps []storageType.Timestamp) ([]storageType.Value, error) { - var values []storageType.Value - for i, key := range keys { - value, err := s.GetRow(ctx, key, timestamps[i]) - if err != nil { - return nil, err - } - values = append(values, value) - } - return values, nil -} - -func (s *MinioDriver) PutRow(ctx context.Context, key storageType.Key, value storageType.Value, segment string, timestamp storageType.Timestamp) error { - minioKey, err := codec.MvccEncode(key, timestamp, segment) - if err != nil { - return err - } - err = s.driver.Put(ctx, minioKey, value) - return err -} -func (s *MinioDriver) PutRows(ctx context.Context, keys []storageType.Key, values []storageType.Value, segments []string, timestamps []storageType.Timestamp) error { - maxThread := 100 - batchSize := 1 - keysLength := len(keys) - - if keysLength/batchSize > maxThread { - batchSize = keysLength / maxThread - } - - batchNums := keysLength / batchSize - - if keysLength%batchSize != 0 { - batchNums = keysLength/batchSize + 1 - } - - errCh := make(chan error) - f := func(ctx2 context.Context, keys2 []storageType.Key, values2 []storageType.Value, segments2 []string, timestamps2 []storageType.Timestamp) { - for i := 0; i < len(keys2); i++ { - err := s.PutRow(ctx2, keys2[i], values2[i], segments2[i], timestamps2[i]) - errCh <- err - } - } - for i := 0; i < batchNums; i++ { - j := i - go func() { - start, end := j*batchSize, (j+1)*batchSize - if len(keys) < end { - end = len(keys) - } - f(ctx, keys[start:end], values[start:end], segments[start:end], timestamps[start:end]) - }() - } - - for i := 0; i < len(keys); i++ { - if err := <-errCh; err != nil { - return err - } - } - return nil -} - -func (s *MinioDriver) GetSegments(ctx context.Context, key storageType.Key, timestamp storageType.Timestamp) ([]string, error) { - keyEnd, err := codec.MvccEncode(key, timestamp, "") - if err != nil { - return nil, err - } - keys, _, err := s.driver.Scan(ctx, append(key, byte('_')), keyEnd, -1, true) - if err != nil { - return nil, err - } - segmentsSet := map[string]bool{} - for _, key := range keys { - _, _, segment, err := codec.MvccDecode(key) - if err != nil { - panic("must no error") - } - if segment != "delete" { - segmentsSet[segment] = true - } - } - - var segments []string - for k, v := range segmentsSet { - if v { - segments = append(segments, k) - } - } - return segments, err -} - -func (s *MinioDriver) DeleteRow(ctx context.Context, key storageType.Key, timestamp storageType.Timestamp) error { - minioKey, err := codec.MvccEncode(key, timestamp, "delete") - if err != nil { - return err - } - value := []byte("0") - err = s.driver.Put(ctx, minioKey, value) - return err -} - -func (s *MinioDriver) DeleteRows(ctx context.Context, keys []storageType.Key, timestamps []storageType.Timestamp) error { - maxThread := 100 - batchSize := 1 - keysLength := len(keys) - - if keysLength/batchSize > maxThread { - batchSize = keysLength / maxThread - } - - batchNums := keysLength / batchSize - - if keysLength%batchSize != 0 { - batchNums = keysLength/batchSize + 1 - } - - errCh := make(chan error) - f := func(ctx2 context.Context, keys2 []storageType.Key, timestamps2 []storageType.Timestamp) { - for i := 0; i < len(keys2); i++ { - err := s.DeleteRow(ctx2, keys2[i], timestamps2[i]) - errCh <- err - } - } - for i := 0; i < batchNums; i++ { - j := i - go func() { - start, end := j*batchSize, (j+1)*batchSize - if len(keys) < end { - end = len(keys) - } - f(ctx, keys[start:end], timestamps[start:end]) - }() - } - - for i := 0; i < len(keys); i++ { - if err := <-errCh; err != nil { - return err - } - } - return nil -} - -func (s *MinioDriver) PutLog(ctx context.Context, key storageType.Key, value storageType.Value, timestamp storageType.Timestamp, channel int) error { - logKey := codec.LogEncode(key, timestamp, channel) - err := s.driver.Put(ctx, logKey, value) - return err -} - -func (s *MinioDriver) GetLog(ctx context.Context, start storageType.Timestamp, end storageType.Timestamp, channels []int) ([]storageType.Value, error) { - keys, values, err := s.driver.GetByPrefix(ctx, []byte("log_"), false) - if err != nil { - return nil, err - } - - var resultValues []storageType.Value - for i, key := range keys { - _, ts, channel, err := codec.LogDecode(string(key)) - if err != nil { - return nil, err - } - if ts >= start && ts <= end { - for j := 0; j < len(channels); j++ { - if channel == channels[j] { - resultValues = append(resultValues, values[i]) - } - } - } - } - - return resultValues, nil -} - -func (s *MinioDriver) GetSegmentIndex(ctx context.Context, segment string) (storageType.SegmentIndex, error) { - - return s.driver.Get(ctx, codec.SegmentEncode(segment, "index")) -} - -func (s *MinioDriver) PutSegmentIndex(ctx context.Context, segment string, index storageType.SegmentIndex) error { - - return s.driver.Put(ctx, codec.SegmentEncode(segment, "index"), index) -} - -func (s *MinioDriver) DeleteSegmentIndex(ctx context.Context, segment string) error { - - return s.driver.Delete(ctx, codec.SegmentEncode(segment, "index")) -} - -func (s *MinioDriver) GetSegmentDL(ctx context.Context, segment string) (storageType.SegmentDL, error) { - - return s.driver.Get(ctx, codec.SegmentEncode(segment, "DL")) -} - -func (s *MinioDriver) PutSegmentDL(ctx context.Context, segment string, log storageType.SegmentDL) error { - - return s.driver.Put(ctx, codec.SegmentEncode(segment, "DL"), log) -} - -func (s *MinioDriver) DeleteSegmentDL(ctx context.Context, segment string) error { - - return s.driver.Delete(ctx, codec.SegmentEncode(segment, "DL")) -} diff --git a/internal/storage/internal/minio/minio_storeEngine.go b/internal/storage/internal/minio/minio_storeEngine.go deleted file mode 100644 index 64d74e859032b0306c9e9cce655d48d3df52a8a0..0000000000000000000000000000000000000000 --- a/internal/storage/internal/minio/minio_storeEngine.go +++ /dev/null @@ -1,130 +0,0 @@ -package miniodriver - -import ( - "bytes" - "context" - "io" - - "github.com/minio/minio-go/v7" - . "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -type minioStore struct { - client *minio.Client -} - -func (s *minioStore) Put(ctx context.Context, key Key, value Value) error { - reader := bytes.NewReader(value) - _, err := s.client.PutObject(ctx, bucketName, string(key), reader, int64(len(value)), minio.PutObjectOptions{}) - - if err != nil { - return err - } - - return err -} - -func (s *minioStore) Get(ctx context.Context, key Key) (Value, error) { - object, err := s.client.GetObject(ctx, bucketName, string(key), minio.GetObjectOptions{}) - if err != nil { - return nil, err - } - - size := 256 * 1024 - buf := make([]byte, size) - n, err := object.Read(buf) - if err != nil && err != io.EOF { - return nil, err - } - return buf[:n], nil -} - -func (s *minioStore) GetByPrefix(ctx context.Context, prefix Key, keyOnly bool) ([]Key, []Value, error) { - objects := s.client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{Prefix: string(prefix)}) - - var objectsKeys []Key - var objectsValues []Value - - for object := range objects { - objectsKeys = append(objectsKeys, []byte(object.Key)) - if !keyOnly { - value, err := s.Get(ctx, []byte(object.Key)) - if err != nil { - return nil, nil, err - } - objectsValues = append(objectsValues, value) - } - } - - return objectsKeys, objectsValues, nil - -} - -func (s *minioStore) Scan(ctx context.Context, keyStart Key, keyEnd Key, limit int, keyOnly bool) ([]Key, []Value, error) { - var keys []Key - var values []Value - limitCount := uint(limit) - for object := range s.client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{Prefix: string(keyStart)}) { - if object.Key >= string(keyEnd) { - keys = append(keys, []byte(object.Key)) - if !keyOnly { - value, err := s.Get(ctx, []byte(object.Key)) - if err != nil { - return nil, nil, err - } - values = append(values, value) - } - limitCount-- - if limitCount <= 0 { - break - } - } - } - - return keys, values, nil -} - -func (s *minioStore) Delete(ctx context.Context, key Key) error { - err := s.client.RemoveObject(ctx, bucketName, string(key), minio.RemoveObjectOptions{}) - return err -} - -func (s *minioStore) DeleteByPrefix(ctx context.Context, prefix Key) error { - objectsCh := make(chan minio.ObjectInfo) - - go func() { - defer close(objectsCh) - - for object := range s.client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{Prefix: string(prefix)}) { - objectsCh <- object - } - }() - - for rErr := range s.client.RemoveObjects(ctx, bucketName, objectsCh, minio.RemoveObjectsOptions{GovernanceBypass: true}) { - if rErr.Err != nil { - return rErr.Err - } - } - return nil -} - -func (s *minioStore) DeleteRange(ctx context.Context, keyStart Key, keyEnd Key) error { - objectsCh := make(chan minio.ObjectInfo) - - go func() { - defer close(objectsCh) - - for object := range s.client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{Prefix: string(keyStart)}) { - if object.Key <= string(keyEnd) { - objectsCh <- object - } - } - }() - - for rErr := range s.client.RemoveObjects(ctx, bucketName, objectsCh, minio.RemoveObjectsOptions{GovernanceBypass: true}) { - if rErr.Err != nil { - return rErr.Err - } - } - return nil -} diff --git a/internal/storage/internal/minio/minio_test.go b/internal/storage/internal/minio/minio_test.go deleted file mode 100644 index d98a98cfeaac2a4d77b8cf999940a04c1a61c71d..0000000000000000000000000000000000000000 --- a/internal/storage/internal/minio/minio_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package miniodriver - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - storagetype "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -var option = storagetype.Option{BucketName: "zilliz-hz"} -var ctx = context.Background() -var client, err = NewMinioDriver(ctx, option) - -func TestMinioDriver_PutRowAndGetRow(t *testing.T) { - err = client.PutRow(ctx, []byte("bar"), []byte("abcdefghijklmnoopqrstuvwxyz"), "SegmentA", 1) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("bar"), []byte("djhfkjsbdfbsdughorsgsdjhgoisdgh"), "SegmentA", 2) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("bar"), []byte("123854676ershdgfsgdfk,sdhfg;sdi8"), "SegmentB", 3) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("bar1"), []byte("testkeybarorbar_1"), "SegmentC", 3) - assert.Nil(t, err) - object, _ := client.GetRow(ctx, []byte("bar"), 5) - assert.Equal(t, "abcdefghijklmnoopqrstuvwxyz", string(object)) - object, _ = client.GetRow(ctx, []byte("bar"), 2) - assert.Equal(t, "djhfkjsbdfbsdughorsgsdjhgoisdgh", string(object)) - object, _ = client.GetRow(ctx, []byte("bar"), 5) - assert.Equal(t, "123854676ershdgfsgdfk,sdhfg;sdi8", string(object)) - object, _ = client.GetRow(ctx, []byte("bar1"), 5) - assert.Equal(t, "testkeybarorbar_1", string(object)) -} - -func TestMinioDriver_DeleteRow(t *testing.T) { - err = client.DeleteRow(ctx, []byte("bar"), 5) - assert.Nil(t, err) - object, _ := client.GetRow(ctx, []byte("bar"), 6) - assert.Nil(t, object) - err = client.DeleteRow(ctx, []byte("bar1"), 5) - assert.Nil(t, err) - object2, _ := client.GetRow(ctx, []byte("bar1"), 6) - assert.Nil(t, object2) -} - -func TestMinioDriver_GetSegments(t *testing.T) { - err = client.PutRow(ctx, []byte("seg"), []byte("abcdefghijklmnoopqrstuvwxyz"), "SegmentA", 1) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("seg"), []byte("djhfkjsbdfbsdughorsgsdjhgoisdgh"), "SegmentA", 2) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("seg"), []byte("123854676ershdgfsgdfk,sdhfg;sdi8"), "SegmentB", 3) - assert.Nil(t, err) - err = client.PutRow(ctx, []byte("seg2"), []byte("testkeybarorbar_1"), "SegmentC", 1) - assert.Nil(t, err) - - segements, err := client.GetSegments(ctx, []byte("seg"), 4) - assert.Nil(t, err) - assert.Equal(t, 2, len(segements)) - if segements[0] == "SegmentA" { - assert.Equal(t, "SegmentA", segements[0]) - assert.Equal(t, "SegmentB", segements[1]) - } else { - assert.Equal(t, "SegmentB", segements[0]) - assert.Equal(t, "SegmentA", segements[1]) - } -} - -func TestMinioDriver_PutRowsAndGetRows(t *testing.T) { - keys := [][]byte{[]byte("foo"), []byte("bar")} - values := [][]byte{[]byte("The key is foo!"), []byte("The key is bar!")} - segments := []string{"segmentA", "segmentB"} - timestamps := []uint64{1, 2} - err = client.PutRows(ctx, keys, values, segments, timestamps) - assert.Nil(t, err) - - objects, err := client.GetRows(ctx, keys, timestamps) - assert.Nil(t, err) - assert.Equal(t, "The key is foo!", string(objects[0])) - assert.Equal(t, "The key is bar!", string(objects[1])) -} - -func TestMinioDriver_DeleteRows(t *testing.T) { - keys := [][]byte{[]byte("foo"), []byte("bar")} - timestamps := []uint64{3, 3} - err := client.DeleteRows(ctx, keys, timestamps) - assert.Nil(t, err) - - objects, err := client.GetRows(ctx, keys, timestamps) - assert.Nil(t, err) - assert.Nil(t, objects[0]) - assert.Nil(t, objects[1]) -} - -func TestMinioDriver_PutLogAndGetLog(t *testing.T) { - err = client.PutLog(ctx, []byte("insert"), []byte("This is insert log!"), 1, 11) - assert.Nil(t, err) - err = client.PutLog(ctx, []byte("delete"), []byte("This is delete log!"), 2, 10) - assert.Nil(t, err) - err = client.PutLog(ctx, []byte("update"), []byte("This is update log!"), 3, 9) - assert.Nil(t, err) - err = client.PutLog(ctx, []byte("select"), []byte("This is select log!"), 4, 8) - assert.Nil(t, err) - - channels := []int{5, 8, 9, 10, 11, 12, 13} - logValues, err := client.GetLog(ctx, 0, 5, channels) - assert.Nil(t, err) - assert.Equal(t, "This is select log!", string(logValues[0])) - assert.Equal(t, "This is update log!", string(logValues[1])) - assert.Equal(t, "This is delete log!", string(logValues[2])) - assert.Equal(t, "This is insert log!", string(logValues[3])) -} - -func TestMinioDriver_Segment(t *testing.T) { - err := client.PutSegmentIndex(ctx, "segmentA", []byte("This is segmentA's index!")) - assert.Nil(t, err) - - segmentIndex, err := client.GetSegmentIndex(ctx, "segmentA") - assert.Equal(t, "This is segmentA's index!", string(segmentIndex)) - assert.Nil(t, err) - - err = client.DeleteSegmentIndex(ctx, "segmentA") - assert.Nil(t, err) -} - -func TestMinioDriver_SegmentDL(t *testing.T) { - err := client.PutSegmentDL(ctx, "segmentB", []byte("This is segmentB's delete log!")) - assert.Nil(t, err) - - segmentDL, err := client.GetSegmentDL(ctx, "segmentB") - assert.Nil(t, err) - assert.Equal(t, "This is segmentB's delete log!", string(segmentDL)) - - err = client.DeleteSegmentDL(ctx, "segmentB") - assert.Nil(t, err) -} diff --git a/internal/storage/internal/tikv/codec/codec.go b/internal/storage/internal/tikv/codec/codec.go deleted file mode 100644 index ca09296097e8778ca1023c3395801e7cb0e99669..0000000000000000000000000000000000000000 --- a/internal/storage/internal/tikv/codec/codec.go +++ /dev/null @@ -1,62 +0,0 @@ -package codec - -import ( - "encoding/binary" - "errors" - - "github.com/tikv/client-go/codec" -) - -var ( - Delimiter = byte('_') - DelimiterPlusOne = Delimiter + 0x01 - DeleteMark = byte('d') - SegmentIndexMark = byte('i') - SegmentDLMark = byte('d') -) - -// EncodeKey append timestamp, delimiter, and suffix string -// to one slice key. -// Note: suffix string should not contains Delimiter -func EncodeKey(key []byte, timestamp uint64, suffix string) []byte { - //TODO: should we encode key to memory comparable - ret := EncodeDelimiter(key, Delimiter) - ret = codec.EncodeUintDesc(ret, timestamp) - return append(ret, suffix...) -} - -func DecodeKey(key []byte) ([]byte, uint64, string, error) { - if len(key) < 8 { - return nil, 0, "", errors.New("insufficient bytes to decode value") - } - - lenDeKey := 0 - for i := len(key) - 1; i > 0; i-- { - if key[i] == Delimiter { - lenDeKey = i - break - } - } - - if lenDeKey == 0 || lenDeKey+8 > len(key) { - return nil, 0, "", errors.New("insufficient bytes to decode value") - } - - tsBytes := key[lenDeKey+1 : lenDeKey+9] - ts := binary.BigEndian.Uint64(tsBytes) - suffix := string(key[lenDeKey+9:]) - key = key[:lenDeKey-1] - return key, ^ts, suffix, nil -} - -// EncodeDelimiter append a delimiter byte to slice b, and return the appended slice. -func EncodeDelimiter(b []byte, delimiter byte) []byte { - return append(b, delimiter) -} - -func EncodeSegment(segName []byte, segType byte) []byte { - segmentKey := []byte("segment") - segmentKey = append(segmentKey, Delimiter) - segmentKey = append(segmentKey, segName...) - return append(segmentKey, Delimiter, segType) -} diff --git a/internal/storage/internal/tikv/tikv_store.go b/internal/storage/internal/tikv/tikv_store.go deleted file mode 100644 index 5ecf8936f675f574f262219013001db4519f77cd..0000000000000000000000000000000000000000 --- a/internal/storage/internal/tikv/tikv_store.go +++ /dev/null @@ -1,389 +0,0 @@ -package tikvdriver - -import ( - "context" - "errors" - "strconv" - "strings" - - "github.com/tikv/client-go/config" - "github.com/tikv/client-go/rawkv" - . "github.com/zilliztech/milvus-distributed/internal/storage/internal/tikv/codec" - . "github.com/zilliztech/milvus-distributed/internal/storage/type" - storagetype "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -func keyAddOne(key Key) Key { - if key == nil { - return nil - } - lenKey := len(key) - ret := make(Key, lenKey) - copy(ret, key) - ret[lenKey-1] += 0x01 - return ret -} - -type tikvEngine struct { - client *rawkv.Client - conf config.Config -} - -func (e tikvEngine) Put(ctx context.Context, key Key, value Value) error { - return e.client.Put(ctx, key, value) -} - -func (e tikvEngine) BatchPut(ctx context.Context, keys []Key, values []Value) error { - return e.client.BatchPut(ctx, keys, values) -} - -func (e tikvEngine) Get(ctx context.Context, key Key) (Value, error) { - return e.client.Get(ctx, key) -} - -func (e tikvEngine) GetByPrefix(ctx context.Context, prefix Key, keyOnly bool) (keys []Key, values []Value, err error) { - startKey := prefix - endKey := keyAddOne(prefix) - limit := e.conf.Raw.MaxScanLimit - for { - ks, vs, err := e.Scan(ctx, startKey, endKey, limit, keyOnly) - if err != nil { - return keys, values, err - } - keys = append(keys, ks...) - values = append(values, vs...) - if len(ks) < limit { - break - } - // update the start key, and exclude the start key - startKey = append(ks[len(ks)-1], '\000') - } - return -} - -func (e tikvEngine) Scan(ctx context.Context, startKey Key, endKey Key, limit int, keyOnly bool) ([]Key, []Value, error) { - return e.client.Scan(ctx, startKey, endKey, limit, rawkv.ScanOption{KeyOnly: keyOnly}) -} - -func (e tikvEngine) Delete(ctx context.Context, key Key) error { - return e.client.Delete(ctx, key) -} - -func (e tikvEngine) DeleteByPrefix(ctx context.Context, prefix Key) error { - startKey := prefix - endKey := keyAddOne(prefix) - return e.client.DeleteRange(ctx, startKey, endKey) -} - -func (e tikvEngine) DeleteRange(ctx context.Context, startKey Key, endKey Key) error { - return e.client.DeleteRange(ctx, startKey, endKey) -} - -func (e tikvEngine) Close() error { - return e.client.Close() -} - -type TikvStore struct { - engine *tikvEngine -} - -func NewTikvStore(ctx context.Context, option storagetype.Option) (*TikvStore, error) { - - conf := config.Default() - client, err := rawkv.NewClient(ctx, []string{option.TikvAddress}, conf) - if err != nil { - return nil, err - } - return &TikvStore{ - &tikvEngine{ - client: client, - conf: conf, - }, - }, nil -} - -func (s *TikvStore) Name() string { - return "TiKV storage" -} - -func (s *TikvStore) put(ctx context.Context, key Key, value Value, timestamp Timestamp, suffix string) error { - return s.engine.Put(ctx, EncodeKey(key, timestamp, suffix), value) -} - -func (s *TikvStore) scanLE(ctx context.Context, key Key, timestamp Timestamp, keyOnly bool) ([]Timestamp, []Key, []Value, error) { - panic("implement me") -} - -func (s *TikvStore) scanGE(ctx context.Context, key Key, timestamp Timestamp, keyOnly bool) ([]Timestamp, []Key, []Value, error) { - panic("implement me") -} - -func (s *TikvStore) scan(ctx context.Context, key Key, start Timestamp, end Timestamp, keyOnly bool) ([]Timestamp, []Key, []Value, error) { - //startKey := EncodeKey(key, start, "") - //endKey := EncodeKey(EncodeDelimiter(key, DelimiterPlusOne), end, "") - //return s.engine.Scan(ctx, startKey, endKey, -1, keyOnly) - panic("implement me") -} - -func (s *TikvStore) deleteLE(ctx context.Context, key Key, timestamp Timestamp) error { - panic("implement me") -} - -func (s *TikvStore) deleteGE(ctx context.Context, key Key, timestamp Timestamp) error { - panic("implement me") -} - -func (s *TikvStore) deleteRange(ctx context.Context, key Key, start Timestamp, end Timestamp) error { - panic("implement me") -} - -func (s *TikvStore) GetRow(ctx context.Context, key Key, timestamp Timestamp) (Value, error) { - startKey := EncodeKey(key, timestamp, "") - endKey := EncodeDelimiter(key, DelimiterPlusOne) - keys, values, err := s.engine.Scan(ctx, startKey, endKey, 1, false) - if err != nil || keys == nil { - return nil, err - } - _, _, suffix, err := DecodeKey(keys[0]) - if err != nil { - return nil, err - } - // key is marked deleted - if suffix == string(DeleteMark) { - return nil, nil - } - return values[0], nil -} - -// TODO: how to spilt keys to some batches -var batchSize = 100 - -type kvPair struct { - key Key - value Value - err error -} - -func batchKeys(keys []Key) [][]Key { - keysLen := len(keys) - numBatch := (keysLen-1)/batchSize + 1 - batches := make([][]Key, numBatch) - - for i := 0; i < numBatch; i++ { - batchStart := i * batchSize - batchEnd := batchStart + batchSize - // the last batch - if i == numBatch-1 { - batchEnd = keysLen - } - batches[i] = keys[batchStart:batchEnd] - } - return batches -} - -func (s *TikvStore) GetRows(ctx context.Context, keys []Key, timestamps []Timestamp) ([]Value, error) { - if len(keys) != len(timestamps) { - return nil, errors.New("the len of keys is not equal to the len of timestamps") - } - - batches := batchKeys(keys) - ch := make(chan kvPair, len(keys)) - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - for n, b := range batches { - batch := b - numBatch := n - go func() { - for i, key := range batch { - select { - case <-ctx.Done(): - return - default: - v, err := s.GetRow(ctx, key, timestamps[numBatch*batchSize+i]) - ch <- kvPair{ - key: key, - value: v, - err: err, - } - } - } - }() - } - - var err error - var values []Value - kvMap := make(map[string]Value) - for i := 0; i < len(keys); i++ { - kv := <-ch - if kv.err != nil { - cancel() - if err == nil { - err = kv.err - } - } - kvMap[string(kv.key)] = kv.value - } - for _, key := range keys { - values = append(values, kvMap[string(key)]) - } - return values, err -} - -func (s *TikvStore) PutRow(ctx context.Context, key Key, value Value, segment string, timestamp Timestamp) error { - return s.put(ctx, key, value, timestamp, segment) -} - -func (s *TikvStore) PutRows(ctx context.Context, keys []Key, values []Value, segments []string, timestamps []Timestamp) error { - if len(keys) != len(values) { - return errors.New("the len of keys is not equal to the len of values") - } - if len(keys) != len(timestamps) { - return errors.New("the len of keys is not equal to the len of timestamps") - } - - encodedKeys := make([]Key, len(keys)) - for i, key := range keys { - encodedKeys[i] = EncodeKey(key, timestamps[i], segments[i]) - } - return s.engine.BatchPut(ctx, encodedKeys, values) -} - -func (s *TikvStore) DeleteRow(ctx context.Context, key Key, timestamp Timestamp) error { - return s.put(ctx, key, Value{0x00}, timestamp, string(DeleteMark)) -} - -func (s *TikvStore) DeleteRows(ctx context.Context, keys []Key, timestamps []Timestamp) error { - encodeKeys := make([]Key, len(keys)) - values := make([]Value, len(keys)) - for i, key := range keys { - encodeKeys[i] = EncodeKey(key, timestamps[i], string(DeleteMark)) - values[i] = Value{0x00} - } - return s.engine.BatchPut(ctx, encodeKeys, values) -} - -//func (s *TikvStore) DeleteRows(ctx context.Context, keys []Key, timestamp Timestamp) error { -// batches := batchKeys(keys) -// ch := make(chan error, len(batches)) -// ctx, cancel := context.WithCancel(ctx) -// -// for _, b := range batches { -// batch := b -// go func() { -// for _, key := range batch { -// select { -// case <-ctx.Done(): -// return -// default: -// ch <- s.DeleteRow(ctx, key, timestamp) -// } -// } -// }() -// } -// -// var err error -// for i := 0; i < len(keys); i++ { -// if e := <-ch; e != nil { -// cancel() -// if err == nil { -// err = e -// } -// } -// } -// return err -//} - -func (s *TikvStore) PutLog(ctx context.Context, key Key, value Value, timestamp Timestamp, channel int) error { - suffix := string(EncodeDelimiter(key, DelimiterPlusOne)) + strconv.Itoa(channel) - return s.put(ctx, Key("log"), value, timestamp, suffix) -} - -func (s *TikvStore) GetLog(ctx context.Context, start Timestamp, end Timestamp, channels []int) (logs []Value, err error) { - key := Key("log") - startKey := EncodeKey(key, end, "") - endKey := EncodeKey(key, start, "") - // TODO: use for loop to ensure get all keys - keys, values, err := s.engine.Scan(ctx, startKey, endKey, s.engine.conf.Raw.MaxScanLimit, false) - if err != nil || keys == nil { - return nil, err - } - - for i, key := range keys { - _, _, suffix, err := DecodeKey(key) - log := values[i] - if err != nil { - return logs, err - } - - // no channels filter - if len(channels) == 0 { - logs = append(logs, log) - } - slice := strings.Split(suffix, string(DelimiterPlusOne)) - channel, err := strconv.Atoi(slice[len(slice)-1]) - if err != nil { - panic(err) - } - for _, item := range channels { - if item == channel { - logs = append(logs, log) - break - } - } - } - return -} - -func (s *TikvStore) GetSegmentIndex(ctx context.Context, segment string) (SegmentIndex, error) { - return s.engine.Get(ctx, EncodeSegment([]byte(segment), SegmentIndexMark)) -} - -func (s *TikvStore) PutSegmentIndex(ctx context.Context, segment string, index SegmentIndex) error { - return s.engine.Put(ctx, EncodeSegment([]byte(segment), SegmentIndexMark), index) -} - -func (s *TikvStore) DeleteSegmentIndex(ctx context.Context, segment string) error { - return s.engine.Delete(ctx, EncodeSegment([]byte(segment), SegmentIndexMark)) -} - -func (s *TikvStore) GetSegmentDL(ctx context.Context, segment string) (SegmentDL, error) { - return s.engine.Get(ctx, EncodeSegment([]byte(segment), SegmentDLMark)) -} - -func (s *TikvStore) PutSegmentDL(ctx context.Context, segment string, log SegmentDL) error { - return s.engine.Put(ctx, EncodeSegment([]byte(segment), SegmentDLMark), log) -} - -func (s *TikvStore) DeleteSegmentDL(ctx context.Context, segment string) error { - return s.engine.Delete(ctx, EncodeSegment([]byte(segment), SegmentDLMark)) -} - -func (s *TikvStore) GetSegments(ctx context.Context, key Key, timestamp Timestamp) ([]string, error) { - keys, _, err := s.engine.GetByPrefix(ctx, EncodeDelimiter(key, Delimiter), true) - if err != nil { - return nil, err - } - segmentsSet := map[string]bool{} - for _, key := range keys { - _, ts, segment, err := DecodeKey(key) - if err != nil { - panic("must no error") - } - if ts <= timestamp && segment != string(DeleteMark) { - segmentsSet[segment] = true - } - } - - var segments []string - for k, v := range segmentsSet { - if v { - segments = append(segments, k) - } - } - return segments, err -} - -func (s *TikvStore) Close() error { - return s.engine.Close() -} diff --git a/internal/storage/internal/tikv/tikv_test.go b/internal/storage/internal/tikv/tikv_test.go deleted file mode 100644 index 4e69d14d2c3079f7251123666881206a9af03d03..0000000000000000000000000000000000000000 --- a/internal/storage/internal/tikv/tikv_test.go +++ /dev/null @@ -1,293 +0,0 @@ -package tikvdriver - -import ( - "bytes" - "context" - "fmt" - "math" - "os" - "sort" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - . "github.com/zilliztech/milvus-distributed/internal/storage/internal/tikv/codec" - . "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -//var store TikvStore -var store *TikvStore -var option = Option{TikvAddress: "localhost:2379"} - -func TestMain(m *testing.M) { - store, _ = NewTikvStore(context.Background(), option) - exitCode := m.Run() - _ = store.Close() - os.Exit(exitCode) -} - -func TestTikvEngine_Prefix(t *testing.T) { - ctx := context.Background() - prefix := Key("key") - engine := store.engine - value := Value("value") - - // Put some key with same prefix - key := prefix - err := engine.Put(ctx, key, value) - require.Nil(t, err) - key = EncodeKey(prefix, 0, "") - err = engine.Put(ctx, key, value) - assert.Nil(t, err) - - // Get by prefix - ks, _, err := engine.GetByPrefix(ctx, prefix, true) - assert.Equal(t, 2, len(ks)) - assert.Nil(t, err) - - // Delete by prefix - err = engine.DeleteByPrefix(ctx, prefix) - assert.Nil(t, err) - ks, _, err = engine.GetByPrefix(ctx, prefix, true) - assert.Equal(t, 0, len(ks)) - assert.Nil(t, err) - - //Test large amount keys - num := engine.conf.Raw.MaxScanLimit + 1 - keys := make([]Key, num) - values := make([]Value, num) - for i := 0; i < num; i++ { - key = EncodeKey(prefix, uint64(i), "") - keys[i] = key - values[i] = value - } - err = engine.BatchPut(ctx, keys, values) - assert.Nil(t, err) - - ks, _, err = engine.GetByPrefix(ctx, prefix, true) - assert.Nil(t, err) - assert.Equal(t, num, len(ks)) - err = engine.DeleteByPrefix(ctx, prefix) - assert.Nil(t, err) -} - -func TestTikvStore_Row(t *testing.T) { - ctx := context.Background() - key := Key("key") - - // Add same row with different timestamp - err := store.PutRow(ctx, key, Value("value0"), "segment0", 0) - assert.Nil(t, err) - err = store.PutRow(ctx, key, Value("value1"), "segment0", 2) - assert.Nil(t, err) - - // Get most recent row using key and timestamp - v, err := store.GetRow(ctx, key, 3) - assert.Nil(t, err) - assert.Equal(t, Value("value1"), v) - v, err = store.GetRow(ctx, key, 2) - assert.Nil(t, err) - assert.Equal(t, Value("value1"), v) - v, err = store.GetRow(ctx, key, 1) - assert.Nil(t, err) - assert.Equal(t, Value("value0"), v) - - // Add a different row, but with same prefix - key1 := Key("key_y") - err = store.PutRow(ctx, key1, Value("valuey"), "segment0", 2) - assert.Nil(t, err) - - // Get most recent row using key and timestamp - v, err = store.GetRow(ctx, key, 3) - assert.Nil(t, err) - assert.Equal(t, Value("value1"), v) - v, err = store.GetRow(ctx, key1, 3) - assert.Nil(t, err) - assert.Equal(t, Value("valuey"), v) - - // Delete a row - err = store.DeleteRow(ctx, key, 4) - assert.Nil(t, err) - v, err = store.GetRow(ctx, key, 5) - assert.Nil(t, err) - assert.Nil(t, v) - - // Clear test data - err = store.engine.DeleteByPrefix(ctx, key) - assert.Nil(t, err) - k, va, err := store.engine.GetByPrefix(ctx, key, false) - assert.Nil(t, err) - assert.Nil(t, k) - assert.Nil(t, va) -} - -func TestTikvStore_BatchRow(t *testing.T) { - ctx := context.Background() - - // Prepare test data - size := 0 - var testKeys []Key - var testValues []Value - var segments []string - var timestamps []Timestamp - for i := 0; size/store.engine.conf.Raw.MaxBatchPutSize < 1; i++ { - key := fmt.Sprint("key", i) - size += len(key) - testKeys = append(testKeys, []byte(key)) - value := fmt.Sprint("value", i) - size += len(value) - testValues = append(testValues, []byte(value)) - segments = append(segments, "test") - v, err := store.GetRow(ctx, Key(key), math.MaxUint64) - assert.Nil(t, v) - assert.Nil(t, err) - } - - // Batch put rows - for range testKeys { - timestamps = append(timestamps, 1) - } - err := store.PutRows(ctx, testKeys, testValues, segments, timestamps) - assert.Nil(t, err) - - // Batch get rows - for i := range timestamps { - timestamps[i] = 2 - } - checkValues, err := store.GetRows(ctx, testKeys, timestamps) - assert.NotNil(t, checkValues) - assert.Nil(t, err) - assert.Equal(t, len(checkValues), len(testValues)) - for i := range testKeys { - assert.Equal(t, testValues[i], checkValues[i]) - } - - // Delete all test rows - for i := range timestamps { - timestamps[i] = math.MaxUint64 - } - err = store.DeleteRows(ctx, testKeys, timestamps) - assert.Nil(t, err) - // Ensure all test row is deleted - for i := range timestamps { - timestamps[i] = math.MaxUint64 - } - checkValues, err = store.GetRows(ctx, testKeys, timestamps) - assert.Nil(t, err) - for _, value := range checkValues { - assert.Nil(t, value) - } - - // Clean test data - err = store.engine.DeleteByPrefix(ctx, Key("key")) - assert.Nil(t, err) -} - -func TestTikvStore_GetSegments(t *testing.T) { - ctx := context.Background() - key := Key("key") - - // Put rows - err := store.PutRow(ctx, key, Value{0}, "a", 1) - assert.Nil(t, err) - err = store.PutRow(ctx, key, Value{0}, "a", 2) - assert.Nil(t, err) - err = store.PutRow(ctx, key, Value{0}, "c", 3) - assert.Nil(t, err) - - // Get segments - segs, err := store.GetSegments(ctx, key, 2) - assert.Nil(t, err) - assert.Equal(t, 1, len(segs)) - assert.Equal(t, "a", segs[0]) - - segs, err = store.GetSegments(ctx, key, 3) - assert.Nil(t, err) - assert.Equal(t, 2, len(segs)) - - // Clean test data - err = store.engine.DeleteByPrefix(ctx, key) - assert.Nil(t, err) -} - -func TestTikvStore_Log(t *testing.T) { - ctx := context.Background() - - // Put some log - err := store.PutLog(ctx, Key("key1"), Value("value1"), 1, 1) - assert.Nil(t, err) - err = store.PutLog(ctx, Key("key1"), Value("value1_1"), 1, 2) - assert.Nil(t, err) - err = store.PutLog(ctx, Key("key2"), Value("value2"), 2, 1) - assert.Nil(t, err) - - // Check log - log, err := store.GetLog(ctx, 0, 2, []int{1, 2}) - if err != nil { - panic(err) - } - sort.Slice(log, func(i, j int) bool { - return bytes.Compare(log[i], log[j]) == -1 - }) - assert.Equal(t, log[0], Value("value1")) - assert.Equal(t, log[1], Value("value1_1")) - assert.Equal(t, log[2], Value("value2")) - - // Delete test data - err = store.engine.DeleteByPrefix(ctx, Key("log")) - assert.Nil(t, err) -} - -func TestTikvStore_SegmentIndex(t *testing.T) { - ctx := context.Background() - - // Put segment index - err := store.PutSegmentIndex(ctx, "segment0", []byte("index0")) - assert.Nil(t, err) - err = store.PutSegmentIndex(ctx, "segment1", []byte("index1")) - assert.Nil(t, err) - - // Get segment index - index, err := store.GetSegmentIndex(ctx, "segment0") - assert.Nil(t, err) - assert.Equal(t, []byte("index0"), index) - index, err = store.GetSegmentIndex(ctx, "segment1") - assert.Nil(t, err) - assert.Equal(t, []byte("index1"), index) - - // Delete segment index - err = store.DeleteSegmentIndex(ctx, "segment0") - assert.Nil(t, err) - err = store.DeleteSegmentIndex(ctx, "segment1") - assert.Nil(t, err) - index, err = store.GetSegmentIndex(ctx, "segment0") - assert.Nil(t, err) - assert.Nil(t, index) -} - -func TestTikvStore_DeleteSegmentDL(t *testing.T) { - ctx := context.Background() - - // Put segment delete log - err := store.PutSegmentDL(ctx, "segment0", []byte("index0")) - assert.Nil(t, err) - err = store.PutSegmentDL(ctx, "segment1", []byte("index1")) - assert.Nil(t, err) - - // Get segment delete log - index, err := store.GetSegmentDL(ctx, "segment0") - assert.Nil(t, err) - assert.Equal(t, []byte("index0"), index) - index, err = store.GetSegmentDL(ctx, "segment1") - assert.Nil(t, err) - assert.Equal(t, []byte("index1"), index) - - // Delete segment delete log - err = store.DeleteSegmentDL(ctx, "segment0") - assert.Nil(t, err) - err = store.DeleteSegmentDL(ctx, "segment1") - assert.Nil(t, err) - index, err = store.GetSegmentDL(ctx, "segment0") - assert.Nil(t, err) - assert.Nil(t, index) -} diff --git a/internal/storage/storage.go b/internal/storage/storage.go deleted file mode 100644 index 67e9e44e8939075caaaa8aefa17cccc12e786bbd..0000000000000000000000000000000000000000 --- a/internal/storage/storage.go +++ /dev/null @@ -1,39 +0,0 @@ -package storage - -import ( - "context" - "errors" - - S3Driver "github.com/zilliztech/milvus-distributed/internal/storage/internal/S3" - minIODriver "github.com/zilliztech/milvus-distributed/internal/storage/internal/minio" - tikvDriver "github.com/zilliztech/milvus-distributed/internal/storage/internal/tikv" - storagetype "github.com/zilliztech/milvus-distributed/internal/storage/type" -) - -func NewStore(ctx context.Context, option storagetype.Option) (storagetype.Store, error) { - var err error - var store storagetype.Store - switch option.Type { - case storagetype.TIKVDriver: - store, err = tikvDriver.NewTikvStore(ctx, option) - if err != nil { - panic(err.Error()) - } - return store, nil - case storagetype.MinIODriver: - store, err = minIODriver.NewMinioDriver(ctx, option) - if err != nil { - //panic(err.Error()) - return nil, err - } - return store, nil - case storagetype.S3DRIVER: - store, err = S3Driver.NewS3Driver(ctx, option) - if err != nil { - //panic(err.Error()) - return nil, err - } - return store, nil - } - return nil, errors.New("unsupported driver") -} diff --git a/internal/storage/type/storagetype.go b/internal/storage/type/storagetype.go deleted file mode 100644 index 9549a106e573e96589ec9f415379a0a5b7144c2b..0000000000000000000000000000000000000000 --- a/internal/storage/type/storagetype.go +++ /dev/null @@ -1,79 +0,0 @@ -package storagetype - -import ( - "context" - - "github.com/zilliztech/milvus-distributed/internal/util/typeutil" -) - -type Key = []byte -type Value = []byte -type Timestamp = typeutil.Timestamp -type DriverType = string -type SegmentIndex = []byte -type SegmentDL = []byte - -type Option struct { - Type DriverType - TikvAddress string - BucketName string -} - -const ( - MinIODriver DriverType = "MinIO" - TIKVDriver DriverType = "TIKV" - S3DRIVER DriverType = "S3" -) - -/* -type Store interface { - Get(ctx context.Context, key Key, timestamp Timestamp) (Value, error) - BatchGet(ctx context.Context, keys [] Key, timestamp Timestamp) ([]Value, error) - Set(ctx context.Context, key Key, v Value, timestamp Timestamp) error - BatchSet(ctx context.Context, keys []Key, v []Value, timestamp Timestamp) error - Delete(ctx context.Context, key Key, timestamp Timestamp) error - BatchDelete(ctx context.Context, keys []Key, timestamp Timestamp) error - Close() error -} -*/ - -type storeEngine interface { - Put(ctx context.Context, key Key, value Value) error - Get(ctx context.Context, key Key) (Value, error) - GetByPrefix(ctx context.Context, prefix Key, keyOnly bool) ([]Key, []Value, error) - Scan(ctx context.Context, startKey Key, endKey Key, limit int, keyOnly bool) ([]Key, []Value, error) - Delete(ctx context.Context, key Key) error - DeleteByPrefix(ctx context.Context, prefix Key) error - DeleteRange(ctx context.Context, keyStart Key, keyEnd Key) error -} - -type Store interface { - //put(ctx context.Context, key Key, value Value, timestamp Timestamp, suffix string) error - //scanLE(ctx context.Context, key Key, timestamp Timestamp, keyOnly bool) ([]Timestamp, []Key, []Value, error) - //scanGE(ctx context.Context, key Key, timestamp Timestamp, keyOnly bool) ([]Timestamp, []Key, []Value, error) - //deleteLE(ctx context.Context, key Key, timestamp Timestamp) error - //deleteGE(ctx context.Context, key Key, timestamp Timestamp) error - //deleteRange(ctx context.Context, key Key, start Timestamp, end Timestamp) error - - GetRow(ctx context.Context, key Key, timestamp Timestamp) (Value, error) - GetRows(ctx context.Context, keys []Key, timestamps []Timestamp) ([]Value, error) - - PutRow(ctx context.Context, key Key, value Value, segment string, timestamp Timestamp) error - PutRows(ctx context.Context, keys []Key, values []Value, segments []string, timestamps []Timestamp) error - - GetSegments(ctx context.Context, key Key, timestamp Timestamp) ([]string, error) - - DeleteRow(ctx context.Context, key Key, timestamp Timestamp) error - DeleteRows(ctx context.Context, keys []Key, timestamps []Timestamp) error - - PutLog(ctx context.Context, key Key, value Value, timestamp Timestamp, channel int) error - GetLog(ctx context.Context, start Timestamp, end Timestamp, channels []int) ([]Value, error) - - GetSegmentIndex(ctx context.Context, segment string) (SegmentIndex, error) - PutSegmentIndex(ctx context.Context, segment string, index SegmentIndex) error - DeleteSegmentIndex(ctx context.Context, segment string) error - - GetSegmentDL(ctx context.Context, segment string) (SegmentDL, error) - PutSegmentDL(ctx context.Context, segment string, log SegmentDL) error - DeleteSegmentDL(ctx context.Context, segment string) error -} diff --git a/internal/util/flowgraph/input_node.go b/internal/util/flowgraph/input_node.go index 7c4271b23be5e31373966c3b64acfc395285916f..b7891040e8208894b2c66f07582ddc26adc3d59e 100644 --- a/internal/util/flowgraph/input_node.go +++ b/internal/util/flowgraph/input_node.go @@ -1,8 +1,12 @@ package flowgraph import ( + "fmt" "log" + "github.com/opentracing/opentracing-go" + "github.com/zilliztech/milvus-distributed/internal/proto/internalpb" + "github.com/zilliztech/milvus-distributed/internal/msgstream" ) @@ -25,11 +29,32 @@ func (inNode *InputNode) InStream() *msgstream.MsgStream { } // empty input and return one *Msg -func (inNode *InputNode) Operate(in []*Msg) []*Msg { +func (inNode *InputNode) Operate([]*Msg) []*Msg { //fmt.Println("Do InputNode operation") - msgPack := (*inNode.inStream).Consume() + var childs []opentracing.Span + tracer := opentracing.GlobalTracer() + if tracer != nil && msgPack != nil { + for _, msg := range msgPack.Msgs { + if msg.Type() == internalpb.MsgType_kInsert || msg.Type() == internalpb.MsgType_kSearch { + var child opentracing.Span + ctx := msg.GetContext() + if parent := opentracing.SpanFromContext(ctx); parent != nil { + child = tracer.StartSpan(fmt.Sprintf("through msg input node, start time = %d", msg.BeginTs()), + opentracing.FollowsFrom(parent.Context())) + } else { + child = tracer.StartSpan(fmt.Sprintf("through msg input node, start time = %d", msg.BeginTs())) + } + child.SetTag("hash keys", msg.HashKeys()) + child.SetTag("start time", msg.BeginTs()) + child.SetTag("end time", msg.EndTs()) + msg.SetContext(opentracing.ContextWithSpan(ctx, child)) + childs = append(childs, child) + } + } + } + // TODO: add status if msgPack == nil { log.Println("null msg pack") @@ -42,6 +67,10 @@ func (inNode *InputNode) Operate(in []*Msg) []*Msg { timestampMax: msgPack.EndTs, } + for _, child := range childs { + child.Finish() + } + return []*Msg{&msgStreamMsg} } diff --git a/internal/util/flowgraph/message.go b/internal/util/flowgraph/message.go index f02d2604cbee807f791a9877cf969e3047fdfb61..e5c01d7d4ef92872f38abc45713cd68f06f5a6ee 100644 --- a/internal/util/flowgraph/message.go +++ b/internal/util/flowgraph/message.go @@ -4,6 +4,7 @@ import "github.com/zilliztech/milvus-distributed/internal/msgstream" type Msg interface { TimeTick() Timestamp + DownStreamNodeIdx() int } type MsgStreamMsg struct { diff --git a/internal/writenode/client/client.go b/internal/writenode/client/client.go index a966ff8862e21d5c0d701d84eb5507be1881ef76..6c922e2e1ca66740060e768c1dfcfadc8729e9d9 100644 --- a/internal/writenode/client/client.go +++ b/internal/writenode/client/client.go @@ -48,7 +48,7 @@ type SegmentDescription struct { CloseTime Timestamp } -func (c *Client) FlushSegment(segmentID UniqueID) error { +func (c *Client) FlushSegment(segmentID UniqueID, collectionID UniqueID, partitionTag string, timestamp Timestamp) error { baseMsg := msgstream.BaseMsg{ BeginTimestamp: 0, EndTimestamp: 0, @@ -56,9 +56,11 @@ func (c *Client) FlushSegment(segmentID UniqueID) error { } flushMsg := internalPb.FlushMsg{ - MsgType: internalPb.MsgType_kFlush, - SegmentID: segmentID, - Timestamp: Timestamp(0), + MsgType: internalPb.MsgType_kFlush, + SegmentID: segmentID, + CollectionID: collectionID, + PartitionTag: partitionTag, + Timestamp: timestamp, } fMsg := &msgstream.FlushMsg{ diff --git a/internal/writenode/collection.go b/internal/writenode/collection.go new file mode 100644 index 0000000000000000000000000000000000000000..21d411d110b6648266471781bb3218e472245378 --- /dev/null +++ b/internal/writenode/collection.go @@ -0,0 +1,37 @@ +package writenode + +import ( + "log" + + "github.com/golang/protobuf/proto" + "github.com/zilliztech/milvus-distributed/internal/proto/schemapb" +) + +type Collection struct { + schema *schemapb.CollectionSchema + id UniqueID +} + +func (c *Collection) Name() string { + return c.schema.Name +} + +func (c *Collection) ID() UniqueID { + return c.id +} + +func newCollection(collectionID UniqueID, schemaStr string) *Collection { + + var schema schemapb.CollectionSchema + err := proto.UnmarshalText(schemaStr, &schema) + if err != nil { + log.Println(err) + return nil + } + + var newCollection = &Collection{ + schema: &schema, + id: collectionID, + } + return newCollection +} diff --git a/internal/writenode/collection_replica.go b/internal/writenode/collection_replica.go new file mode 100644 index 0000000000000000000000000000000000000000..2310802c8f3419bb1f1a58c8f23040f506f2e852 --- /dev/null +++ b/internal/writenode/collection_replica.go @@ -0,0 +1,94 @@ +package writenode + +import ( + "strconv" + "sync" + + "github.com/zilliztech/milvus-distributed/internal/errors" +) + +type collectionReplica interface { + + // collection + getCollectionNum() int + addCollection(collectionID UniqueID, schemaBlob string) error + removeCollection(collectionID UniqueID) error + getCollectionByID(collectionID UniqueID) (*Collection, error) + getCollectionByName(collectionName string) (*Collection, error) + hasCollection(collectionID UniqueID) bool +} + +type collectionReplicaImpl struct { + mu sync.RWMutex + collections []*Collection +} + +//----------------------------------------------------------------------------------------------------- collection +func (colReplica *collectionReplicaImpl) getCollectionNum() int { + colReplica.mu.RLock() + defer colReplica.mu.RUnlock() + + return len(colReplica.collections) +} + +func (colReplica *collectionReplicaImpl) addCollection(collectionID UniqueID, schemaBlob string) error { + colReplica.mu.Lock() + defer colReplica.mu.Unlock() + + var newCollection = newCollection(collectionID, schemaBlob) + colReplica.collections = append(colReplica.collections, newCollection) + + return nil +} + +func (colReplica *collectionReplicaImpl) removeCollection(collectionID UniqueID) error { + colReplica.mu.Lock() + defer colReplica.mu.Unlock() + + tmpCollections := make([]*Collection, 0) + for _, col := range colReplica.collections { + if col.ID() != collectionID { + tmpCollections = append(tmpCollections, col) + } + } + colReplica.collections = tmpCollections + return nil +} + +func (colReplica *collectionReplicaImpl) getCollectionByID(collectionID UniqueID) (*Collection, error) { + colReplica.mu.RLock() + defer colReplica.mu.RUnlock() + + for _, collection := range colReplica.collections { + if collection.ID() == collectionID { + return collection, nil + } + } + + return nil, errors.New("cannot find collection, id = " + strconv.FormatInt(collectionID, 10)) +} + +func (colReplica *collectionReplicaImpl) getCollectionByName(collectionName string) (*Collection, error) { + colReplica.mu.RLock() + defer colReplica.mu.RUnlock() + + for _, collection := range colReplica.collections { + if collection.Name() == collectionName { + return collection, nil + } + } + + return nil, errors.New("Cannot found collection: " + collectionName) +} + +func (colReplica *collectionReplicaImpl) hasCollection(collectionID UniqueID) bool { + colReplica.mu.RLock() + defer colReplica.mu.RUnlock() + + for _, col := range colReplica.collections { + if col.ID() == collectionID { + return true + } + } + return false +} diff --git a/internal/writenode/collection_replica_test.go b/internal/writenode/collection_replica_test.go new file mode 100644 index 0000000000000000000000000000000000000000..be17d2b8808a6de428289a38ec0c7fee159dc316 --- /dev/null +++ b/internal/writenode/collection_replica_test.go @@ -0,0 +1,153 @@ +package writenode + +import ( + "testing" + + "github.com/golang/protobuf/proto" + "github.com/zilliztech/milvus-distributed/internal/proto/commonpb" + "github.com/zilliztech/milvus-distributed/internal/proto/etcdpb" + "github.com/zilliztech/milvus-distributed/internal/proto/schemapb" + + "github.com/stretchr/testify/assert" +) + +func newReplica() collectionReplica { + collections := make([]*Collection, 0) + + var replica collectionReplica = &collectionReplicaImpl{ + collections: collections, + } + return replica +} + +func genTestCollectionMeta(collectionName string, collectionID UniqueID) *etcdpb.CollectionMeta { + fieldVec := schemapb.FieldSchema{ + FieldID: UniqueID(100), + Name: "vec", + IsPrimaryKey: false, + DataType: schemapb.DataType_VECTOR_FLOAT, + TypeParams: []*commonpb.KeyValuePair{ + { + Key: "dim", + Value: "16", + }, + }, + IndexParams: []*commonpb.KeyValuePair{ + { + Key: "metric_type", + Value: "L2", + }, + }, + } + + fieldInt := schemapb.FieldSchema{ + FieldID: UniqueID(101), + Name: "age", + IsPrimaryKey: false, + DataType: schemapb.DataType_INT32, + } + + schema := schemapb.CollectionSchema{ + Name: collectionName, + AutoID: true, + Fields: []*schemapb.FieldSchema{ + &fieldVec, &fieldInt, + }, + } + + collectionMeta := etcdpb.CollectionMeta{ + ID: collectionID, + Schema: &schema, + CreateTime: Timestamp(0), + SegmentIDs: []UniqueID{0}, + PartitionTags: []string{"default"}, + } + + return &collectionMeta +} + +func initTestMeta(t *testing.T, replica collectionReplica, collectionName string, collectionID UniqueID, segmentID UniqueID) { + collectionMeta := genTestCollectionMeta(collectionName, collectionID) + + schemaBlob := proto.MarshalTextString(collectionMeta.Schema) + assert.NotEqual(t, "", schemaBlob) + + var err = replica.addCollection(collectionMeta.ID, schemaBlob) + assert.NoError(t, err) + + collection, err := replica.getCollectionByName(collectionName) + assert.NoError(t, err) + assert.Equal(t, collection.Name(), collectionName) + assert.Equal(t, collection.ID(), collectionID) + assert.Equal(t, replica.getCollectionNum(), 1) + +} + +//----------------------------------------------------------------------------------------------------- collection +func TestCollectionReplica_getCollectionNum(t *testing.T) { + replica := newReplica() + initTestMeta(t, replica, "collection0", 0, 0) + assert.Equal(t, replica.getCollectionNum(), 1) +} + +func TestCollectionReplica_addCollection(t *testing.T) { + replica := newReplica() + initTestMeta(t, replica, "collection0", 0, 0) +} + +func TestCollectionReplica_removeCollection(t *testing.T) { + replica := newReplica() + initTestMeta(t, replica, "collection0", 0, 0) + assert.Equal(t, replica.getCollectionNum(), 1) + + err := replica.removeCollection(0) + assert.NoError(t, err) + assert.Equal(t, replica.getCollectionNum(), 0) +} + +func TestCollectionReplica_getCollectionByID(t *testing.T) { + replica := newReplica() + collectionName := "collection0" + collectionID := UniqueID(0) + initTestMeta(t, replica, collectionName, collectionID, 0) + targetCollection, err := replica.getCollectionByID(collectionID) + assert.NoError(t, err) + assert.NotNil(t, targetCollection) + assert.Equal(t, targetCollection.Name(), collectionName) + assert.Equal(t, targetCollection.ID(), collectionID) +} + +func TestCollectionReplica_getCollectionByName(t *testing.T) { + replica := newReplica() + collectionName := "collection0" + collectionID := UniqueID(0) + initTestMeta(t, replica, collectionName, collectionID, 0) + + targetCollection, err := replica.getCollectionByName(collectionName) + assert.NoError(t, err) + assert.NotNil(t, targetCollection) + assert.Equal(t, targetCollection.Name(), collectionName) + assert.Equal(t, targetCollection.ID(), collectionID) + +} + +func TestCollectionReplica_hasCollection(t *testing.T) { + replica := newReplica() + collectionName := "collection0" + collectionID := UniqueID(0) + initTestMeta(t, replica, collectionName, collectionID, 0) + + hasCollection := replica.hasCollection(collectionID) + assert.Equal(t, hasCollection, true) + hasCollection = replica.hasCollection(UniqueID(1)) + assert.Equal(t, hasCollection, false) + +} + +func TestCollectionReplica_freeAll(t *testing.T) { + replica := newReplica() + collectionName := "collection0" + collectionID := UniqueID(0) + initTestMeta(t, replica, collectionName, collectionID, 0) + +} diff --git a/internal/writenode/collection_test.go b/internal/writenode/collection_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6df2ad5d4910276c79d756ad40364ab131721bee --- /dev/null +++ b/internal/writenode/collection_test.go @@ -0,0 +1,34 @@ +package writenode + +import ( + "testing" + + "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/assert" +) + +func TestCollection_newCollection(t *testing.T) { + collectionName := "collection0" + collectionID := UniqueID(0) + collectionMeta := genTestCollectionMeta(collectionName, collectionID) + + schemaBlob := proto.MarshalTextString(collectionMeta.Schema) + assert.NotEqual(t, "", schemaBlob) + + collection := newCollection(collectionMeta.ID, schemaBlob) + assert.Equal(t, collection.Name(), collectionName) + assert.Equal(t, collection.ID(), collectionID) +} + +func TestCollection_deleteCollection(t *testing.T) { + collectionName := "collection0" + collectionID := UniqueID(0) + collectionMeta := genTestCollectionMeta(collectionName, collectionID) + + schemaBlob := proto.MarshalTextString(collectionMeta.Schema) + assert.NotEqual(t, "", schemaBlob) + + collection := newCollection(collectionMeta.ID, schemaBlob) + assert.Equal(t, collection.Name(), collectionName) + assert.Equal(t, collection.ID(), collectionID) +} diff --git a/internal/writenode/data_sync_service.go b/internal/writenode/data_sync_service.go index da91d06a7640a0175963bc05443257cb591cc662..efcd62b0365dce4b6ca4e37588268471340c697b 100644 --- a/internal/writenode/data_sync_service.go +++ b/internal/writenode/data_sync_service.go @@ -12,16 +12,18 @@ type dataSyncService struct { fg *flowgraph.TimeTickedFlowGraph ddChan chan *ddlFlushSyncMsg insertChan chan *insertFlushSyncMsg + replica collectionReplica } func newDataSyncService(ctx context.Context, - ddChan chan *ddlFlushSyncMsg, insertChan chan *insertFlushSyncMsg) *dataSyncService { + ddChan chan *ddlFlushSyncMsg, insertChan chan *insertFlushSyncMsg, replica collectionReplica) *dataSyncService { return &dataSyncService{ ctx: ctx, fg: nil, ddChan: ddChan, insertChan: insertChan, + replica: replica, } } @@ -46,8 +48,8 @@ func (dsService *dataSyncService) initNodes() { var filterDmNode Node = newFilteredDmNode() - var ddNode Node = newDDNode(dsService.ctx, dsService.ddChan) - var insertBufferNode Node = newInsertBufferNode(dsService.ctx, dsService.insertChan) + var ddNode Node = newDDNode(dsService.ctx, dsService.ddChan, dsService.replica) + var insertBufferNode Node = newInsertBufferNode(dsService.ctx, dsService.insertChan, dsService.replica) dsService.fg.AddNode(&dmStreamNode) dsService.fg.AddNode(&ddStreamNode) diff --git a/internal/writenode/data_sync_service_test.go b/internal/writenode/data_sync_service_test.go index a37425ec3e283af2627283e5a3c877ec8420ab6a..df82cec4d93fa212db9432a624a2d2e2cefd134b 100644 --- a/internal/writenode/data_sync_service_test.go +++ b/internal/writenode/data_sync_service_test.go @@ -197,7 +197,8 @@ func TestDataSyncService_Start(t *testing.T) { assert.NoError(t, err) // dataSync - node.dataSyncService = newDataSyncService(node.ctx, nil, nil) + replica := newReplica() + node.dataSyncService = newDataSyncService(node.ctx, nil, nil, replica) go node.dataSyncService.start() node.Close() diff --git a/internal/writenode/flow_graph_dd_node.go b/internal/writenode/flow_graph_dd_node.go index 7dd8e1fd433fda223cab267bbae39bb07ce2122c..9d3b4db158ed89f920a865ddd966ebe382cfc067 100644 --- a/internal/writenode/flow_graph_dd_node.go +++ b/internal/writenode/flow_graph_dd_node.go @@ -9,9 +9,6 @@ import ( "strconv" "github.com/golang/protobuf/proto" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/zilliztech/milvus-distributed/internal/allocator" "github.com/zilliztech/milvus-distributed/internal/kv" miniokv "github.com/zilliztech/milvus-distributed/internal/kv/minio" @@ -30,6 +27,7 @@ type ddNode struct { idAllocator *allocator.IDAllocator kv kv.Base + replica collectionReplica } type ddData struct { @@ -228,6 +226,15 @@ func (ddNode *ddNode) createCollection(msg *msgstream.CreateCollectionMsg) { log.Println(err) return } + + schemaStr := proto.MarshalTextString(&schema) + // add collection + err = ddNode.replica.addCollection(collectionID, schemaStr) + if err != nil { + log.Println(err) + return + } + collectionName := schema.Name ddNode.ddMsg.collectionRecords[collectionName] = append(ddNode.ddMsg.collectionRecords[collectionName], metaOperateRecord{ @@ -252,6 +259,11 @@ func (ddNode *ddNode) createCollection(msg *msgstream.CreateCollectionMsg) { func (ddNode *ddNode) dropCollection(msg *msgstream.DropCollectionMsg) { collectionID := msg.CollectionID + err := ddNode.replica.removeCollection(collectionID) + if err != nil { + log.Println(err) + } + // remove collection if _, ok := ddNode.ddRecords.collectionRecords[collectionID]; !ok { err := errors.New("cannot found collection " + strconv.FormatInt(collectionID, 10)) @@ -347,7 +359,7 @@ func (ddNode *ddNode) dropPartition(msg *msgstream.DropPartitionMsg) { ddNode.ddBuffer.ddData[collectionID].eventTypes = append(ddNode.ddBuffer.ddData[collectionID].eventTypes, storage.DropPartitionEventType) } -func newDDNode(ctx context.Context, outCh chan *ddlFlushSyncMsg) *ddNode { +func newDDNode(ctx context.Context, outCh chan *ddlFlushSyncMsg, replica collectionReplica) *ddNode { maxQueueLength := Params.FlowGraphMaxQueueLength maxParallelism := Params.FlowGraphMaxParallelism @@ -360,19 +372,16 @@ func newDDNode(ctx context.Context, outCh chan *ddlFlushSyncMsg) *ddNode { partitionRecords: make(map[UniqueID]interface{}), } - minIOEndPoint := Params.MinioAddress - minIOAccessKeyID := Params.MinioAccessKeyID - minIOSecretAccessKey := Params.MinioSecretAccessKey - minIOUseSSL := Params.MinioUseSSL - minIOClient, err := minio.New(minIOEndPoint, &minio.Options{ - Creds: credentials.NewStaticV4(minIOAccessKeyID, minIOSecretAccessKey, ""), - Secure: minIOUseSSL, - }) - if err != nil { - panic(err) - } bucketName := Params.MinioBucketName - minioKV, err := miniokv.NewMinIOKV(ctx, minIOClient, bucketName) + option := &miniokv.Option{ + Address: Params.MinioAddress, + AccessKeyID: Params.MinioAccessKeyID, + SecretAccessKeyID: Params.MinioSecretAccessKey, + UseSSL: Params.MinioUseSSL, + BucketName: bucketName, + CreateBucket: true, + } + minioKV, err := miniokv.NewMinIOKV(ctx, option) if err != nil { panic(err) } @@ -397,5 +406,6 @@ func newDDNode(ctx context.Context, outCh chan *ddlFlushSyncMsg) *ddNode { idAllocator: idAllocator, kv: minioKV, + replica: replica, } } diff --git a/internal/writenode/flow_graph_dd_node_test.go b/internal/writenode/flow_graph_dd_node_test.go index 25de3697eae6eea564feabea3a41469417029de7..9b5d71ffb90af4e2fbb02f0b31e888d1eea4f4de 100644 --- a/internal/writenode/flow_graph_dd_node_test.go +++ b/internal/writenode/flow_graph_dd_node_test.go @@ -45,8 +45,8 @@ func TestFlowGraphDDNode_Operate(t *testing.T) { go fService.start() Params.FlushDdBufSize = 4 - - ddNode := newDDNode(ctx, ddChan) + replica := newReplica() + ddNode := newDDNode(ctx, ddChan, replica) colID := UniqueID(0) colName := "col-test-0" diff --git a/internal/writenode/flow_graph_filter_dm_node.go b/internal/writenode/flow_graph_filter_dm_node.go index 98e9ec0bc4cc5fb596c93a2725ef2bd521da911e..0bca67ebcb4ec4723454c083331822f617275990 100644 --- a/internal/writenode/flow_graph_filter_dm_node.go +++ b/internal/writenode/flow_graph_filter_dm_node.go @@ -1,8 +1,11 @@ package writenode import ( + "context" "log" + "github.com/opentracing/opentracing-go" + "github.com/zilliztech/milvus-distributed/internal/msgstream" "github.com/zilliztech/milvus-distributed/internal/proto/commonpb" internalPb "github.com/zilliztech/milvus-distributed/internal/proto/internalpb" @@ -31,11 +34,34 @@ func (fdmNode *filterDmNode) Operate(in []*Msg) []*Msg { // TODO: add error handling } + var childs []opentracing.Span + tracer := opentracing.GlobalTracer() + if tracer != nil { + for _, msg := range msgStreamMsg.TsMessages() { + if msg.Type() == internalPb.MsgType_kInsert { + var child opentracing.Span + ctx := msg.GetContext() + if parent := opentracing.SpanFromContext(ctx); parent != nil { + child = tracer.StartSpan("pass filter node", + opentracing.FollowsFrom(parent.Context())) + } else { + child = tracer.StartSpan("pass filter node") + } + child.SetTag("hash keys", msg.HashKeys()) + child.SetTag("start time", msg.BeginTs()) + child.SetTag("end time", msg.EndTs()) + msg.SetContext(opentracing.ContextWithSpan(ctx, child)) + childs = append(childs, child) + } + } + } + ddMsg, ok := (*in[1]).(*ddMsg) if !ok { log.Println("type assertion failed for ddMsg") // TODO: add error handling } + fdmNode.ddMsg = ddMsg var iMsg = insertMsg{ @@ -56,11 +82,20 @@ func (fdmNode *filterDmNode) Operate(in []*Msg) []*Msg { } } - for _, msg := range msgStreamMsg.TsMessages() { + for key, msg := range msgStreamMsg.TsMessages() { switch msg.Type() { case internalPb.MsgType_kInsert: + var ctx2 context.Context + if childs != nil { + if childs[key] != nil { + ctx2 = opentracing.ContextWithSpan(msg.GetContext(), childs[key]) + } else { + ctx2 = context.Background() + } + } resMsg := fdmNode.filterInvalidInsertMessage(msg.(*msgstream.InsertMsg)) if resMsg != nil { + resMsg.SetContext(ctx2) iMsg.insertMessages = append(iMsg.insertMessages, resMsg) } // case internalPb.MsgType_kDelete: @@ -69,8 +104,11 @@ func (fdmNode *filterDmNode) Operate(in []*Msg) []*Msg { log.Println("Non supporting message type:", msg.Type()) } } - var res Msg = &iMsg + + for _, child := range childs { + child.Finish() + } return []*Msg{&res} } diff --git a/internal/writenode/flow_graph_insert_buffer_node.go b/internal/writenode/flow_graph_insert_buffer_node.go index 0c001ee55d802fc3de1316de49679ce99a986e76..189a25bc60a077fd6acc88adb0f885f96ecbba82 100644 --- a/internal/writenode/flow_graph_insert_buffer_node.go +++ b/internal/writenode/flow_graph_insert_buffer_node.go @@ -4,18 +4,17 @@ import ( "bytes" "context" "encoding/binary" + "fmt" "log" "path" "strconv" - "time" "unsafe" - "github.com/golang/protobuf/proto" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/opentracing/opentracing-go" + oplog "github.com/opentracing/opentracing-go/log" + "github.com/zilliztech/milvus-distributed/internal/allocator" "github.com/zilliztech/milvus-distributed/internal/kv" - etcdkv "github.com/zilliztech/milvus-distributed/internal/kv/etcd" miniokv "github.com/zilliztech/milvus-distributed/internal/kv/minio" "github.com/zilliztech/milvus-distributed/internal/msgstream" "github.com/zilliztech/milvus-distributed/internal/proto/etcdpb" @@ -23,7 +22,6 @@ import ( "github.com/zilliztech/milvus-distributed/internal/proto/schemapb" "github.com/zilliztech/milvus-distributed/internal/storage" "github.com/zilliztech/milvus-distributed/internal/util/typeutil" - "go.etcd.io/etcd/clientv3" ) const ( @@ -37,13 +35,13 @@ type ( insertBufferNode struct { BaseNode - kvClient *etcdkv.EtcdKV insertBuffer *insertBuffer minIOKV kv.Base minioPrifex string idAllocator *allocator.IDAllocator outCh chan *insertFlushSyncMsg pulsarWriteNodeTimeTickStream *msgstream.PulsarMsgStream + replica collectionReplica } insertBuffer struct { @@ -102,11 +100,23 @@ func (ibNode *insertBufferNode) Operate(in []*Msg) []*Msg { // iMsg is insertMsg // 1. iMsg -> buffer for _, msg := range iMsg.insertMessages { + ctx := msg.GetContext() + var span opentracing.Span + if ctx != nil { + span, _ = opentracing.StartSpanFromContext(ctx, fmt.Sprintf("insert buffer node, start time = %d", msg.BeginTs())) + } else { + span = opentracing.StartSpan(fmt.Sprintf("insert buffer node, start time = %d", msg.BeginTs())) + } + span.SetTag("hash keys", msg.HashKeys()) + span.SetTag("start time", msg.BeginTs()) + span.SetTag("end time", msg.EndTs()) if len(msg.RowIDs) != len(msg.Timestamps) || len(msg.RowIDs) != len(msg.RowData) { log.Println("Error: misaligned messages detected") continue } currentSegID := msg.GetSegmentID() + collectionName := msg.GetCollectionName() + span.LogFields(oplog.Int("segment id", int(currentSegID))) idata, ok := ibNode.insertBuffer.insertData[currentSegID] if !ok { @@ -115,17 +125,35 @@ func (ibNode *insertBufferNode) Operate(in []*Msg) []*Msg { } } + // Timestamps + _, ok = idata.Data[1].(*storage.Int64FieldData) + if !ok { + idata.Data[1] = &storage.Int64FieldData{ + Data: []int64{}, + NumRows: 0, + } + } + tsData := idata.Data[1].(*storage.Int64FieldData) + for _, ts := range msg.Timestamps { + tsData.Data = append(tsData.Data, int64(ts)) + } + tsData.NumRows += len(msg.Timestamps) + span.LogFields(oplog.Int("tsData numRows", tsData.NumRows)) + // 1.1 Get CollectionMeta from etcd - segMeta, collMeta, err := ibNode.getMeta(currentSegID) + collection, err := ibNode.replica.getCollectionByName(collectionName) + //collSchema, err := ibNode.getCollectionSchemaByName(collectionName) if err != nil { // GOOSE TODO add error handler log.Println("Get meta wrong:", err) continue } + collectionID := collection.ID() + collSchema := collection.schema // 1.2 Get Fields var pos int = 0 // Record position of blob - for _, field := range collMeta.Schema.Fields { + for _, field := range collSchema.Fields { switch field.DataType { case schemapb.DataType_VECTOR_FLOAT: var dim int @@ -360,9 +388,11 @@ func (ibNode *insertBufferNode) Operate(in []*Msg) []*Msg { // 1.3 store in buffer ibNode.insertBuffer.insertData[currentSegID] = idata + span.LogFields(oplog.String("store in buffer", "store in buffer")) // 1.4 if full // 1.4.1 generate binlogs + span.LogFields(oplog.String("generate binlogs", "generate binlogs")) if ibNode.insertBuffer.full(currentSegID) { log.Printf(". Insert Buffer full, auto flushing (%v) rows of data...", ibNode.insertBuffer.size(currentSegID)) // partitionTag -> partitionID @@ -372,7 +402,10 @@ func (ibNode *insertBufferNode) Operate(in []*Msg) []*Msg { log.Println("partitionTag to partitionID wrong") // TODO GOOSE add error handler } - + collMeta := &etcdpb.CollectionMeta{ + Schema: collSchema, + ID: collectionID, + } inCodec := storage.NewInsertCodec(collMeta) // buffer data to binlogs @@ -388,7 +421,7 @@ func (ibNode *insertBufferNode) Operate(in []*Msg) []*Msg { log.Println(".. Clearing buffer") // 1.5.2 binLogs -> minIO/S3 - collIDStr := strconv.FormatInt(segMeta.GetCollectionID(), 10) + collIDStr := strconv.FormatInt(collectionID, 10) partitionIDStr := strconv.FormatInt(partitionID, 10) segIDStr := strconv.FormatInt(currentSegID, 10) keyPrefix := path.Join(ibNode.minioPrifex, collIDStr, partitionIDStr, segIDStr) @@ -428,6 +461,7 @@ func (ibNode *insertBufferNode) Operate(in []*Msg) []*Msg { ibNode.outCh <- inBinlogMsg } } + span.Finish() } if len(iMsg.insertMessages) > 0 { @@ -447,20 +481,24 @@ func (ibNode *insertBufferNode) Operate(in []*Msg) []*Msg { for _, msg := range iMsg.flushMessages { currentSegID := msg.GetSegmentID() flushTs := msg.GetTimestamp() - + partitionTag := msg.GetPartitionTag() + collectionID := msg.GetCollectionID() log.Printf(". Receiving flush message segID(%v)...", currentSegID) if ibNode.insertBuffer.size(currentSegID) > 0 { log.Println(".. Buffer not empty, flushing ...") - segMeta, collMeta, err := ibNode.getMeta(currentSegID) + collSchema, err := ibNode.getCollectionSchemaByID(collectionID) if err != nil { // GOOSE TODO add error handler log.Println("Get meta wrong: ", err) } + collMeta := &etcdpb.CollectionMeta{ + Schema: collSchema, + ID: collectionID, + } inCodec := storage.NewInsertCodec(collMeta) // partitionTag -> partitionID - partitionTag := segMeta.GetPartitionTag() partitionID, err := typeutil.Hash32String(partitionTag) if err != nil { // GOOSE TODO add error handler @@ -478,7 +516,7 @@ func (ibNode *insertBufferNode) Operate(in []*Msg) []*Msg { delete(ibNode.insertBuffer.insertData, currentSegID) // binLogs -> minIO/S3 - collIDStr := strconv.FormatInt(segMeta.GetCollectionID(), 10) + collIDStr := strconv.FormatInt(collectionID, 10) partitionIDStr := strconv.FormatInt(partitionID, 10) segIDStr := strconv.FormatInt(currentSegID, 10) keyPrefix := path.Join(ibNode.minioPrifex, collIDStr, partitionIDStr, segIDStr) @@ -537,31 +575,20 @@ func (ibNode *insertBufferNode) Operate(in []*Msg) []*Msg { return nil } -func (ibNode *insertBufferNode) getMeta(segID UniqueID) (*etcdpb.SegmentMeta, *etcdpb.CollectionMeta, error) { - - segMeta := &etcdpb.SegmentMeta{} - - key := path.Join(SegmentPrefix, strconv.FormatInt(segID, 10)) - value, err := ibNode.kvClient.Load(key) +func (ibNode *insertBufferNode) getCollectionSchemaByID(collectionID UniqueID) (*schemapb.CollectionSchema, error) { + ret, err := ibNode.replica.getCollectionByID(collectionID) if err != nil { - return nil, nil, err - } - err = proto.UnmarshalText(value, segMeta) - if err != nil { - return nil, nil, err + return nil, err } + return ret.schema, nil +} - collMeta := &etcdpb.CollectionMeta{} - key = path.Join(CollectionPrefix, strconv.FormatInt(segMeta.GetCollectionID(), 10)) - value, err = ibNode.kvClient.Load(key) +func (ibNode *insertBufferNode) getCollectionSchemaByName(collectionName string) (*schemapb.CollectionSchema, error) { + ret, err := ibNode.replica.getCollectionByName(collectionName) if err != nil { - return nil, nil, err + return nil, err } - err = proto.UnmarshalText(value, collMeta) - if err != nil { - return nil, nil, err - } - return segMeta, collMeta, nil + return ret.schema, nil } func (ibNode *insertBufferNode) writeHardTimeTick(ts Timestamp) error { @@ -582,7 +609,7 @@ func (ibNode *insertBufferNode) writeHardTimeTick(ts Timestamp) error { return ibNode.pulsarWriteNodeTimeTickStream.Produce(&msgPack) } -func newInsertBufferNode(ctx context.Context, outCh chan *insertFlushSyncMsg) *insertBufferNode { +func newInsertBufferNode(ctx context.Context, outCh chan *insertFlushSyncMsg, replica collectionReplica) *insertBufferNode { maxQueueLength := Params.FlowGraphMaxQueueLength maxParallelism := Params.FlowGraphMaxParallelism @@ -596,34 +623,18 @@ func newInsertBufferNode(ctx context.Context, outCh chan *insertFlushSyncMsg) *i maxSize: maxSize, } - // EtcdKV - ETCDAddr := Params.EtcdAddress - MetaRootPath := Params.MetaRootPath - log.Println("metaRootPath: ", MetaRootPath) - cli, err := clientv3.New(clientv3.Config{ - Endpoints: []string{ETCDAddr}, - DialTimeout: 5 * time.Second, - }) - if err != nil { - panic(err) - } - kvClient := etcdkv.NewEtcdKV(cli, MetaRootPath) - // MinIO - minioendPoint := Params.MinioAddress - miniioAccessKeyID := Params.MinioAccessKeyID - miniioSecretAccessKey := Params.MinioSecretAccessKey - minioUseSSL := Params.MinioUseSSL - minioBucketName := Params.MinioBucketName - - minioClient, err := minio.New(minioendPoint, &minio.Options{ - Creds: credentials.NewStaticV4(miniioAccessKeyID, miniioSecretAccessKey, ""), - Secure: minioUseSSL, - }) - if err != nil { - panic(err) + + option := &miniokv.Option{ + Address: Params.MinioAddress, + AccessKeyID: Params.MinioAccessKeyID, + SecretAccessKeyID: Params.MinioSecretAccessKey, + UseSSL: Params.MinioUseSSL, + CreateBucket: true, + BucketName: Params.MinioBucketName, } - minIOKV, err := miniokv.NewMinIOKV(ctx, minioClient, minioBucketName) + + minIOKV, err := miniokv.NewMinIOKV(ctx, option) if err != nil { panic(err) } @@ -644,12 +655,12 @@ func newInsertBufferNode(ctx context.Context, outCh chan *insertFlushSyncMsg) *i return &insertBufferNode{ BaseNode: baseNode, - kvClient: kvClient, insertBuffer: iBuffer, minIOKV: minIOKV, minioPrifex: minioPrefix, idAllocator: idAllocator, outCh: outCh, pulsarWriteNodeTimeTickStream: wTt, + replica: replica, } } diff --git a/internal/writenode/flow_graph_insert_buffer_node_test.go b/internal/writenode/flow_graph_insert_buffer_node_test.go index ef268dd3ba9c84e0895c0c2264cbb58f72d087f2..567b90c34f29345afca77a45bbed8d9c59843aa0 100644 --- a/internal/writenode/flow_graph_insert_buffer_node_test.go +++ b/internal/writenode/flow_graph_insert_buffer_node_test.go @@ -47,7 +47,8 @@ func TestFlowGraphInputBufferNode_Operate(t *testing.T) { go fService.start() // Params.FlushInsertBufSize = 2 - iBNode := newInsertBufferNode(ctx, insertChan) + replica := newReplica() + iBNode := newInsertBufferNode(ctx, insertChan, replica) newMeta() inMsg := genInsertMsg() diff --git a/internal/writenode/flow_graph_message.go b/internal/writenode/flow_graph_message.go index 5825ef570e97425cbdda95ef44cda20576be9740..147822e7c5035b8574817dc5911c845727a92078 100644 --- a/internal/writenode/flow_graph_message.go +++ b/internal/writenode/flow_graph_message.go @@ -46,14 +46,30 @@ func (ksMsg *key2SegMsg) TimeTick() Timestamp { return ksMsg.timeRange.timestampMax } +func (ksMsg *key2SegMsg) DownStreamNodeIdx() int { + return 0 +} + func (suMsg *ddMsg) TimeTick() Timestamp { return suMsg.timeRange.timestampMax } +func (suMsg *ddMsg) DownStreamNodeIdx() int { + return 0 +} + func (iMsg *insertMsg) TimeTick() Timestamp { return iMsg.timeRange.timestampMax } +func (iMsg *insertMsg) DownStreamNodeIdx() int { + return 0 +} + func (dMsg *deleteMsg) TimeTick() Timestamp { return dMsg.timeRange.timestampMax } + +func (dMsg *deleteMsg) DownStreamNodeIdx() int { + return 0 +} diff --git a/internal/writenode/meta_service.go b/internal/writenode/meta_service.go new file mode 100644 index 0000000000000000000000000000000000000000..75dfb4a4027c4f9f5e0466dc2110a059ec1f3f4d --- /dev/null +++ b/internal/writenode/meta_service.go @@ -0,0 +1,135 @@ +package writenode + +import ( + "context" + "fmt" + "log" + "path" + "reflect" + "strings" + "time" + + "github.com/golang/protobuf/proto" + "go.etcd.io/etcd/clientv3" + + etcdkv "github.com/zilliztech/milvus-distributed/internal/kv/etcd" + "github.com/zilliztech/milvus-distributed/internal/proto/etcdpb" +) + +type metaService struct { + ctx context.Context + kvBase *etcdkv.EtcdKV + replica collectionReplica +} + +func newMetaService(ctx context.Context, replica collectionReplica) *metaService { + ETCDAddr := Params.EtcdAddress + MetaRootPath := Params.MetaRootPath + + cli, _ := clientv3.New(clientv3.Config{ + Endpoints: []string{ETCDAddr}, + DialTimeout: 5 * time.Second, + }) + + return &metaService{ + ctx: ctx, + kvBase: etcdkv.NewEtcdKV(cli, MetaRootPath), + replica: replica, + } +} + +func (mService *metaService) start() { + // init from meta + err := mService.loadCollections() + if err != nil { + log.Fatal("metaService loadCollections failed") + } +} + +func GetCollectionObjID(key string) string { + ETCDRootPath := Params.MetaRootPath + + prefix := path.Join(ETCDRootPath, CollectionPrefix) + "/" + return strings.TrimPrefix(key, prefix) +} + +func isCollectionObj(key string) bool { + ETCDRootPath := Params.MetaRootPath + + prefix := path.Join(ETCDRootPath, CollectionPrefix) + "/" + prefix = strings.TrimSpace(prefix) + index := strings.Index(key, prefix) + + return index == 0 +} + +func isSegmentObj(key string) bool { + ETCDRootPath := Params.MetaRootPath + + prefix := path.Join(ETCDRootPath, SegmentPrefix) + "/" + prefix = strings.TrimSpace(prefix) + index := strings.Index(key, prefix) + + return index == 0 +} + +func printCollectionStruct(obj *etcdpb.CollectionMeta) { + v := reflect.ValueOf(obj) + v = reflect.Indirect(v) + typeOfS := v.Type() + + for i := 0; i < v.NumField(); i++ { + if typeOfS.Field(i).Name == "GrpcMarshalString" { + continue + } + fmt.Printf("Field: %s\tValue: %v\n", typeOfS.Field(i).Name, v.Field(i).Interface()) + } +} + +func (mService *metaService) processCollectionCreate(id string, value string) { + //println(fmt.Sprintf("Create Collection:$%s$", id)) + + col := mService.collectionUnmarshal(value) + if col != nil { + schema := col.Schema + schemaBlob := proto.MarshalTextString(schema) + err := mService.replica.addCollection(col.ID, schemaBlob) + if err != nil { + log.Println(err) + } + } +} + +func (mService *metaService) loadCollections() error { + keys, values, err := mService.kvBase.LoadWithPrefix(CollectionPrefix) + if err != nil { + return err + } + + for i := range keys { + objID := GetCollectionObjID(keys[i]) + mService.processCollectionCreate(objID, values[i]) + } + + return nil +} + +//----------------------------------------------------------------------- Unmarshal and Marshal +func (mService *metaService) collectionUnmarshal(value string) *etcdpb.CollectionMeta { + col := etcdpb.CollectionMeta{} + err := proto.UnmarshalText(value, &col) + if err != nil { + log.Println(err) + return nil + } + return &col +} + +func (mService *metaService) collectionMarshal(col *etcdpb.CollectionMeta) string { + value := proto.MarshalTextString(col) + if value == "" { + log.Println("marshal collection failed") + return "" + } + return value +} diff --git a/internal/writenode/meta_service_test.go b/internal/writenode/meta_service_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b1e8a9e038c8647c140d86aae45dc1810a1c1ebd --- /dev/null +++ b/internal/writenode/meta_service_test.go @@ -0,0 +1,100 @@ +package writenode + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMetaService_start(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + replica := newReplica() + + metaService := newMetaService(ctx, replica) + + metaService.start() +} + +func TestMetaService_getCollectionObjId(t *testing.T) { + var key = "/collection/collection0" + var collectionObjID1 = GetCollectionObjID(key) + + assert.Equal(t, collectionObjID1, "/collection/collection0") + + key = "fakeKey" + var collectionObjID2 = GetCollectionObjID(key) + + assert.Equal(t, collectionObjID2, "fakeKey") +} + +func TestMetaService_isCollectionObj(t *testing.T) { + var key = Params.MetaRootPath + "/collection/collection0" + var b1 = isCollectionObj(key) + + assert.Equal(t, b1, true) + + key = Params.MetaRootPath + "/segment/segment0" + var b2 = isCollectionObj(key) + + assert.Equal(t, b2, false) +} + +func TestMetaService_processCollectionCreate(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + replica := newReplica() + metaService := newMetaService(ctx, replica) + defer cancel() + id := "0" + value := `schema: < + name: "test" + fields: < + fieldID:100 + name: "vec" + data_type: VECTOR_FLOAT + type_params: < + key: "dim" + value: "16" + > + index_params: < + key: "metric_type" + value: "L2" + > + > + fields: < + fieldID:101 + name: "age" + data_type: INT32 + type_params: < + key: "dim" + value: "1" + > + > + > + segmentIDs: 0 + partition_tags: "default" + ` + + metaService.processCollectionCreate(id, value) + + collectionNum := replica.getCollectionNum() + assert.Equal(t, collectionNum, 1) + + collection, err := replica.getCollectionByName("test") + assert.NoError(t, err) + assert.Equal(t, collection.ID(), UniqueID(0)) +} + +func TestMetaService_loadCollections(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + replica := newReplica() + + metaService := newMetaService(ctx, replica) + + err2 := (*metaService).loadCollections() + assert.Nil(t, err2) +} diff --git a/internal/writenode/write_node.go b/internal/writenode/write_node.go index b747bd397be62c5dd08ca7ac42322bb7e1e8e962..319c13e4593c580af568833f3ae24143af4e8878 100644 --- a/internal/writenode/write_node.go +++ b/internal/writenode/write_node.go @@ -2,6 +2,12 @@ package writenode import ( "context" + "fmt" + "io" + + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" ) type WriteNode struct { @@ -9,15 +15,27 @@ type WriteNode struct { WriteNodeID uint64 dataSyncService *dataSyncService flushSyncService *flushSyncService + metaService *metaService + replica collectionReplica + tracer opentracing.Tracer + closer io.Closer } func NewWriteNode(ctx context.Context, writeNodeID uint64) *WriteNode { + collections := make([]*Collection, 0) + + var replica collectionReplica = &collectionReplicaImpl{ + collections: collections, + } + node := &WriteNode{ ctx: ctx, WriteNodeID: writeNodeID, dataSyncService: nil, flushSyncService: nil, + metaService: nil, + replica: replica, } return node @@ -28,6 +46,22 @@ func Init() { } func (node *WriteNode) Start() error { + cfg := &config.Configuration{ + ServiceName: "tracing", + Sampler: &config.SamplerConfig{ + Type: "const", + Param: 1, + }, + Reporter: &config.ReporterConfig{ + LogSpans: true, + }, + } + var err error + node.tracer, node.closer, err = cfg.NewTracer(config.Logger(jaeger.StdLogger)) + if err != nil { + panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err)) + } + opentracing.SetGlobalTracer(node.tracer) // TODO GOOSE Init Size?? chanSize := 100 @@ -35,10 +69,12 @@ func (node *WriteNode) Start() error { insertChan := make(chan *insertFlushSyncMsg, chanSize) node.flushSyncService = newFlushSyncService(node.ctx, ddChan, insertChan) - node.dataSyncService = newDataSyncService(node.ctx, ddChan, insertChan) + node.dataSyncService = newDataSyncService(node.ctx, ddChan, insertChan, node.replica) + node.metaService = newMetaService(node.ctx, node.replica) go node.dataSyncService.start() go node.flushSyncService.start() + node.metaService.start() return nil } diff --git a/tools/core_gen/all_generate.py b/tools/core_gen/all_generate.py index 499022d583bf44465868530a4ddee5aec991523b..a4be00cead810a06504156c058b1755086ac1899 100755 --- a/tools/core_gen/all_generate.py +++ b/tools/core_gen/all_generate.py @@ -58,6 +58,10 @@ if __name__ == "__main__": 'visitor_name': "ExecExprVisitor", "parameter_name": 'expr', }, + { + 'visitor_name': "VerifyExprVisitor", + "parameter_name": 'expr', + }, ], 'PlanNode': [ { @@ -68,7 +72,10 @@ if __name__ == "__main__": 'visitor_name': "ExecPlanNodeVisitor", "parameter_name": 'node', }, - + { + 'visitor_name': "VerifyPlanNodeVisitor", + "parameter_name": 'node', + }, ] } extract_extra_body(visitor_info, query_path) diff --git a/tools/core_gen/templates/visitor_derived.h b/tools/core_gen/templates/visitor_derived.h index cda1ea1a74c15cb90ca13fe26b879bdf1cd021cc..5fe2be775ddaebaa817d4819413898d71d4468a1 100644 --- a/tools/core_gen/templates/visitor_derived.h +++ b/tools/core_gen/templates/visitor_derived.h @@ -13,7 +13,7 @@ #include "@@base_visitor@@.h" namespace @@namespace@@ { -class @@visitor_name@@ : @@base_visitor@@ { +class @@visitor_name@@ : public @@base_visitor@@ { public: @@body@@