mirror of
https://gitee.com/milvus-io/milvus.git
synced 2025-12-07 09:38:39 +08:00
enhance: Add l0 segment entry num quota (#34733)
See also #34670 This PR add quota configuration for l0 segment entry number per collection. If l0 compaction cannot keep up the insertion/upsertion rate, this feature could back press the related rate. --------- Signed-off-by: Congqi Xia <congqi.xia@zilliz.com>
This commit is contained in:
parent
aa5418a5a9
commit
67324eb809
@ -284,7 +284,8 @@ func CheckDiskQuota(job ImportJob, meta *meta, imeta ImportMeta) (int64, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := merr.WrapErrServiceQuotaExceeded("disk quota exceeded, please allocate more resources")
|
err := merr.WrapErrServiceQuotaExceeded("disk quota exceeded, please allocate more resources")
|
||||||
totalUsage, collectionsUsage, _ := meta.GetCollectionBinlogSize()
|
quotaInfo := meta.GetQuotaInfo()
|
||||||
|
totalUsage, collectionsUsage := quotaInfo.TotalBinlogSize, quotaInfo.CollectionBinlogSize
|
||||||
|
|
||||||
tasks := imeta.GetTaskBy(WithJob(job.GetJobID()), WithType(PreImportTaskType))
|
tasks := imeta.GetTaskBy(WithJob(job.GetJobID()), WithType(PreImportTaskType))
|
||||||
files := make([]*datapb.ImportFileStats, 0)
|
files := make([]*datapb.ImportFileStats, 0)
|
||||||
|
|||||||
@ -46,6 +46,7 @@ import (
|
|||||||
"github.com/milvus-io/milvus/pkg/util/lock"
|
"github.com/milvus-io/milvus/pkg/util/lock"
|
||||||
"github.com/milvus-io/milvus/pkg/util/merr"
|
"github.com/milvus-io/milvus/pkg/util/merr"
|
||||||
"github.com/milvus-io/milvus/pkg/util/metautil"
|
"github.com/milvus-io/milvus/pkg/util/metautil"
|
||||||
|
"github.com/milvus-io/milvus/pkg/util/metricsinfo"
|
||||||
"github.com/milvus-io/milvus/pkg/util/paramtable"
|
"github.com/milvus-io/milvus/pkg/util/paramtable"
|
||||||
"github.com/milvus-io/milvus/pkg/util/timerecord"
|
"github.com/milvus-io/milvus/pkg/util/timerecord"
|
||||||
"github.com/milvus-io/milvus/pkg/util/tsoutil"
|
"github.com/milvus-io/milvus/pkg/util/tsoutil"
|
||||||
@ -383,13 +384,16 @@ func (m *meta) GetNumRowsOfCollection(collectionID UniqueID) int64 {
|
|||||||
return m.getNumRowsOfCollectionUnsafe(collectionID)
|
return m.getNumRowsOfCollectionUnsafe(collectionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCollectionBinlogSize returns the total binlog size and binlog size of collections.
|
func (m *meta) GetQuotaInfo() *metricsinfo.DataCoordQuotaMetrics {
|
||||||
func (m *meta) GetCollectionBinlogSize() (int64, map[UniqueID]int64, map[UniqueID]map[UniqueID]int64) {
|
info := &metricsinfo.DataCoordQuotaMetrics{}
|
||||||
m.RLock()
|
m.RLock()
|
||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
collectionBinlogSize := make(map[UniqueID]int64)
|
collectionBinlogSize := make(map[UniqueID]int64)
|
||||||
partitionBinlogSize := make(map[UniqueID]map[UniqueID]int64)
|
partitionBinlogSize := make(map[UniqueID]map[UniqueID]int64)
|
||||||
collectionRowsNum := make(map[UniqueID]map[commonpb.SegmentState]int64)
|
collectionRowsNum := make(map[UniqueID]map[commonpb.SegmentState]int64)
|
||||||
|
// collection id => l0 delta entry count
|
||||||
|
collectionL0RowCounts := make(map[UniqueID]int64)
|
||||||
|
|
||||||
segments := m.segments.GetSegments()
|
segments := m.segments.GetSegments()
|
||||||
var total int64
|
var total int64
|
||||||
for _, segment := range segments {
|
for _, segment := range segments {
|
||||||
@ -417,6 +421,10 @@ func (m *meta) GetCollectionBinlogSize() (int64, map[UniqueID]int64, map[UniqueI
|
|||||||
collectionRowsNum[segment.GetCollectionID()] = make(map[commonpb.SegmentState]int64)
|
collectionRowsNum[segment.GetCollectionID()] = make(map[commonpb.SegmentState]int64)
|
||||||
}
|
}
|
||||||
collectionRowsNum[segment.GetCollectionID()][segment.GetState()] += segment.GetNumOfRows()
|
collectionRowsNum[segment.GetCollectionID()][segment.GetState()] += segment.GetNumOfRows()
|
||||||
|
|
||||||
|
if segment.GetLevel() == datapb.SegmentLevel_L0 {
|
||||||
|
collectionL0RowCounts[segment.GetCollectionID()] += segment.getDeltaCount()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,7 +437,13 @@ func (m *meta) GetCollectionBinlogSize() (int64, map[UniqueID]int64, map[UniqueI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return total, collectionBinlogSize, partitionBinlogSize
|
|
||||||
|
info.TotalBinlogSize = total
|
||||||
|
info.CollectionBinlogSize = collectionBinlogSize
|
||||||
|
info.PartitionsBinlogSize = partitionBinlogSize
|
||||||
|
info.CollectionL0RowCount = collectionL0RowCounts
|
||||||
|
|
||||||
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCollectionIndexFilesSize returns the total index files size of all segment for each collection.
|
// GetCollectionIndexFilesSize returns the total index files size of all segment for each collection.
|
||||||
|
|||||||
@ -589,16 +589,16 @@ func TestMeta_Basic(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// check TotalBinlogSize
|
// check TotalBinlogSize
|
||||||
total, collectionBinlogSize, _ := meta.GetCollectionBinlogSize()
|
quotaInfo := meta.GetQuotaInfo()
|
||||||
assert.Len(t, collectionBinlogSize, 1)
|
assert.Len(t, quotaInfo.CollectionBinlogSize, 1)
|
||||||
assert.Equal(t, int64(size0+size1), collectionBinlogSize[collID])
|
assert.Equal(t, int64(size0+size1), quotaInfo.CollectionBinlogSize[collID])
|
||||||
assert.Equal(t, int64(size0+size1), total)
|
assert.Equal(t, int64(size0+size1), quotaInfo.TotalBinlogSize)
|
||||||
|
|
||||||
meta.collections[collID] = collInfo
|
meta.collections[collID] = collInfo
|
||||||
total, collectionBinlogSize, _ = meta.GetCollectionBinlogSize()
|
quotaInfo = meta.GetQuotaInfo()
|
||||||
assert.Len(t, collectionBinlogSize, 1)
|
assert.Len(t, quotaInfo.CollectionBinlogSize, 1)
|
||||||
assert.Equal(t, int64(size0+size1), collectionBinlogSize[collID])
|
assert.Equal(t, int64(size0+size1), quotaInfo.CollectionBinlogSize[collID])
|
||||||
assert.Equal(t, int64(size0+size1), total)
|
assert.Equal(t, int64(size0+size1), quotaInfo.TotalBinlogSize)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Test GetCollectionBinlogSize", func(t *testing.T) {
|
t.Run("Test GetCollectionBinlogSize", func(t *testing.T) {
|
||||||
|
|||||||
@ -37,14 +37,10 @@ import (
|
|||||||
|
|
||||||
// getQuotaMetrics returns DataCoordQuotaMetrics.
|
// getQuotaMetrics returns DataCoordQuotaMetrics.
|
||||||
func (s *Server) getQuotaMetrics() *metricsinfo.DataCoordQuotaMetrics {
|
func (s *Server) getQuotaMetrics() *metricsinfo.DataCoordQuotaMetrics {
|
||||||
total, colSizes, partSizes := s.meta.GetCollectionBinlogSize()
|
info := s.meta.GetQuotaInfo()
|
||||||
// Just generate the metrics data regularly
|
// Just generate the metrics data regularly
|
||||||
_ = s.meta.GetCollectionIndexFilesSize()
|
_ = s.meta.GetCollectionIndexFilesSize()
|
||||||
return &metricsinfo.DataCoordQuotaMetrics{
|
return info
|
||||||
TotalBinlogSize: total,
|
|
||||||
CollectionBinlogSize: colSizes,
|
|
||||||
PartitionsBinlogSize: partSizes,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getCollectionMetrics(ctx context.Context) *metricsinfo.DataCoordCollectionMetrics {
|
func (s *Server) getCollectionMetrics(ctx context.Context) *metricsinfo.DataCoordCollectionMetrics {
|
||||||
|
|||||||
@ -53,6 +53,7 @@ type SegmentInfo struct {
|
|||||||
isCompacting bool
|
isCompacting bool
|
||||||
// a cache to avoid calculate twice
|
// a cache to avoid calculate twice
|
||||||
size atomic.Int64
|
size atomic.Int64
|
||||||
|
deltaRowcount atomic.Int64
|
||||||
lastWrittenTime time.Time
|
lastWrittenTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,14 +62,20 @@ type SegmentInfo struct {
|
|||||||
// Note that the allocation information is not preserved,
|
// Note that the allocation information is not preserved,
|
||||||
// the worst case scenario is to have a segment with twice size we expects
|
// the worst case scenario is to have a segment with twice size we expects
|
||||||
func NewSegmentInfo(info *datapb.SegmentInfo) *SegmentInfo {
|
func NewSegmentInfo(info *datapb.SegmentInfo) *SegmentInfo {
|
||||||
return &SegmentInfo{
|
s := &SegmentInfo{
|
||||||
SegmentInfo: info,
|
SegmentInfo: info,
|
||||||
currRows: info.GetNumOfRows(),
|
currRows: info.GetNumOfRows(),
|
||||||
allocations: make([]*Allocation, 0, 16),
|
|
||||||
lastFlushTime: time.Now().Add(-1 * paramtable.Get().DataCoordCfg.SegmentFlushInterval.GetAsDuration(time.Second)),
|
|
||||||
// A growing segment from recovery can be also considered idle.
|
|
||||||
lastWrittenTime: getZeroTime(),
|
|
||||||
}
|
}
|
||||||
|
// setup growing fields
|
||||||
|
if s.GetState() == commonpb.SegmentState_Growing {
|
||||||
|
s.allocations = make([]*Allocation, 0, 16)
|
||||||
|
s.lastFlushTime = time.Now().Add(-1 * paramtable.Get().DataCoordCfg.SegmentFlushInterval.GetAsDuration(time.Second))
|
||||||
|
// A growing segment from recovery can be also considered idle.
|
||||||
|
s.lastWrittenTime = getZeroTime()
|
||||||
|
}
|
||||||
|
// mark as uninitialized
|
||||||
|
s.deltaRowcount.Store(-1)
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSegmentsInfo creates a `SegmentsInfo` instance, which makes sure internal map is initialized
|
// NewSegmentsInfo creates a `SegmentsInfo` instance, which makes sure internal map is initialized
|
||||||
@ -330,6 +337,7 @@ func (s *SegmentInfo) ShadowClone(opts ...SegmentInfoOption) *SegmentInfo {
|
|||||||
lastWrittenTime: s.lastWrittenTime,
|
lastWrittenTime: s.lastWrittenTime,
|
||||||
}
|
}
|
||||||
cloned.size.Store(s.size.Load())
|
cloned.size.Store(s.size.Load())
|
||||||
|
cloned.deltaRowcount.Store(s.deltaRowcount.Load())
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(cloned)
|
opt(cloned)
|
||||||
@ -492,5 +500,19 @@ func (s *SegmentInfo) getSegmentSize() int64 {
|
|||||||
return s.size.Load()
|
return s.size.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SegmentInfo) getDeltaCount() int64 {
|
||||||
|
if s.deltaRowcount.Load() < 0 {
|
||||||
|
var rc int64
|
||||||
|
for _, deltaLogs := range s.GetDeltalogs() {
|
||||||
|
for _, l := range deltaLogs.GetBinlogs() {
|
||||||
|
rc += l.GetEntriesNum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.deltaRowcount.Store(rc)
|
||||||
|
}
|
||||||
|
r := s.deltaRowcount.Load()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
// SegmentInfoSelector is the function type to select SegmentInfo from meta
|
// SegmentInfoSelector is the function type to select SegmentInfo from meta
|
||||||
type SegmentInfoSelector func(*SegmentInfo) bool
|
type SegmentInfoSelector func(*SegmentInfo) bool
|
||||||
|
|||||||
@ -270,13 +270,14 @@ func getRateTypes(scope internalpb.RateScope, opType opType) typeutil.Set[intern
|
|||||||
|
|
||||||
func (q *QuotaCenter) Start() {
|
func (q *QuotaCenter) Start() {
|
||||||
q.wg.Add(1)
|
q.wg.Add(1)
|
||||||
go q.run()
|
go func() {
|
||||||
|
defer q.wg.Done()
|
||||||
|
q.run()
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// run starts the service of QuotaCenter.
|
// run starts the service of QuotaCenter.
|
||||||
func (q *QuotaCenter) run() {
|
func (q *QuotaCenter) run() {
|
||||||
defer q.wg.Done()
|
|
||||||
|
|
||||||
interval := Params.QuotaConfig.QuotaCenterCollectInterval.GetAsDuration(time.Second)
|
interval := Params.QuotaConfig.QuotaCenterCollectInterval.GetAsDuration(time.Second)
|
||||||
log.Info("Start QuotaCenter", zap.Duration("collectInterval", interval))
|
log.Info("Start QuotaCenter", zap.Duration("collectInterval", interval))
|
||||||
ticker := time.NewTicker(interval)
|
ticker := time.NewTicker(interval)
|
||||||
@ -957,6 +958,8 @@ func (q *QuotaCenter) calculateWriteRates() error {
|
|||||||
updateCollectionFactor(memFactors)
|
updateCollectionFactor(memFactors)
|
||||||
growingSegFactors := q.getGrowingSegmentsSizeFactor()
|
growingSegFactors := q.getGrowingSegmentsSizeFactor()
|
||||||
updateCollectionFactor(growingSegFactors)
|
updateCollectionFactor(growingSegFactors)
|
||||||
|
l0Factors := q.getL0SegmentsSizeFactor()
|
||||||
|
updateCollectionFactor(l0Factors)
|
||||||
|
|
||||||
ttCollections := make([]int64, 0)
|
ttCollections := make([]int64, 0)
|
||||||
memoryCollections := make([]int64, 0)
|
memoryCollections := make([]int64, 0)
|
||||||
@ -1214,6 +1217,26 @@ func (q *QuotaCenter) getGrowingSegmentsSizeFactor() map[int64]float64 {
|
|||||||
return collectionFactor
|
return collectionFactor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getL0SegmentsSizeFactor checks wether any collection
|
||||||
|
func (q *QuotaCenter) getL0SegmentsSizeFactor() map[int64]float64 {
|
||||||
|
if !Params.QuotaConfig.L0SegmentRowCountProtectionEnabled.GetAsBool() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l0segmentSizeLowWaterLevel := Params.QuotaConfig.L0SegmentRowCountLowWaterLevel.GetAsInt64()
|
||||||
|
l0SegmentSizeHighWaterLevel := Params.QuotaConfig.L0SegmentRowCountHighWaterLevel.GetAsInt64()
|
||||||
|
|
||||||
|
collectionFactor := make(map[int64]float64)
|
||||||
|
for collectionID, l0RowCount := range q.dataCoordMetrics.CollectionL0RowCount {
|
||||||
|
if l0RowCount < l0segmentSizeLowWaterLevel {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
factor := float64(l0SegmentSizeHighWaterLevel-l0RowCount) / float64(l0SegmentSizeHighWaterLevel-l0segmentSizeLowWaterLevel)
|
||||||
|
collectionFactor[collectionID] = factor
|
||||||
|
}
|
||||||
|
return collectionFactor
|
||||||
|
}
|
||||||
|
|
||||||
// calculateRates calculates target rates by different strategies.
|
// calculateRates calculates target rates by different strategies.
|
||||||
func (q *QuotaCenter) calculateRates() error {
|
func (q *QuotaCenter) calculateRates() error {
|
||||||
err := q.resetAllCurrentRates()
|
err := q.resetAllCurrentRates()
|
||||||
|
|||||||
@ -88,6 +88,8 @@ type DataCoordQuotaMetrics struct {
|
|||||||
TotalBinlogSize int64
|
TotalBinlogSize int64
|
||||||
CollectionBinlogSize map[int64]int64
|
CollectionBinlogSize map[int64]int64
|
||||||
PartitionsBinlogSize map[int64]map[int64]int64
|
PartitionsBinlogSize map[int64]map[int64]int64
|
||||||
|
// l0 segments
|
||||||
|
CollectionL0RowCount map[int64]int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// DataNodeQuotaMetrics are metrics of DataNode.
|
// DataNodeQuotaMetrics are metrics of DataNode.
|
||||||
|
|||||||
@ -152,6 +152,9 @@ type quotaConfig struct {
|
|||||||
DiskQuotaPerDB ParamItem `refreshable:"true"`
|
DiskQuotaPerDB ParamItem `refreshable:"true"`
|
||||||
DiskQuotaPerCollection ParamItem `refreshable:"true"`
|
DiskQuotaPerCollection ParamItem `refreshable:"true"`
|
||||||
DiskQuotaPerPartition ParamItem `refreshable:"true"`
|
DiskQuotaPerPartition ParamItem `refreshable:"true"`
|
||||||
|
L0SegmentRowCountProtectionEnabled ParamItem `refreshable:"true"`
|
||||||
|
L0SegmentRowCountLowWaterLevel ParamItem `refreshable:"true"`
|
||||||
|
L0SegmentRowCountHighWaterLevel ParamItem `refreshable:"true"`
|
||||||
|
|
||||||
// limit reading
|
// limit reading
|
||||||
ForceDenyReading ParamItem `refreshable:"true"`
|
ForceDenyReading ParamItem `refreshable:"true"`
|
||||||
@ -1856,6 +1859,33 @@ but the rate will not be lower than minRateRatio * dmlRate.`,
|
|||||||
}
|
}
|
||||||
p.DiskQuotaPerPartition.Init(base.mgr)
|
p.DiskQuotaPerPartition.Init(base.mgr)
|
||||||
|
|
||||||
|
p.L0SegmentRowCountProtectionEnabled = ParamItem{
|
||||||
|
Key: "quotaAndLimits.limitWriting.l0SegmentsRowCountProtection.enabled",
|
||||||
|
Version: "2.4.7",
|
||||||
|
DefaultValue: "false",
|
||||||
|
Doc: "switch to enable l0 segment row count quota",
|
||||||
|
Export: true,
|
||||||
|
}
|
||||||
|
p.L0SegmentRowCountProtectionEnabled.Init(base.mgr)
|
||||||
|
|
||||||
|
p.L0SegmentRowCountLowWaterLevel = ParamItem{
|
||||||
|
Key: "quotaAndLimits.limitWriting.l0SegmentsRowCountProtection.lowWaterLevel",
|
||||||
|
Version: "2.4.7",
|
||||||
|
DefaultValue: "32768",
|
||||||
|
Doc: "l0 segment row count quota, low water level",
|
||||||
|
Export: true,
|
||||||
|
}
|
||||||
|
p.L0SegmentRowCountLowWaterLevel.Init(base.mgr)
|
||||||
|
|
||||||
|
p.L0SegmentRowCountHighWaterLevel = ParamItem{
|
||||||
|
Key: "quotaAndLimits.limitWriting.l0SegmentsRowCountProtection.highWaterLevel",
|
||||||
|
Version: "2.4.7",
|
||||||
|
DefaultValue: "65536",
|
||||||
|
Doc: "l0 segment row count quota, low water level",
|
||||||
|
Export: true,
|
||||||
|
}
|
||||||
|
p.L0SegmentRowCountHighWaterLevel.Init(base.mgr)
|
||||||
|
|
||||||
// limit reading
|
// limit reading
|
||||||
p.ForceDenyReading = ParamItem{
|
p.ForceDenyReading = ParamItem{
|
||||||
Key: "quotaAndLimits.limitReading.forceDeny",
|
Key: "quotaAndLimits.limitReading.forceDeny",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user