2021-04-19 11:35:38 +08:00
|
|
|
// 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.
|
2021-01-15 17:09:41 +08:00
|
|
|
package dataservice
|
|
|
|
|
|
|
|
import (
|
2021-03-23 16:57:59 +08:00
|
|
|
"context"
|
2021-01-15 17:09:41 +08:00
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2021-03-27 11:35:42 +08:00
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
|
|
"github.com/zilliztech/milvus-distributed/internal/log"
|
2021-04-12 16:35:51 +08:00
|
|
|
"github.com/zilliztech/milvus-distributed/internal/msgstream"
|
|
|
|
"github.com/zilliztech/milvus-distributed/internal/proto/commonpb"
|
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
"github.com/zilliztech/milvus-distributed/internal/util/trace"
|
|
|
|
"github.com/zilliztech/milvus-distributed/internal/util/tsoutil"
|
2021-01-15 17:09:41 +08:00
|
|
|
"github.com/zilliztech/milvus-distributed/internal/util/typeutil"
|
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
"github.com/zilliztech/milvus-distributed/internal/proto/datapb"
|
2021-01-15 17:09:41 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type errRemainInSufficient struct {
|
|
|
|
requestRows int
|
|
|
|
}
|
|
|
|
|
2021-01-19 12:10:49 +08:00
|
|
|
func newErrRemainInSufficient(requestRows int) errRemainInSufficient {
|
|
|
|
return errRemainInSufficient{requestRows: requestRows}
|
2021-01-15 17:09:41 +08:00
|
|
|
}
|
|
|
|
|
2021-01-19 12:10:49 +08:00
|
|
|
func (err errRemainInSufficient) Error() string {
|
2021-01-15 17:09:41 +08:00
|
|
|
return "segment remaining is insufficient for" + strconv.Itoa(err.requestRows)
|
|
|
|
}
|
|
|
|
|
|
|
|
// segmentAllocator is used to allocate rows for segments and record the allocations.
|
2021-03-05 16:52:45 +08:00
|
|
|
type segmentAllocatorInterface interface {
|
2021-01-15 17:09:41 +08:00
|
|
|
// OpenSegment add the segment to allocator and set it allocatable
|
2021-03-23 16:57:59 +08:00
|
|
|
OpenSegment(ctx context.Context, segmentInfo *datapb.SegmentInfo) error
|
2021-01-15 17:09:41 +08:00
|
|
|
// AllocSegment allocate rows and record the allocation.
|
2021-03-23 16:57:59 +08:00
|
|
|
AllocSegment(ctx context.Context, collectionID UniqueID, partitionID UniqueID, channelName string, requestRows int) (UniqueID, int, Timestamp, error)
|
2021-01-15 17:09:41 +08:00
|
|
|
// GetSealedSegments get all sealed segment.
|
2021-03-23 16:57:59 +08:00
|
|
|
GetSealedSegments(ctx context.Context) ([]UniqueID, error)
|
2021-01-15 17:09:41 +08:00
|
|
|
// SealSegment set segment sealed, the segment will not be allocated anymore.
|
2021-03-23 16:57:59 +08:00
|
|
|
SealSegment(ctx context.Context, segmentID UniqueID) error
|
2021-01-15 17:09:41 +08:00
|
|
|
// DropSegment drop the segment from allocator.
|
2021-03-23 16:57:59 +08:00
|
|
|
DropSegment(ctx context.Context, segmentID UniqueID)
|
2021-01-15 17:09:41 +08:00
|
|
|
// ExpireAllocations check all allocations' expire time and remove the expired allocation.
|
2021-03-23 16:57:59 +08:00
|
|
|
ExpireAllocations(ctx context.Context, timeTick Timestamp) error
|
2021-01-22 19:43:27 +08:00
|
|
|
// SealAllSegments get all opened segment ids of collection. return success and failed segment ids
|
2021-03-23 16:57:59 +08:00
|
|
|
SealAllSegments(ctx context.Context, collectionID UniqueID)
|
2021-01-15 17:09:41 +08:00
|
|
|
// IsAllocationsExpired check all allocations of segment expired.
|
2021-03-23 16:57:59 +08:00
|
|
|
IsAllocationsExpired(ctx context.Context, segmentID UniqueID, ts Timestamp) (bool, error)
|
2021-01-15 17:09:41 +08:00
|
|
|
}
|
|
|
|
|
2021-03-05 16:52:45 +08:00
|
|
|
type segmentStatus struct {
|
|
|
|
id UniqueID
|
|
|
|
collectionID UniqueID
|
|
|
|
partitionID UniqueID
|
|
|
|
total int
|
|
|
|
sealed bool
|
|
|
|
lastExpireTime Timestamp
|
|
|
|
allocations []*allocation
|
|
|
|
insertChannel string
|
|
|
|
}
|
|
|
|
type allocation struct {
|
|
|
|
rowNums int
|
|
|
|
expireTime Timestamp
|
|
|
|
}
|
|
|
|
type segmentAllocator struct {
|
|
|
|
mt *meta
|
|
|
|
segments map[UniqueID]*segmentStatus //segment id -> status
|
|
|
|
segmentExpireDuration int64
|
|
|
|
segmentThreshold float64
|
|
|
|
segmentThresholdFactor float64
|
|
|
|
mu sync.RWMutex
|
|
|
|
allocator allocatorInterface
|
2021-04-12 16:35:51 +08:00
|
|
|
segmentInfoStream msgstream.MsgStream
|
|
|
|
}
|
|
|
|
|
|
|
|
type Option struct {
|
|
|
|
apply func(alloc *segmentAllocator)
|
2021-03-05 16:52:45 +08:00
|
|
|
}
|
2021-01-15 17:09:41 +08:00
|
|
|
|
2021-04-12 16:35:51 +08:00
|
|
|
func WithSegmentStream(stream msgstream.MsgStream) Option {
|
|
|
|
return Option{
|
|
|
|
apply: func(alloc *segmentAllocator) {
|
|
|
|
alloc.segmentInfoStream = stream
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func newSegmentAllocator(meta *meta, allocator allocatorInterface, opts ...Option) *segmentAllocator {
|
|
|
|
alloc := &segmentAllocator{
|
2021-01-22 19:43:27 +08:00
|
|
|
mt: meta,
|
2021-01-15 17:09:41 +08:00
|
|
|
segments: make(map[UniqueID]*segmentStatus),
|
|
|
|
segmentExpireDuration: Params.SegIDAssignExpiration,
|
|
|
|
segmentThreshold: Params.SegmentSize * 1024 * 1024,
|
|
|
|
segmentThresholdFactor: Params.SegmentSizeFactor,
|
2021-01-19 12:10:49 +08:00
|
|
|
allocator: allocator,
|
2021-01-15 17:09:41 +08:00
|
|
|
}
|
2021-04-12 16:35:51 +08:00
|
|
|
for _, opt := range opts {
|
|
|
|
opt.apply(alloc)
|
|
|
|
}
|
|
|
|
return alloc
|
2021-01-15 17:09:41 +08:00
|
|
|
}
|
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
func (allocator *segmentAllocator) OpenSegment(ctx context.Context, segmentInfo *datapb.SegmentInfo) error {
|
|
|
|
sp, _ := trace.StartSpanFromContext(ctx)
|
|
|
|
defer sp.Finish()
|
2021-01-29 17:08:31 +08:00
|
|
|
allocator.mu.Lock()
|
|
|
|
defer allocator.mu.Unlock()
|
2021-04-09 09:55:04 +08:00
|
|
|
if _, ok := allocator.segments[segmentInfo.ID]; ok {
|
|
|
|
return fmt.Errorf("segment %d already exist", segmentInfo.ID)
|
2021-01-15 17:09:41 +08:00
|
|
|
}
|
2021-04-12 16:35:51 +08:00
|
|
|
return allocator.open(segmentInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (allocator *segmentAllocator) open(segmentInfo *datapb.SegmentInfo) error {
|
2021-01-22 19:43:27 +08:00
|
|
|
totalRows, err := allocator.estimateTotalRows(segmentInfo.CollectionID)
|
2021-01-15 17:09:41 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-03-27 11:35:42 +08:00
|
|
|
log.Debug("dataservice: estimateTotalRows: ",
|
|
|
|
zap.Int64("CollectionID", segmentInfo.CollectionID),
|
2021-04-09 09:55:04 +08:00
|
|
|
zap.Int64("SegmentID", segmentInfo.ID),
|
2021-03-27 11:35:42 +08:00
|
|
|
zap.Int("Rows", totalRows))
|
2021-04-09 09:55:04 +08:00
|
|
|
allocator.segments[segmentInfo.ID] = &segmentStatus{
|
|
|
|
id: segmentInfo.ID,
|
2021-01-22 19:43:27 +08:00
|
|
|
collectionID: segmentInfo.CollectionID,
|
|
|
|
partitionID: segmentInfo.PartitionID,
|
2021-01-15 17:09:41 +08:00
|
|
|
total: totalRows,
|
|
|
|
sealed: false,
|
|
|
|
lastExpireTime: 0,
|
2021-02-02 18:53:10 +08:00
|
|
|
insertChannel: segmentInfo.InsertChannel,
|
2021-01-15 17:09:41 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
func (allocator *segmentAllocator) AllocSegment(ctx context.Context, collectionID UniqueID,
|
2021-01-22 11:07:07 +08:00
|
|
|
partitionID UniqueID, channelName string, requestRows int) (segID UniqueID, retCount int, expireTime Timestamp, err error) {
|
2021-03-23 16:57:59 +08:00
|
|
|
sp, _ := trace.StartSpanFromContext(ctx)
|
|
|
|
defer sp.Finish()
|
2021-01-15 17:09:41 +08:00
|
|
|
allocator.mu.Lock()
|
|
|
|
defer allocator.mu.Unlock()
|
|
|
|
|
|
|
|
for _, segStatus := range allocator.segments {
|
|
|
|
if segStatus.sealed || segStatus.collectionID != collectionID || segStatus.partitionID != partitionID ||
|
2021-02-02 18:53:10 +08:00
|
|
|
segStatus.insertChannel != channelName {
|
2021-01-15 17:09:41 +08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
var success bool
|
|
|
|
success, err = allocator.alloc(segStatus, requestRows)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !success {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
segID = segStatus.id
|
|
|
|
retCount = requestRows
|
|
|
|
expireTime = segStatus.lastExpireTime
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-04-12 16:35:51 +08:00
|
|
|
var segStatus *segmentStatus
|
|
|
|
segStatus, err = allocator.openNewSegment(ctx, collectionID, partitionID, channelName)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var success bool
|
|
|
|
success, err = allocator.alloc(segStatus, requestRows)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !success {
|
|
|
|
err = newErrRemainInSufficient(requestRows)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
segID = segStatus.id
|
|
|
|
retCount = requestRows
|
|
|
|
expireTime = segStatus.lastExpireTime
|
2021-01-15 17:09:41 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-03-05 16:52:45 +08:00
|
|
|
func (allocator *segmentAllocator) alloc(segStatus *segmentStatus, numRows int) (bool, error) {
|
2021-01-15 17:09:41 +08:00
|
|
|
totalOfAllocations := 0
|
|
|
|
for _, allocation := range segStatus.allocations {
|
|
|
|
totalOfAllocations += allocation.rowNums
|
|
|
|
}
|
2021-01-19 12:10:49 +08:00
|
|
|
segMeta, err := allocator.mt.GetSegment(segStatus.id)
|
2021-01-15 17:09:41 +08:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
free := segStatus.total - int(segMeta.NumRows) - totalOfAllocations
|
2021-03-27 11:35:42 +08:00
|
|
|
log.Debug("dataservice::alloc: ",
|
|
|
|
zap.Any("segMeta.NumRows", int(segMeta.NumRows)),
|
|
|
|
zap.Any("totalOfAllocations", totalOfAllocations))
|
2021-01-15 17:09:41 +08:00
|
|
|
if numRows > free {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2021-01-19 12:10:49 +08:00
|
|
|
ts, err := allocator.allocator.allocTimestamp()
|
2021-01-15 17:09:41 +08:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
physicalTs, logicalTs := tsoutil.ParseTS(ts)
|
|
|
|
expirePhysicalTs := physicalTs.Add(time.Duration(allocator.segmentExpireDuration) * time.Millisecond)
|
|
|
|
expireTs := tsoutil.ComposeTS(expirePhysicalTs.UnixNano()/int64(time.Millisecond), int64(logicalTs))
|
|
|
|
segStatus.lastExpireTime = expireTs
|
|
|
|
segStatus.allocations = append(segStatus.allocations, &allocation{
|
|
|
|
numRows,
|
2021-01-19 12:10:49 +08:00
|
|
|
expireTs,
|
2021-01-15 17:09:41 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2021-04-12 16:35:51 +08:00
|
|
|
func (allocator *segmentAllocator) openNewSegment(ctx context.Context, collectionID UniqueID, partitionID UniqueID, channelName string) (*segmentStatus, error) {
|
|
|
|
sp, _ := trace.StartSpanFromContext(ctx)
|
|
|
|
defer sp.Finish()
|
|
|
|
id, err := allocator.allocator.allocID()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
segmentInfo, err := BuildSegment(collectionID, partitionID, id, channelName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err = allocator.mt.AddSegment(segmentInfo); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err = allocator.open(segmentInfo); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
infoMsg := &msgstream.SegmentInfoMsg{
|
|
|
|
BaseMsg: msgstream.BaseMsg{
|
|
|
|
HashValues: []uint32{0},
|
|
|
|
},
|
|
|
|
SegmentMsg: datapb.SegmentMsg{
|
|
|
|
Base: &commonpb.MsgBase{
|
|
|
|
MsgType: commonpb.MsgType_SegmentInfo,
|
|
|
|
MsgID: 0,
|
|
|
|
Timestamp: 0,
|
|
|
|
SourceID: Params.NodeID,
|
|
|
|
},
|
|
|
|
Segment: segmentInfo,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
msgPack := &msgstream.MsgPack{
|
|
|
|
Msgs: []msgstream.TsMsg{infoMsg},
|
|
|
|
}
|
|
|
|
if allocator.segmentInfoStream != nil {
|
|
|
|
if err = allocator.segmentInfoStream.Produce(msgPack); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return allocator.segments[segmentInfo.ID], nil
|
|
|
|
}
|
|
|
|
|
2021-03-05 16:52:45 +08:00
|
|
|
func (allocator *segmentAllocator) estimateTotalRows(collectionID UniqueID) (int, error) {
|
2021-01-15 17:09:41 +08:00
|
|
|
collMeta, err := allocator.mt.GetCollection(collectionID)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
sizePerRecord, err := typeutil.EstimateSizePerRecord(collMeta.Schema)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
return int(allocator.segmentThreshold / float64(sizePerRecord)), nil
|
|
|
|
}
|
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
func (allocator *segmentAllocator) GetSealedSegments(ctx context.Context) ([]UniqueID, error) {
|
|
|
|
sp, _ := trace.StartSpanFromContext(ctx)
|
|
|
|
defer sp.Finish()
|
2021-01-15 17:09:41 +08:00
|
|
|
allocator.mu.Lock()
|
|
|
|
defer allocator.mu.Unlock()
|
|
|
|
keys := make([]UniqueID, 0)
|
|
|
|
for _, segStatus := range allocator.segments {
|
|
|
|
if !segStatus.sealed {
|
|
|
|
sealed, err := allocator.checkSegmentSealed(segStatus)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
segStatus.sealed = sealed
|
|
|
|
}
|
|
|
|
if segStatus.sealed {
|
|
|
|
keys = append(keys, segStatus.id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return keys, nil
|
|
|
|
}
|
|
|
|
|
2021-03-05 16:52:45 +08:00
|
|
|
func (allocator *segmentAllocator) checkSegmentSealed(segStatus *segmentStatus) (bool, error) {
|
2021-01-19 12:10:49 +08:00
|
|
|
segMeta, err := allocator.mt.GetSegment(segStatus.id)
|
2021-01-15 17:09:41 +08:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return float64(segMeta.NumRows) >= allocator.segmentThresholdFactor*float64(segStatus.total), nil
|
|
|
|
}
|
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
func (allocator *segmentAllocator) SealSegment(ctx context.Context, segmentID UniqueID) error {
|
|
|
|
sp, _ := trace.StartSpanFromContext(ctx)
|
|
|
|
defer sp.Finish()
|
2021-01-15 17:09:41 +08:00
|
|
|
allocator.mu.Lock()
|
|
|
|
defer allocator.mu.Unlock()
|
|
|
|
status, ok := allocator.segments[segmentID]
|
|
|
|
if !ok {
|
2021-01-22 11:07:07 +08:00
|
|
|
return nil
|
|
|
|
}
|
2021-01-15 17:09:41 +08:00
|
|
|
status.sealed = true
|
2021-01-22 11:07:07 +08:00
|
|
|
return nil
|
2021-01-15 17:09:41 +08:00
|
|
|
}
|
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
func (allocator *segmentAllocator) DropSegment(ctx context.Context, segmentID UniqueID) {
|
|
|
|
sp, _ := trace.StartSpanFromContext(ctx)
|
|
|
|
defer sp.Finish()
|
2021-01-15 17:09:41 +08:00
|
|
|
allocator.mu.Lock()
|
|
|
|
defer allocator.mu.Unlock()
|
|
|
|
delete(allocator.segments, segmentID)
|
|
|
|
}
|
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
func (allocator *segmentAllocator) ExpireAllocations(ctx context.Context, timeTick Timestamp) error {
|
|
|
|
sp, _ := trace.StartSpanFromContext(ctx)
|
|
|
|
defer sp.Finish()
|
2021-01-15 17:09:41 +08:00
|
|
|
allocator.mu.Lock()
|
|
|
|
defer allocator.mu.Unlock()
|
|
|
|
for _, segStatus := range allocator.segments {
|
|
|
|
for i := 0; i < len(segStatus.allocations); i++ {
|
|
|
|
if timeTick < segStatus.allocations[i].expireTime {
|
|
|
|
continue
|
|
|
|
}
|
2021-03-27 11:35:42 +08:00
|
|
|
log.Debug("dataservice::ExpireAllocations: ",
|
|
|
|
zap.Any("segStatus.id", segStatus.id),
|
|
|
|
zap.Any("segStatus.allocations.rowNums", segStatus.allocations[i].rowNums))
|
2021-01-15 17:09:41 +08:00
|
|
|
segStatus.allocations = append(segStatus.allocations[:i], segStatus.allocations[i+1:]...)
|
|
|
|
i--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
func (allocator *segmentAllocator) IsAllocationsExpired(ctx context.Context, segmentID UniqueID, ts Timestamp) (bool, error) {
|
|
|
|
sp, _ := trace.StartSpanFromContext(ctx)
|
|
|
|
defer sp.Finish()
|
2021-01-15 17:09:41 +08:00
|
|
|
allocator.mu.RLock()
|
|
|
|
defer allocator.mu.RUnlock()
|
|
|
|
status, ok := allocator.segments[segmentID]
|
|
|
|
if !ok {
|
|
|
|
return false, fmt.Errorf("segment %d not found", segmentID)
|
|
|
|
}
|
|
|
|
return status.lastExpireTime <= ts, nil
|
|
|
|
}
|
2021-01-22 19:43:27 +08:00
|
|
|
|
2021-03-23 16:57:59 +08:00
|
|
|
func (allocator *segmentAllocator) SealAllSegments(ctx context.Context, collectionID UniqueID) {
|
|
|
|
sp, _ := trace.StartSpanFromContext(ctx)
|
|
|
|
defer sp.Finish()
|
2021-01-22 19:43:27 +08:00
|
|
|
allocator.mu.Lock()
|
|
|
|
defer allocator.mu.Unlock()
|
|
|
|
for _, status := range allocator.segments {
|
|
|
|
if status.collectionID == collectionID {
|
|
|
|
if status.sealed {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
status.sealed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|