2023-11-04 12:10:17 +08:00
|
|
|
package writebuffer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math"
|
|
|
|
|
|
|
|
"github.com/cockroachdb/errors"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
|
|
"github.com/milvus-io/milvus-proto/go-api/v2/msgpb"
|
|
|
|
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
|
|
|
|
"github.com/milvus-io/milvus/internal/storage"
|
|
|
|
"github.com/milvus-io/milvus/pkg/log"
|
|
|
|
"github.com/milvus-io/milvus/pkg/util/paramtable"
|
|
|
|
"github.com/milvus-io/milvus/pkg/util/typeutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
noLimit int64 = -1
|
|
|
|
)
|
|
|
|
|
|
|
|
type BufferBase struct {
|
|
|
|
rows int64
|
|
|
|
rowLimit int64
|
|
|
|
size int64
|
|
|
|
sizeLimit int64
|
|
|
|
|
|
|
|
TimestampFrom typeutil.Timestamp
|
|
|
|
TimestampTo typeutil.Timestamp
|
|
|
|
|
|
|
|
startPos *msgpb.MsgPosition
|
|
|
|
endPos *msgpb.MsgPosition
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *BufferBase) UpdateStatistics(entryNum, size int64, tr TimeRange, startPos, endPos *msgpb.MsgPosition) {
|
|
|
|
b.rows += entryNum
|
|
|
|
b.size += size
|
|
|
|
|
|
|
|
if tr.timestampMin < b.TimestampFrom {
|
|
|
|
b.TimestampFrom = tr.timestampMin
|
|
|
|
}
|
|
|
|
if tr.timestampMax > b.TimestampTo {
|
|
|
|
b.TimestampTo = tr.timestampMax
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.startPos == nil || startPos.Timestamp < b.startPos.Timestamp {
|
|
|
|
b.startPos = startPos
|
|
|
|
}
|
|
|
|
if b.endPos == nil || endPos.Timestamp > b.endPos.Timestamp {
|
|
|
|
b.endPos = endPos
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *BufferBase) IsFull() bool {
|
|
|
|
return (b.rowLimit != noLimit && b.rows >= b.rowLimit) ||
|
|
|
|
(b.sizeLimit != noLimit && b.size >= b.sizeLimit)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *BufferBase) IsEmpty() bool {
|
2023-11-21 15:02:25 +08:00
|
|
|
return b.rows == 0 && b.size == 0
|
2023-11-04 12:10:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *BufferBase) MinTimestamp() typeutil.Timestamp {
|
|
|
|
if b.startPos == nil {
|
|
|
|
return math.MaxUint64
|
|
|
|
}
|
|
|
|
return b.startPos.GetTimestamp()
|
|
|
|
}
|
|
|
|
|
2023-12-05 17:36:36 +08:00
|
|
|
func (b *BufferBase) GetTimeRange() *TimeRange {
|
2024-05-23 09:53:40 +08:00
|
|
|
return NewTimeRange(b.TimestampFrom, b.TimestampTo)
|
2023-12-05 17:36:36 +08:00
|
|
|
}
|
|
|
|
|
2023-11-04 12:10:17 +08:00
|
|
|
type InsertBuffer struct {
|
|
|
|
BufferBase
|
|
|
|
collSchema *schemapb.CollectionSchema
|
|
|
|
|
2024-06-13 11:15:56 +08:00
|
|
|
buffers []*storage.InsertData
|
2023-11-04 12:10:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewInsertBuffer(sch *schemapb.CollectionSchema) (*InsertBuffer, error) {
|
2023-11-21 15:02:25 +08:00
|
|
|
estSize, err := typeutil.EstimateSizePerRecord(sch)
|
2023-11-04 12:10:17 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Warn("failed to estimate size per record", zap.Error(err))
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-11-21 15:02:25 +08:00
|
|
|
if estSize == 0 {
|
2023-11-04 12:10:17 +08:00
|
|
|
return nil, errors.New("Invalid schema")
|
|
|
|
}
|
2024-06-13 11:15:56 +08:00
|
|
|
|
2023-11-21 15:02:25 +08:00
|
|
|
sizeLimit := paramtable.Get().DataNodeCfg.FlushInsertBufferSize.GetAsInt64()
|
2023-11-04 12:10:17 +08:00
|
|
|
|
2024-06-13 11:15:56 +08:00
|
|
|
ib := &InsertBuffer{
|
2023-11-04 12:10:17 +08:00
|
|
|
BufferBase: BufferBase{
|
2023-11-21 15:02:25 +08:00
|
|
|
rowLimit: noLimit,
|
|
|
|
sizeLimit: sizeLimit,
|
2023-11-04 12:10:17 +08:00
|
|
|
TimestampFrom: math.MaxUint64,
|
|
|
|
TimestampTo: 0,
|
|
|
|
},
|
|
|
|
collSchema: sch,
|
2024-06-13 11:15:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ib, nil
|
2023-11-04 12:10:17 +08:00
|
|
|
}
|
|
|
|
|
2024-06-13 11:15:56 +08:00
|
|
|
func (ib *InsertBuffer) buffer(inData *storage.InsertData, tr TimeRange, startPos, endPos *msgpb.MsgPosition) {
|
|
|
|
// buffer := ib.currentBuffer()
|
|
|
|
// storage.MergeInsertData(buffer.buffer, inData)
|
|
|
|
ib.buffers = append(ib.buffers, inData)
|
|
|
|
ib.UpdateStatistics(int64(inData.GetRowNum()), int64(inData.GetMemorySize()), tr, startPos, endPos)
|
|
|
|
}
|
2023-11-04 12:10:17 +08:00
|
|
|
|
2024-06-13 11:15:56 +08:00
|
|
|
func (ib *InsertBuffer) Yield() []*storage.InsertData {
|
|
|
|
result := ib.buffers
|
|
|
|
// set buffer nil to so that fragmented buffer could get GCed
|
|
|
|
ib.buffers = nil
|
|
|
|
return result
|
2023-11-04 12:10:17 +08:00
|
|
|
}
|
|
|
|
|
2024-01-19 17:28:53 +08:00
|
|
|
func (ib *InsertBuffer) Buffer(inData *inData, startPos, endPos *msgpb.MsgPosition) int64 {
|
2024-05-23 09:53:40 +08:00
|
|
|
bufferedSize := int64(0)
|
2024-01-19 17:28:53 +08:00
|
|
|
for idx, data := range inData.data {
|
|
|
|
tsData := inData.tsField[idx]
|
2024-06-13 11:15:56 +08:00
|
|
|
tr := ib.getTimestampRange(tsData)
|
|
|
|
ib.buffer(data, tr, startPos, endPos)
|
2023-11-04 12:10:17 +08:00
|
|
|
|
|
|
|
// update buffer size
|
2024-06-13 11:15:56 +08:00
|
|
|
ib.UpdateStatistics(int64(data.GetRowNum()), int64(data.GetMemorySize()), tr, startPos, endPos)
|
2024-05-23 09:53:40 +08:00
|
|
|
bufferedSize += int64(data.GetMemorySize())
|
2023-11-04 12:10:17 +08:00
|
|
|
}
|
2024-05-23 09:53:40 +08:00
|
|
|
return bufferedSize
|
2023-11-04 12:10:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ib *InsertBuffer) getTimestampRange(tsData *storage.Int64FieldData) TimeRange {
|
|
|
|
tr := TimeRange{
|
|
|
|
timestampMin: math.MaxUint64,
|
|
|
|
timestampMax: 0,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, data := range tsData.Data {
|
|
|
|
if uint64(data) < tr.timestampMin {
|
|
|
|
tr.timestampMin = typeutil.Timestamp(data)
|
|
|
|
}
|
|
|
|
if uint64(data) > tr.timestampMax {
|
|
|
|
tr.timestampMax = typeutil.Timestamp(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tr
|
|
|
|
}
|