mirror of
https://gitee.com/milvus-io/milvus.git
synced 2024-12-04 12:59:23 +08:00
99586066f5
This PR cherry-picks the following commits: fix: speed up segment lookup via channel name in datacoord (#33530) needed by the next commit feat: Major compaction (#33620) issue: #30633 pr: #33620 --------- Signed-off-by: yiwangdr <yiwangdr@gmail.com> Signed-off-by: wayblink <anyang.wang@zilliz.com> Co-authored-by: yiwangdr <80064917+yiwangdr@users.noreply.github.com> Co-authored-by: MrPresent-Han <chun.han@zilliz.com>
299 lines
7.5 KiB
Go
299 lines
7.5 KiB
Go
// Licensed to the LF AI & Data foundation under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you 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.
|
|
|
|
package meta
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/samber/lo"
|
|
|
|
"github.com/milvus-io/milvus/internal/proto/querypb"
|
|
"github.com/milvus-io/milvus/pkg/util/typeutil"
|
|
)
|
|
|
|
type lvCriterion struct {
|
|
nodeID int64
|
|
channelName string
|
|
collectionID int64
|
|
hasOtherFilter bool
|
|
}
|
|
|
|
type LeaderViewFilter interface {
|
|
Match(*LeaderView) bool
|
|
AddFilter(*lvCriterion)
|
|
}
|
|
|
|
type lvFilterFunc func(view *LeaderView) bool
|
|
|
|
func (f lvFilterFunc) Match(view *LeaderView) bool {
|
|
return f(view)
|
|
}
|
|
|
|
func (f lvFilterFunc) AddFilter(c *lvCriterion) {
|
|
c.hasOtherFilter = true
|
|
}
|
|
|
|
type lvChannelNameFilter string
|
|
|
|
func (f lvChannelNameFilter) Match(v *LeaderView) bool {
|
|
return v.Channel == string(f)
|
|
}
|
|
|
|
func (f lvChannelNameFilter) AddFilter(c *lvCriterion) {
|
|
c.channelName = string(f)
|
|
}
|
|
|
|
type lvNodeFilter int64
|
|
|
|
func (f lvNodeFilter) Match(v *LeaderView) bool {
|
|
return v.ID == int64(f)
|
|
}
|
|
|
|
func (f lvNodeFilter) AddFilter(c *lvCriterion) {
|
|
c.nodeID = int64(f)
|
|
}
|
|
|
|
type lvCollectionFilter int64
|
|
|
|
func (f lvCollectionFilter) Match(v *LeaderView) bool {
|
|
return v.CollectionID == int64(f)
|
|
}
|
|
|
|
func (f lvCollectionFilter) AddFilter(c *lvCriterion) {
|
|
c.collectionID = int64(f)
|
|
}
|
|
|
|
func WithNodeID2LeaderView(nodeID int64) LeaderViewFilter {
|
|
return lvNodeFilter(nodeID)
|
|
}
|
|
|
|
func WithChannelName2LeaderView(channelName string) LeaderViewFilter {
|
|
return lvChannelNameFilter(channelName)
|
|
}
|
|
|
|
func WithCollectionID2LeaderView(collectionID int64) LeaderViewFilter {
|
|
return lvCollectionFilter(collectionID)
|
|
}
|
|
|
|
func WithReplica2LeaderView(replica *Replica) LeaderViewFilter {
|
|
return lvFilterFunc(func(view *LeaderView) bool {
|
|
if replica == nil {
|
|
return false
|
|
}
|
|
return replica.GetCollectionID() == view.CollectionID && replica.Contains(view.ID)
|
|
})
|
|
}
|
|
|
|
func WithSegment2LeaderView(segmentID int64, isGrowing bool) LeaderViewFilter {
|
|
return lvFilterFunc(func(view *LeaderView) bool {
|
|
if isGrowing {
|
|
_, ok := view.GrowingSegments[segmentID]
|
|
return ok
|
|
}
|
|
_, ok := view.Segments[segmentID]
|
|
return ok
|
|
})
|
|
}
|
|
|
|
type LeaderView struct {
|
|
ID int64
|
|
CollectionID int64
|
|
Channel string
|
|
Version int64
|
|
Segments map[int64]*querypb.SegmentDist
|
|
GrowingSegments map[int64]*Segment
|
|
TargetVersion int64
|
|
NumOfGrowingRows int64
|
|
PartitionStatsVersions map[int64]int64
|
|
}
|
|
|
|
func (view *LeaderView) Clone() *LeaderView {
|
|
segments := make(map[int64]*querypb.SegmentDist)
|
|
for k, v := range view.Segments {
|
|
segments[k] = v
|
|
}
|
|
|
|
growings := make(map[int64]*Segment)
|
|
for k, v := range view.GrowingSegments {
|
|
growings[k] = v
|
|
}
|
|
|
|
return &LeaderView{
|
|
ID: view.ID,
|
|
CollectionID: view.CollectionID,
|
|
Channel: view.Channel,
|
|
Version: view.Version,
|
|
Segments: segments,
|
|
GrowingSegments: growings,
|
|
TargetVersion: view.TargetVersion,
|
|
NumOfGrowingRows: view.NumOfGrowingRows,
|
|
PartitionStatsVersions: view.PartitionStatsVersions,
|
|
}
|
|
}
|
|
|
|
type nodeViews struct {
|
|
views []*LeaderView
|
|
// channel name => LeaderView
|
|
channelView map[string]*LeaderView
|
|
// collection id => leader views
|
|
collectionViews map[int64][]*LeaderView
|
|
}
|
|
|
|
func (v nodeViews) Filter(criterion *lvCriterion, filters ...LeaderViewFilter) []*LeaderView {
|
|
mergedFilter := func(view *LeaderView) bool {
|
|
for _, filter := range filters {
|
|
if !filter.Match(view) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
var views []*LeaderView
|
|
switch {
|
|
case criterion.channelName != "":
|
|
if view, ok := v.channelView[criterion.channelName]; ok {
|
|
views = append(views, view)
|
|
}
|
|
case criterion.collectionID != 0:
|
|
views = v.collectionViews[criterion.collectionID]
|
|
default:
|
|
views = v.views
|
|
}
|
|
|
|
if criterion.hasOtherFilter {
|
|
views = lo.Filter(views, func(view *LeaderView, _ int) bool {
|
|
return mergedFilter(view)
|
|
})
|
|
}
|
|
return views
|
|
}
|
|
|
|
func composeNodeViews(views ...*LeaderView) nodeViews {
|
|
return nodeViews{
|
|
views: views,
|
|
channelView: lo.SliceToMap(views, func(view *LeaderView) (string, *LeaderView) {
|
|
return view.Channel, view
|
|
}),
|
|
collectionViews: lo.GroupBy(views, func(view *LeaderView) int64 {
|
|
return view.CollectionID
|
|
}),
|
|
}
|
|
}
|
|
|
|
type NotifyDelegatorChanges = func(collectionID ...int64)
|
|
|
|
type LeaderViewManager struct {
|
|
rwmutex sync.RWMutex
|
|
views map[int64]nodeViews // LeaderID -> Views (one per shard)
|
|
notifyFunc NotifyDelegatorChanges
|
|
}
|
|
|
|
func NewLeaderViewManager() *LeaderViewManager {
|
|
return &LeaderViewManager{
|
|
views: make(map[int64]nodeViews),
|
|
}
|
|
}
|
|
|
|
func (mgr *LeaderViewManager) SetNotifyFunc(notifyFunc NotifyDelegatorChanges) {
|
|
mgr.notifyFunc = notifyFunc
|
|
}
|
|
|
|
// Update updates the leader's views, all views have to be with the same leader ID
|
|
func (mgr *LeaderViewManager) Update(leaderID int64, views ...*LeaderView) {
|
|
mgr.rwmutex.Lock()
|
|
defer mgr.rwmutex.Unlock()
|
|
|
|
oldViews := make(map[string]*LeaderView, 0)
|
|
if _, ok := mgr.views[leaderID]; ok {
|
|
oldViews = mgr.views[leaderID].channelView
|
|
}
|
|
|
|
newViews := lo.SliceToMap(views, func(v *LeaderView) (string, *LeaderView) {
|
|
return v.Channel, v
|
|
})
|
|
|
|
// update leader views
|
|
mgr.views[leaderID] = composeNodeViews(views...)
|
|
|
|
// compute leader location change, find it's correspond collection
|
|
if mgr.notifyFunc != nil {
|
|
viewChanges := typeutil.NewUniqueSet()
|
|
for channel, oldView := range oldViews {
|
|
// if channel released from current node
|
|
if _, ok := newViews[channel]; !ok {
|
|
viewChanges.Insert(oldView.CollectionID)
|
|
}
|
|
}
|
|
|
|
for channel, newView := range newViews {
|
|
// if channel loaded to current node
|
|
if _, ok := oldViews[channel]; !ok {
|
|
viewChanges.Insert(newView.CollectionID)
|
|
}
|
|
}
|
|
mgr.notifyFunc(viewChanges.Collect()...)
|
|
}
|
|
}
|
|
|
|
func (mgr *LeaderViewManager) GetLeaderShardView(id int64, shard string) *LeaderView {
|
|
mgr.rwmutex.RLock()
|
|
defer mgr.rwmutex.RUnlock()
|
|
|
|
return mgr.views[id].channelView[shard]
|
|
}
|
|
|
|
func (mgr *LeaderViewManager) GetByFilter(filters ...LeaderViewFilter) []*LeaderView {
|
|
mgr.rwmutex.RLock()
|
|
defer mgr.rwmutex.RUnlock()
|
|
|
|
return mgr.getByFilter(filters...)
|
|
}
|
|
|
|
func (mgr *LeaderViewManager) getByFilter(filters ...LeaderViewFilter) []*LeaderView {
|
|
criterion := &lvCriterion{}
|
|
for _, filter := range filters {
|
|
filter.AddFilter(criterion)
|
|
}
|
|
|
|
var candidates []nodeViews
|
|
if criterion.nodeID > 0 {
|
|
nodeView, ok := mgr.views[criterion.nodeID]
|
|
if ok {
|
|
candidates = append(candidates, nodeView)
|
|
}
|
|
} else {
|
|
candidates = lo.Values(mgr.views)
|
|
}
|
|
|
|
var result []*LeaderView
|
|
for _, candidate := range candidates {
|
|
result = append(result, candidate.Filter(criterion, filters...)...)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (mgr *LeaderViewManager) GetLatestShardLeaderByFilter(filters ...LeaderViewFilter) *LeaderView {
|
|
mgr.rwmutex.RLock()
|
|
defer mgr.rwmutex.RUnlock()
|
|
views := mgr.getByFilter(filters...)
|
|
|
|
return lo.MaxBy(views, func(v1, v2 *LeaderView) bool {
|
|
return v1.Version > v2.Version
|
|
})
|
|
}
|