2021-01-18 10:09:17 +08:00
|
|
|
package querynode
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2021-01-19 14:44:03 +08:00
|
|
|
"unsafe"
|
2021-01-18 10:09:17 +08:00
|
|
|
|
|
|
|
"github.com/zilliztech/milvus-distributed/internal/kv"
|
2021-01-19 11:37:16 +08:00
|
|
|
miniokv "github.com/zilliztech/milvus-distributed/internal/kv/minio"
|
|
|
|
"github.com/zilliztech/milvus-distributed/internal/msgstream"
|
2021-01-18 10:09:17 +08:00
|
|
|
"github.com/zilliztech/milvus-distributed/internal/proto/commonpb"
|
|
|
|
"github.com/zilliztech/milvus-distributed/internal/proto/datapb"
|
|
|
|
"github.com/zilliztech/milvus-distributed/internal/proto/indexpb"
|
2021-01-19 11:37:16 +08:00
|
|
|
internalPb "github.com/zilliztech/milvus-distributed/internal/proto/internalpb2"
|
2021-01-18 10:09:17 +08:00
|
|
|
queryPb "github.com/zilliztech/milvus-distributed/internal/proto/querypb"
|
|
|
|
"github.com/zilliztech/milvus-distributed/internal/storage"
|
|
|
|
)
|
|
|
|
|
|
|
|
type segmentManager struct {
|
|
|
|
replica collectionReplica
|
|
|
|
|
2021-01-19 11:37:16 +08:00
|
|
|
loadIndexReqChan chan []msgstream.TsMsg
|
|
|
|
|
2021-01-18 10:09:17 +08:00
|
|
|
// TODO: replace by client instead of grpc client
|
|
|
|
dataClient datapb.DataServiceClient
|
|
|
|
indexBuilderClient indexpb.IndexServiceClient
|
|
|
|
|
2021-01-19 11:37:16 +08:00
|
|
|
kv kv.Base // minio kv
|
|
|
|
iCodec *storage.InsertCodec
|
|
|
|
}
|
|
|
|
|
|
|
|
func newSegmentManager(ctx context.Context, replica collectionReplica, loadIndexReqChan chan []msgstream.TsMsg) *segmentManager {
|
|
|
|
bucketName := Params.MinioBucketName
|
|
|
|
option := &miniokv.Option{
|
|
|
|
Address: Params.MinioEndPoint,
|
|
|
|
AccessKeyID: Params.MinioAccessKeyID,
|
|
|
|
SecretAccessKeyID: Params.MinioSecretAccessKey,
|
|
|
|
UseSSL: Params.MinioUseSSLStr,
|
|
|
|
BucketName: bucketName,
|
|
|
|
CreateBucket: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
minioKV, err := miniokv.NewMinIOKV(ctx, option)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &segmentManager{
|
|
|
|
replica: replica,
|
|
|
|
loadIndexReqChan: loadIndexReqChan,
|
|
|
|
|
|
|
|
// TODO: init clients
|
|
|
|
dataClient: nil,
|
|
|
|
indexBuilderClient: nil,
|
|
|
|
|
|
|
|
kv: minioKV,
|
|
|
|
iCodec: &storage.InsertCodec{},
|
|
|
|
}
|
2021-01-18 10:09:17 +08:00
|
|
|
}
|
|
|
|
|
2021-01-19 14:44:03 +08:00
|
|
|
func (s *segmentManager) loadSegment(segmentID UniqueID, partitionID UniqueID, collectionID UniqueID, fieldIDs *[]int64) error {
|
2021-01-18 10:38:41 +08:00
|
|
|
insertBinlogPathRequest := &datapb.InsertBinlogPathRequest{
|
2021-01-18 10:09:17 +08:00
|
|
|
SegmentID: segmentID,
|
|
|
|
}
|
|
|
|
|
2021-01-18 10:38:41 +08:00
|
|
|
pathResponse, err := s.dataClient.GetInsertBinlogPaths(context.TODO(), insertBinlogPathRequest)
|
2021-01-18 10:09:17 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(pathResponse.FieldIDs) != len(pathResponse.Paths) {
|
|
|
|
return errors.New("illegal InsertBinlogPathsResponse")
|
|
|
|
}
|
|
|
|
|
2021-01-19 14:44:03 +08:00
|
|
|
// create segment
|
|
|
|
err = s.replica.addSegment(segmentID, partitionID, collectionID, segTypeSealed)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-18 10:38:41 +08:00
|
|
|
containsFunc := func(s []int64, e int64) bool {
|
|
|
|
for _, a := range s {
|
|
|
|
if a == e {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, fieldID := range pathResponse.FieldIDs {
|
|
|
|
// filter out the needless fields
|
|
|
|
if !containsFunc(*fieldIDs, fieldID) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-01-18 10:09:17 +08:00
|
|
|
paths := pathResponse.Paths[i].Values
|
|
|
|
blobs := make([]*storage.Blob, 0)
|
|
|
|
for _, path := range paths {
|
|
|
|
binLog, err := s.kv.Load(path)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: return or continue?
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
blobs = append(blobs, &storage.Blob{
|
|
|
|
Key: "", // TODO: key???
|
|
|
|
Value: []byte(binLog),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
_, _, insertData, err := s.iCodec.Deserialize(blobs)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: return or continue
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(insertData.Data) != 1 {
|
|
|
|
return errors.New("we expect only one field in deserialized insert data")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, value := range insertData.Data {
|
2021-01-19 14:44:03 +08:00
|
|
|
var numRows int
|
|
|
|
var data interface{}
|
|
|
|
|
2021-01-18 10:09:17 +08:00
|
|
|
switch fieldData := value.(type) {
|
|
|
|
case storage.BoolFieldData:
|
2021-01-19 14:44:03 +08:00
|
|
|
numRows = fieldData.NumRows
|
|
|
|
data = fieldData.Data
|
2021-01-18 10:09:17 +08:00
|
|
|
case storage.Int8FieldData:
|
2021-01-19 14:44:03 +08:00
|
|
|
numRows = fieldData.NumRows
|
|
|
|
data = fieldData.Data
|
2021-01-18 10:09:17 +08:00
|
|
|
case storage.Int16FieldData:
|
2021-01-19 14:44:03 +08:00
|
|
|
numRows = fieldData.NumRows
|
|
|
|
data = fieldData.Data
|
2021-01-18 10:09:17 +08:00
|
|
|
case storage.Int32FieldData:
|
2021-01-19 14:44:03 +08:00
|
|
|
numRows = fieldData.NumRows
|
|
|
|
data = fieldData.Data
|
2021-01-18 10:09:17 +08:00
|
|
|
case storage.Int64FieldData:
|
2021-01-19 14:44:03 +08:00
|
|
|
numRows = fieldData.NumRows
|
|
|
|
data = fieldData.Data
|
2021-01-18 10:09:17 +08:00
|
|
|
case storage.FloatFieldData:
|
2021-01-19 14:44:03 +08:00
|
|
|
numRows = fieldData.NumRows
|
|
|
|
data = fieldData.Data
|
2021-01-18 10:09:17 +08:00
|
|
|
case storage.DoubleFieldData:
|
2021-01-19 14:44:03 +08:00
|
|
|
numRows = fieldData.NumRows
|
|
|
|
data = fieldData.Data
|
2021-01-18 10:38:41 +08:00
|
|
|
case storage.StringFieldData:
|
2021-01-19 14:44:03 +08:00
|
|
|
numRows = fieldData.NumRows
|
|
|
|
data = fieldData.Data
|
2021-01-18 10:38:41 +08:00
|
|
|
case storage.FloatVectorFieldData:
|
|
|
|
// segment to be loaded doesn't need vector field,
|
|
|
|
// so we ignore the type of vector field data
|
|
|
|
continue
|
|
|
|
case storage.BinaryVectorFieldData:
|
|
|
|
continue
|
2021-01-18 10:09:17 +08:00
|
|
|
default:
|
2021-01-18 10:38:41 +08:00
|
|
|
return errors.New("unexpected field data type")
|
2021-01-18 10:09:17 +08:00
|
|
|
}
|
2021-01-19 14:44:03 +08:00
|
|
|
|
|
|
|
segment, err := s.replica.getSegmentByID(segmentID)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: return or continue?
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = segment.segmentLoadFieldData(fieldID, numRows, unsafe.Pointer(&data))
|
|
|
|
if err != nil {
|
|
|
|
// TODO: return or continue?
|
|
|
|
return err
|
|
|
|
}
|
2021-01-18 10:09:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-18 10:38:41 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *segmentManager) loadIndex(segmentID UniqueID, indexID UniqueID) error {
|
|
|
|
indexFilePathRequest := &indexpb.IndexFilePathRequest{
|
|
|
|
IndexID: indexID,
|
|
|
|
}
|
|
|
|
pathResponse, err := s.indexBuilderClient.GetIndexFilePaths(context.TODO(), indexFilePathRequest)
|
|
|
|
if err != nil || pathResponse.Status.ErrorCode != commonpb.ErrorCode_SUCCESS {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// get vector field ids from schema to load index
|
2021-01-19 14:44:03 +08:00
|
|
|
vecFieldIDs, err := s.replica.getVecFieldsBySegmentID(segmentID)
|
2021-01-18 10:38:41 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-01-19 14:44:03 +08:00
|
|
|
for id, name := range vecFieldIDs {
|
|
|
|
var targetIndexParam indexParam
|
|
|
|
// TODO: get index param from master
|
2021-01-19 11:37:16 +08:00
|
|
|
// non-blocking send
|
2021-01-19 14:44:03 +08:00
|
|
|
go s.sendLoadIndex(pathResponse.IndexFilePaths, segmentID, id, name, targetIndexParam)
|
2021-01-18 10:09:17 +08:00
|
|
|
}
|
2021-01-18 10:38:41 +08:00
|
|
|
|
2021-01-18 10:09:17 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-19 11:37:16 +08:00
|
|
|
func (s *segmentManager) sendLoadIndex(indexPaths []string,
|
|
|
|
segmentID int64,
|
|
|
|
fieldID int64,
|
|
|
|
fieldName string,
|
|
|
|
indexParams map[string]string) {
|
|
|
|
var indexParamsKV []*commonpb.KeyValuePair
|
|
|
|
for key, value := range indexParams {
|
|
|
|
indexParamsKV = append(indexParamsKV, &commonpb.KeyValuePair{
|
|
|
|
Key: key,
|
|
|
|
Value: value,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
loadIndexRequest := internalPb.LoadIndex{
|
|
|
|
Base: &commonpb.MsgBase{
|
|
|
|
MsgType: commonpb.MsgType_kSearchResult,
|
|
|
|
},
|
|
|
|
SegmentID: segmentID,
|
|
|
|
FieldName: fieldName,
|
|
|
|
FieldID: fieldID,
|
|
|
|
IndexPaths: indexPaths,
|
|
|
|
IndexParams: indexParamsKV,
|
|
|
|
}
|
|
|
|
|
|
|
|
loadIndexMsg := &msgstream.LoadIndexMsg{
|
|
|
|
LoadIndex: loadIndexRequest,
|
|
|
|
}
|
|
|
|
|
|
|
|
messages := []msgstream.TsMsg{loadIndexMsg}
|
|
|
|
s.loadIndexReqChan <- messages
|
|
|
|
}
|
|
|
|
|
2021-01-18 10:09:17 +08:00
|
|
|
func (s *segmentManager) releaseSegment(in *queryPb.ReleaseSegmentRequest) error {
|
|
|
|
// TODO: implement
|
|
|
|
// TODO: release specific field, we need segCore supply relevant interface
|
|
|
|
return nil
|
|
|
|
}
|