2021-10-18 10:38:35 +08:00
|
|
|
// 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 datanode
|
|
|
|
|
|
|
|
import (
|
2021-11-15 17:19:10 +08:00
|
|
|
"context"
|
2021-10-19 11:04:34 +08:00
|
|
|
"fmt"
|
|
|
|
"path"
|
|
|
|
"strconv"
|
2021-10-18 10:38:35 +08:00
|
|
|
"sync"
|
|
|
|
|
2023-02-26 11:31:49 +08:00
|
|
|
"github.com/cockroachdb/errors"
|
2023-03-04 23:21:50 +08:00
|
|
|
"github.com/samber/lo"
|
2022-09-16 09:56:47 +08:00
|
|
|
"go.uber.org/atomic"
|
|
|
|
"go.uber.org/zap"
|
2023-08-17 15:44:18 +08:00
|
|
|
"golang.org/x/sync/errgroup"
|
2022-09-16 09:56:47 +08:00
|
|
|
|
2023-06-09 01:28:37 +08:00
|
|
|
"github.com/milvus-io/milvus-proto/go-api/v2/msgpb"
|
2023-03-23 19:43:57 +08:00
|
|
|
"github.com/milvus-io/milvus/internal/datanode/allocator"
|
2021-11-15 17:19:10 +08:00
|
|
|
"github.com/milvus-io/milvus/internal/proto/datapb"
|
2021-10-19 11:04:34 +08:00
|
|
|
"github.com/milvus-io/milvus/internal/proto/etcdpb"
|
|
|
|
"github.com/milvus-io/milvus/internal/storage"
|
2023-04-06 19:14:32 +08:00
|
|
|
"github.com/milvus-io/milvus/pkg/common"
|
|
|
|
"github.com/milvus-io/milvus/pkg/log"
|
|
|
|
"github.com/milvus-io/milvus/pkg/metrics"
|
|
|
|
"github.com/milvus-io/milvus/pkg/util/commonpbutil"
|
2023-05-29 10:21:28 +08:00
|
|
|
"github.com/milvus-io/milvus/pkg/util/merr"
|
2023-04-06 19:14:32 +08:00
|
|
|
"github.com/milvus-io/milvus/pkg/util/metautil"
|
|
|
|
"github.com/milvus-io/milvus/pkg/util/paramtable"
|
|
|
|
"github.com/milvus-io/milvus/pkg/util/retry"
|
|
|
|
"github.com/milvus-io/milvus/pkg/util/timerecord"
|
2023-07-24 10:23:01 +08:00
|
|
|
"github.com/milvus-io/milvus/pkg/util/typeutil"
|
2021-10-18 10:38:35 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// flushManager defines a flush manager signature
|
|
|
|
type flushManager interface {
|
|
|
|
// notify flush manager insert buffer data
|
2023-05-29 10:21:28 +08:00
|
|
|
flushBufferData(data *BufferData, segmentID UniqueID, flushed bool, dropped bool, pos *msgpb.MsgPosition) (*storage.PrimaryKeyStats, error)
|
2021-10-18 10:38:35 +08:00
|
|
|
// notify flush manager del buffer data
|
2023-03-04 23:21:50 +08:00
|
|
|
flushDelData(data *DelDataBuf, segmentID UniqueID, pos *msgpb.MsgPosition) error
|
2023-06-16 14:14:39 +08:00
|
|
|
// isFull return true if the task pool is full
|
|
|
|
isFull() bool
|
2021-10-28 19:16:31 +08:00
|
|
|
// injectFlush injects compaction or other blocking task before flush sync
|
2021-11-24 18:41:16 +08:00
|
|
|
injectFlush(injection *taskInjection, segments ...UniqueID)
|
2021-12-01 10:11:39 +08:00
|
|
|
// startDropping changes flush manager into dropping mode
|
|
|
|
startDropping()
|
|
|
|
// notifyAllFlushed tells flush manager there is not future incoming flush task for drop mode
|
|
|
|
notifyAllFlushed()
|
2021-11-17 10:39:15 +08:00
|
|
|
// close handles resource clean up
|
|
|
|
close()
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// segmentFlushPack contains result to save into meta
|
|
|
|
type segmentFlushPack struct {
|
|
|
|
segmentID UniqueID
|
2021-12-19 20:00:42 +08:00
|
|
|
insertLogs map[UniqueID]*datapb.Binlog
|
|
|
|
statsLogs map[UniqueID]*datapb.Binlog
|
|
|
|
deltaLogs []*datapb.Binlog
|
2023-03-04 23:21:50 +08:00
|
|
|
pos *msgpb.MsgPosition
|
2021-10-19 11:04:34 +08:00
|
|
|
flushed bool
|
2021-11-11 20:56:49 +08:00
|
|
|
dropped bool
|
2021-11-15 17:19:10 +08:00
|
|
|
err error // task execution error, if not nil, notify func should stop datanode
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// notifyMetaFunc notify meta to persistent flush result
|
2021-11-15 17:19:10 +08:00
|
|
|
type notifyMetaFunc func(*segmentFlushPack)
|
2021-10-18 10:38:35 +08:00
|
|
|
|
2021-12-01 10:11:39 +08:00
|
|
|
// flushAndDropFunc notifies meta to flush current state and drop virtual channel
|
|
|
|
type flushAndDropFunc func([]*segmentFlushPack)
|
|
|
|
|
2021-10-18 10:38:35 +08:00
|
|
|
// taskPostFunc clean up function after single flush task done
|
2021-10-25 20:17:34 +08:00
|
|
|
type taskPostFunc func(pack *segmentFlushPack, postInjection postInjectionFunc)
|
|
|
|
|
|
|
|
// postInjectionFunc post injection pack process logic
|
|
|
|
type postInjectionFunc func(pack *segmentFlushPack)
|
2021-10-18 10:38:35 +08:00
|
|
|
|
|
|
|
// make sure implementation
|
|
|
|
var _ flushManager = (*rendezvousFlushManager)(nil)
|
|
|
|
|
2021-12-01 10:11:39 +08:00
|
|
|
// orderFlushQueue keeps the order of task notifyFunc execution in order
|
2021-10-18 10:38:35 +08:00
|
|
|
type orderFlushQueue struct {
|
|
|
|
sync.Once
|
2021-10-19 11:04:34 +08:00
|
|
|
segmentID UniqueID
|
2023-11-03 14:42:17 +08:00
|
|
|
channel string
|
2021-11-24 18:41:16 +08:00
|
|
|
injectCh chan *taskInjection
|
2021-10-25 20:17:34 +08:00
|
|
|
|
2021-10-18 10:38:35 +08:00
|
|
|
// MsgID => flushTask
|
2023-07-24 10:23:01 +08:00
|
|
|
working *typeutil.ConcurrentMap[string, *flushTaskRunner]
|
2021-10-18 10:38:35 +08:00
|
|
|
notifyFunc notifyMetaFunc
|
|
|
|
|
|
|
|
tailMut sync.Mutex
|
|
|
|
tailCh chan struct{}
|
2021-10-25 20:17:34 +08:00
|
|
|
|
|
|
|
injectMut sync.Mutex
|
|
|
|
runningTasks int32
|
|
|
|
postInjection postInjectionFunc
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
2022-01-06 13:38:08 +08:00
|
|
|
// newOrderFlushQueue creates an orderFlushQueue
|
2023-11-03 14:42:17 +08:00
|
|
|
func newOrderFlushQueue(segID UniqueID, channel string, f notifyMetaFunc) *orderFlushQueue {
|
2021-10-25 20:17:34 +08:00
|
|
|
q := &orderFlushQueue{
|
2021-10-19 11:04:34 +08:00
|
|
|
segmentID: segID,
|
2023-11-03 14:42:17 +08:00
|
|
|
channel: channel,
|
2021-10-18 10:38:35 +08:00
|
|
|
notifyFunc: f,
|
2021-11-24 18:41:16 +08:00
|
|
|
injectCh: make(chan *taskInjection, 100),
|
2023-07-24 10:23:01 +08:00
|
|
|
working: typeutil.NewConcurrentMap[string, *flushTaskRunner](),
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
2021-10-25 20:17:34 +08:00
|
|
|
return q
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// init orderFlushQueue use once protect init, init tailCh
|
|
|
|
func (q *orderFlushQueue) init() {
|
|
|
|
q.Once.Do(func() {
|
|
|
|
// new queue acts like tailing task is done
|
|
|
|
q.tailCh = make(chan struct{})
|
|
|
|
close(q.tailCh)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-03-04 23:21:50 +08:00
|
|
|
func (q *orderFlushQueue) getFlushTaskRunner(pos *msgpb.MsgPosition) *flushTaskRunner {
|
2023-07-24 10:23:01 +08:00
|
|
|
t, loaded := q.working.GetOrInsert(getSyncTaskID(pos), newFlushTaskRunner(q.segmentID, q.injectCh))
|
2021-12-01 10:11:39 +08:00
|
|
|
// not loaded means the task runner is new, do initializtion
|
2021-10-18 10:38:35 +08:00
|
|
|
if !loaded {
|
2023-11-03 14:42:17 +08:00
|
|
|
getOrCreateFlushTaskCounter().increase(q.channel)
|
2021-12-01 10:11:39 +08:00
|
|
|
// take over injection if task queue is handling it
|
2021-10-25 20:17:34 +08:00
|
|
|
q.injectMut.Lock()
|
|
|
|
q.runningTasks++
|
|
|
|
q.injectMut.Unlock()
|
2021-12-01 10:11:39 +08:00
|
|
|
// add task to tail
|
2021-10-18 10:38:35 +08:00
|
|
|
q.tailMut.Lock()
|
2021-10-25 20:17:34 +08:00
|
|
|
t.init(q.notifyFunc, q.postTask, q.tailCh)
|
2021-10-18 10:38:35 +08:00
|
|
|
q.tailCh = t.finishSignal
|
|
|
|
q.tailMut.Unlock()
|
2022-10-25 19:31:30 +08:00
|
|
|
log.Info("new flush task runner created and initialized",
|
2023-07-14 15:56:31 +08:00
|
|
|
zap.Int64("segmentID", q.segmentID),
|
2022-07-08 14:50:21 +08:00
|
|
|
zap.String("pos message ID", string(pos.GetMsgID())),
|
|
|
|
)
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2021-12-01 10:11:39 +08:00
|
|
|
// postTask handles clean up work after a task is done
|
2021-10-25 20:17:34 +08:00
|
|
|
func (q *orderFlushQueue) postTask(pack *segmentFlushPack, postInjection postInjectionFunc) {
|
2021-12-01 10:11:39 +08:00
|
|
|
// delete task from working map
|
2023-07-24 10:23:01 +08:00
|
|
|
q.working.GetAndRemove(getSyncTaskID(pack.pos))
|
2023-11-03 14:42:17 +08:00
|
|
|
getOrCreateFlushTaskCounter().decrease(q.channel)
|
2021-12-01 10:11:39 +08:00
|
|
|
// after descreasing working count, check whether flush queue is empty
|
2021-10-25 20:17:34 +08:00
|
|
|
q.injectMut.Lock()
|
|
|
|
q.runningTasks--
|
2021-12-01 10:11:39 +08:00
|
|
|
// set postInjection function if injection is handled in task
|
2021-10-25 20:17:34 +08:00
|
|
|
if postInjection != nil {
|
|
|
|
q.postInjection = postInjection
|
|
|
|
}
|
|
|
|
|
|
|
|
if q.postInjection != nil {
|
|
|
|
q.postInjection(pack)
|
|
|
|
}
|
2022-01-13 18:49:34 +08:00
|
|
|
|
|
|
|
// if flush queue is empty, drain all injection from injectCh
|
|
|
|
if q.runningTasks == 0 {
|
|
|
|
for i := 0; i < len(q.injectCh); i++ {
|
|
|
|
inject := <-q.injectCh
|
|
|
|
go q.handleInject(inject)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-25 20:17:34 +08:00
|
|
|
q.injectMut.Unlock()
|
|
|
|
}
|
|
|
|
|
2021-10-18 10:38:35 +08:00
|
|
|
// enqueueInsertBuffer put insert buffer data into queue
|
2023-03-04 23:21:50 +08:00
|
|
|
func (q *orderFlushQueue) enqueueInsertFlush(task flushInsertTask, binlogs, statslogs map[UniqueID]*datapb.Binlog, flushed bool, dropped bool, pos *msgpb.MsgPosition) {
|
2021-11-11 20:56:49 +08:00
|
|
|
q.getFlushTaskRunner(pos).runFlushInsert(task, binlogs, statslogs, flushed, dropped, pos)
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// enqueueDelBuffer put delete buffer data into queue
|
2023-03-04 23:21:50 +08:00
|
|
|
func (q *orderFlushQueue) enqueueDelFlush(task flushDeleteTask, deltaLogs *DelDataBuf, pos *msgpb.MsgPosition) {
|
2021-10-19 11:04:34 +08:00
|
|
|
q.getFlushTaskRunner(pos).runFlushDel(task, deltaLogs)
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
2021-10-25 20:17:34 +08:00
|
|
|
// inject performs injection for current task queue
|
|
|
|
// send into injectCh in there is running task
|
|
|
|
// or perform injection logic here if there is no injection
|
2021-11-24 18:41:16 +08:00
|
|
|
func (q *orderFlushQueue) inject(inject *taskInjection) {
|
2022-01-13 18:49:34 +08:00
|
|
|
q.injectMut.Lock()
|
|
|
|
defer q.injectMut.Unlock()
|
|
|
|
// check if there are running task(s)
|
|
|
|
// if true, just put injection into injectCh
|
|
|
|
// in case of task misses an injection, the injectCh shall be drained in `postTask`
|
|
|
|
if q.runningTasks > 0 {
|
|
|
|
q.injectCh <- inject
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// otherwise just handle injection here
|
2022-01-27 10:23:40 +08:00
|
|
|
go q.handleInject(inject)
|
2021-10-25 20:17:34 +08:00
|
|
|
}
|
|
|
|
|
2022-01-13 18:49:34 +08:00
|
|
|
func (q *orderFlushQueue) handleInject(inject *taskInjection) {
|
|
|
|
// notify one injection done
|
|
|
|
inject.injectOne()
|
|
|
|
ok := <-inject.injectOver
|
|
|
|
// apply injection
|
|
|
|
if ok {
|
2022-01-27 10:23:40 +08:00
|
|
|
q.injectMut.Lock()
|
|
|
|
defer q.injectMut.Unlock()
|
2022-01-13 18:49:34 +08:00
|
|
|
q.postInjection = inject.postInjection
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-12-01 10:11:39 +08:00
|
|
|
// injectionHandler handles injection for empty flush queue
|
2021-10-25 20:17:34 +08:00
|
|
|
type injectHandler struct {
|
|
|
|
once sync.Once
|
|
|
|
wg sync.WaitGroup
|
|
|
|
done chan struct{}
|
|
|
|
}
|
|
|
|
|
2021-12-01 10:11:39 +08:00
|
|
|
// newInjectHandler create injection handler for flush queue
|
2021-10-25 20:17:34 +08:00
|
|
|
func newInjectHandler(q *orderFlushQueue) *injectHandler {
|
|
|
|
h := &injectHandler{
|
|
|
|
done: make(chan struct{}),
|
|
|
|
}
|
|
|
|
h.wg.Add(1)
|
|
|
|
go h.handleInjection(q)
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *injectHandler) handleInjection(q *orderFlushQueue) {
|
|
|
|
defer h.wg.Done()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case inject := <-q.injectCh:
|
|
|
|
q.tailMut.Lock() //Maybe double check
|
|
|
|
injectDone := make(chan struct{})
|
|
|
|
q.tailCh = injectDone
|
|
|
|
q.tailMut.Unlock()
|
|
|
|
case <-h.done:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *injectHandler) close() {
|
|
|
|
h.once.Do(func() {
|
|
|
|
close(h.done)
|
|
|
|
h.wg.Wait()
|
|
|
|
})
|
|
|
|
}
|
2022-01-13 18:49:34 +08:00
|
|
|
*/
|
2021-10-25 20:17:34 +08:00
|
|
|
|
2021-12-01 10:11:39 +08:00
|
|
|
type dropHandler struct {
|
|
|
|
sync.Mutex
|
|
|
|
dropFlushWg sync.WaitGroup
|
|
|
|
flushAndDrop flushAndDropFunc
|
|
|
|
allFlushed chan struct{}
|
|
|
|
packs []*segmentFlushPack
|
|
|
|
}
|
|
|
|
|
2021-10-18 10:38:35 +08:00
|
|
|
// rendezvousFlushManager makes sure insert & del buf all flushed
|
|
|
|
type rendezvousFlushManager struct {
|
2023-03-23 19:43:57 +08:00
|
|
|
allocator.Allocator
|
2022-03-17 18:03:23 +08:00
|
|
|
storage.ChunkManager
|
2022-10-18 15:33:26 +08:00
|
|
|
Channel
|
2021-10-18 10:38:35 +08:00
|
|
|
|
|
|
|
// segment id => flush queue
|
2023-07-24 10:23:01 +08:00
|
|
|
dispatcher *typeutil.ConcurrentMap[int64, *orderFlushQueue]
|
2021-10-18 10:38:35 +08:00
|
|
|
notifyFunc notifyMetaFunc
|
2021-12-01 10:11:39 +08:00
|
|
|
|
|
|
|
dropping atomic.Bool
|
|
|
|
dropHandler dropHandler
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
2022-01-07 13:57:49 +08:00
|
|
|
// getFlushQueue gets or creates an orderFlushQueue for segment id if not found
|
2021-10-18 10:38:35 +08:00
|
|
|
func (m *rendezvousFlushManager) getFlushQueue(segmentID UniqueID) *orderFlushQueue {
|
2023-11-03 14:42:17 +08:00
|
|
|
newQueue := newOrderFlushQueue(segmentID, m.getChannelName(), m.notifyFunc)
|
2023-07-24 10:23:01 +08:00
|
|
|
queue, _ := m.dispatcher.GetOrInsert(segmentID, newQueue)
|
2021-11-17 10:39:15 +08:00
|
|
|
queue.init()
|
2021-10-18 10:38:35 +08:00
|
|
|
return queue
|
|
|
|
}
|
|
|
|
|
2023-03-04 23:21:50 +08:00
|
|
|
func (m *rendezvousFlushManager) handleInsertTask(segmentID UniqueID, task flushInsertTask, binlogs, statslogs map[UniqueID]*datapb.Binlog, flushed bool, dropped bool, pos *msgpb.MsgPosition) {
|
2022-10-25 19:31:30 +08:00
|
|
|
log.Info("handling insert task",
|
2023-07-14 15:56:31 +08:00
|
|
|
zap.Int64("segmentID", segmentID),
|
2022-07-08 14:50:21 +08:00
|
|
|
zap.Bool("flushed", flushed),
|
|
|
|
zap.Bool("dropped", dropped),
|
|
|
|
zap.Any("position", pos),
|
|
|
|
)
|
2021-12-01 10:11:39 +08:00
|
|
|
// in dropping mode
|
|
|
|
if m.dropping.Load() {
|
|
|
|
r := &flushTaskRunner{
|
|
|
|
WaitGroup: sync.WaitGroup{},
|
|
|
|
segmentID: segmentID,
|
|
|
|
}
|
|
|
|
r.WaitGroup.Add(1) // insert and delete are not bound in drop mode
|
|
|
|
r.runFlushInsert(task, binlogs, statslogs, flushed, dropped, pos)
|
|
|
|
r.WaitGroup.Wait()
|
|
|
|
|
|
|
|
m.dropHandler.Lock()
|
|
|
|
defer m.dropHandler.Unlock()
|
|
|
|
m.dropHandler.packs = append(m.dropHandler.packs, r.getFlushPack())
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// normal mode
|
|
|
|
m.getFlushQueue(segmentID).enqueueInsertFlush(task, binlogs, statslogs, flushed, dropped, pos)
|
|
|
|
}
|
|
|
|
|
2023-03-04 23:21:50 +08:00
|
|
|
func (m *rendezvousFlushManager) handleDeleteTask(segmentID UniqueID, task flushDeleteTask, deltaLogs *DelDataBuf, pos *msgpb.MsgPosition) {
|
2023-07-14 15:56:31 +08:00
|
|
|
log.Info("handling delete task", zap.Int64("segmentID", segmentID))
|
2021-12-01 10:11:39 +08:00
|
|
|
// in dropping mode
|
|
|
|
if m.dropping.Load() {
|
|
|
|
// preventing separate delete, check position exists in queue first
|
|
|
|
q := m.getFlushQueue(segmentID)
|
2023-07-24 10:23:01 +08:00
|
|
|
_, ok := q.working.Get(getSyncTaskID(pos))
|
2021-12-01 10:11:39 +08:00
|
|
|
// if ok, means position insert data already in queue, just handle task in normal mode
|
|
|
|
// if not ok, means the insert buf should be handle in drop mode
|
|
|
|
if !ok {
|
|
|
|
r := &flushTaskRunner{
|
|
|
|
WaitGroup: sync.WaitGroup{},
|
|
|
|
segmentID: segmentID,
|
|
|
|
}
|
|
|
|
r.WaitGroup.Add(1) // insert and delete are not bound in drop mode
|
|
|
|
r.runFlushDel(task, deltaLogs)
|
|
|
|
r.WaitGroup.Wait()
|
|
|
|
|
|
|
|
m.dropHandler.Lock()
|
|
|
|
defer m.dropHandler.Unlock()
|
|
|
|
m.dropHandler.packs = append(m.dropHandler.packs, r.getFlushPack())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// normal mode
|
|
|
|
m.getFlushQueue(segmentID).enqueueDelFlush(task, deltaLogs, pos)
|
|
|
|
}
|
|
|
|
|
2023-05-29 10:21:28 +08:00
|
|
|
func (m *rendezvousFlushManager) serializeBinLog(segmentID, partID int64, data *BufferData, inCodec *storage.InsertCodec) ([]*Blob, map[int64]int, error) {
|
|
|
|
fieldMemorySize := make(map[int64]int)
|
|
|
|
|
2021-10-19 11:04:34 +08:00
|
|
|
if data == nil || data.buffer == nil {
|
2023-05-29 10:21:28 +08:00
|
|
|
return []*Blob{}, fieldMemorySize, nil
|
2021-10-19 11:04:34 +08:00
|
|
|
}
|
|
|
|
|
2022-07-19 21:30:30 +08:00
|
|
|
// get memory size of buffer data
|
|
|
|
for fieldID, fieldData := range data.buffer.Data {
|
|
|
|
fieldMemorySize[fieldID] = fieldData.GetMemorySize()
|
|
|
|
}
|
2021-10-19 11:04:34 +08:00
|
|
|
|
|
|
|
// encode data and convert output data
|
2023-05-29 10:21:28 +08:00
|
|
|
blobs, err := inCodec.Serialize(partID, segmentID, data.buffer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return blobs, fieldMemorySize, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *rendezvousFlushManager) serializePkStatsLog(segmentID int64, flushed bool, data *BufferData, inCodec *storage.InsertCodec) (*Blob, *storage.PrimaryKeyStats, error) {
|
|
|
|
var err error
|
|
|
|
var stats *storage.PrimaryKeyStats
|
|
|
|
|
|
|
|
pkField := getPKField(inCodec.Schema)
|
|
|
|
if pkField == nil {
|
2023-06-21 14:00:42 +08:00
|
|
|
log.Error("No pk field in schema", zap.Int64("segmentID", segmentID), zap.Int64("collectionID", inCodec.Schema.GetID()))
|
2023-05-29 10:21:28 +08:00
|
|
|
return nil, nil, fmt.Errorf("no primary key in meta")
|
|
|
|
}
|
|
|
|
|
|
|
|
var insertData storage.FieldData
|
|
|
|
rowNum := int64(0)
|
|
|
|
if data != nil && data.buffer != nil {
|
|
|
|
insertData = data.buffer.Data[pkField.FieldID]
|
|
|
|
rowNum = int64(insertData.RowNum())
|
|
|
|
if insertData.RowNum() > 0 {
|
|
|
|
// gen stats of buffer insert data
|
|
|
|
stats = storage.NewPrimaryKeyStats(pkField.FieldID, int64(pkField.DataType), rowNum)
|
|
|
|
stats.UpdateByMsgs(insertData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get all stats log as a list, serialize to blob
|
|
|
|
// if flushed
|
|
|
|
if flushed {
|
|
|
|
seg := m.getSegment(segmentID)
|
|
|
|
if seg == nil {
|
|
|
|
return nil, nil, merr.WrapErrSegmentNotFound(segmentID)
|
|
|
|
}
|
|
|
|
|
|
|
|
statsList, oldRowNum := seg.getHistoricalStats(pkField)
|
|
|
|
if stats != nil {
|
|
|
|
statsList = append(statsList, stats)
|
|
|
|
}
|
|
|
|
|
|
|
|
blob, err := inCodec.SerializePkStatsList(statsList, oldRowNum+rowNum)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return blob, stats, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if rowNum == 0 {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
2021-10-19 11:04:34 +08:00
|
|
|
|
2023-05-29 10:21:28 +08:00
|
|
|
// only serialize stats gen from new insert data
|
|
|
|
// if not flush
|
|
|
|
blob, err := inCodec.SerializePkStats(stats, rowNum)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return blob, stats, nil
|
|
|
|
}
|
|
|
|
|
2023-06-16 14:14:39 +08:00
|
|
|
// isFull return true if the task pool is full
|
|
|
|
func (m *rendezvousFlushManager) isFull() bool {
|
2023-11-03 14:42:17 +08:00
|
|
|
return getOrCreateFlushTaskCounter().getOrZero(m.getChannelName()) >=
|
|
|
|
int32(Params.DataNodeCfg.MaxParallelSyncTaskNum.GetAsInt())
|
2023-06-16 14:14:39 +08:00
|
|
|
}
|
|
|
|
|
2023-05-29 10:21:28 +08:00
|
|
|
// flushBufferData notifies flush manager insert buffer data.
|
|
|
|
// This method will be retired on errors. Final errors will be propagated upstream and logged.
|
|
|
|
func (m *rendezvousFlushManager) flushBufferData(data *BufferData, segmentID UniqueID, flushed bool, dropped bool, pos *msgpb.MsgPosition) (*storage.PrimaryKeyStats, error) {
|
|
|
|
field2Insert := make(map[UniqueID]*datapb.Binlog)
|
|
|
|
field2Stats := make(map[UniqueID]*datapb.Binlog)
|
|
|
|
kvs := make(map[string][]byte)
|
|
|
|
|
|
|
|
tr := timerecord.NewTimeRecorder("flushDuration")
|
|
|
|
// get segment info
|
|
|
|
collID, partID, meta, err := m.getSegmentMeta(segmentID, pos)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
inCodec := storage.NewInsertCodecWithSchema(meta)
|
|
|
|
// build bin log blob
|
|
|
|
binLogBlobs, fieldMemorySize, err := m.serializeBinLog(segmentID, partID, data, inCodec)
|
2021-10-19 11:04:34 +08:00
|
|
|
if err != nil {
|
2022-10-31 17:41:34 +08:00
|
|
|
return nil, err
|
2021-10-19 11:04:34 +08:00
|
|
|
}
|
|
|
|
|
2023-05-29 10:21:28 +08:00
|
|
|
// build stats log blob
|
|
|
|
pkStatsBlob, stats, err := m.serializePkStatsLog(segmentID, flushed, data, inCodec)
|
2021-10-19 11:04:34 +08:00
|
|
|
if err != nil {
|
2022-10-31 17:41:34 +08:00
|
|
|
return nil, err
|
2021-10-19 11:04:34 +08:00
|
|
|
}
|
|
|
|
|
2023-05-29 10:21:28 +08:00
|
|
|
// allocate
|
|
|
|
// alloc for stats log if have new stats log and not flushing
|
|
|
|
var logidx int64
|
|
|
|
allocNum := uint32(len(binLogBlobs) + boolToInt(!flushed && pkStatsBlob != nil))
|
|
|
|
if allocNum != 0 {
|
|
|
|
logidx, _, err = m.Alloc(allocNum)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// binlogs
|
|
|
|
for _, blob := range binLogBlobs {
|
2021-10-19 11:04:34 +08:00
|
|
|
fieldID, err := strconv.ParseInt(blob.GetKey(), 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Flush failed ... cannot parse string to fieldID ..", zap.Error(err))
|
2022-10-31 17:41:34 +08:00
|
|
|
return nil, err
|
2021-10-19 11:04:34 +08:00
|
|
|
}
|
|
|
|
|
2022-09-25 15:56:51 +08:00
|
|
|
k := metautil.JoinIDPath(collID, partID, segmentID, fieldID, logidx)
|
2022-08-25 19:32:53 +08:00
|
|
|
// [rootPath]/[insert_log]/key
|
|
|
|
key := path.Join(m.ChunkManager.RootPath(), common.SegmentInsertLogPath, k)
|
2022-03-17 18:03:23 +08:00
|
|
|
kvs[key] = blob.Value[:]
|
2021-12-19 20:00:42 +08:00
|
|
|
field2Insert[fieldID] = &datapb.Binlog{
|
|
|
|
EntriesNum: data.size,
|
2022-09-04 09:05:09 +08:00
|
|
|
TimestampFrom: data.tsFrom,
|
|
|
|
TimestampTo: data.tsTo,
|
2021-12-19 20:00:42 +08:00
|
|
|
LogPath: key,
|
2022-07-19 21:30:30 +08:00
|
|
|
LogSize: int64(fieldMemorySize[fieldID]),
|
2021-12-19 20:00:42 +08:00
|
|
|
}
|
2023-10-11 18:49:33 +08:00
|
|
|
|
|
|
|
logidx += 1
|
2021-10-19 11:04:34 +08:00
|
|
|
}
|
|
|
|
|
2023-05-29 10:21:28 +08:00
|
|
|
// pk stats binlog
|
|
|
|
if pkStatsBlob != nil {
|
|
|
|
fieldID, err := strconv.ParseInt(pkStatsBlob.GetKey(), 10, 64)
|
2022-10-31 17:41:34 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error("Flush failed ... cannot parse string to fieldID ..", zap.Error(err))
|
|
|
|
return nil, err
|
2022-10-20 14:33:27 +08:00
|
|
|
}
|
2021-10-19 11:04:34 +08:00
|
|
|
|
2023-05-29 10:21:28 +08:00
|
|
|
// use storage.FlushedStatsLogIdx as logidx if flushed
|
|
|
|
// else use last idx we allocated
|
|
|
|
var key string
|
|
|
|
if flushed {
|
|
|
|
k := metautil.JoinIDPath(collID, partID, segmentID, fieldID)
|
|
|
|
key = path.Join(m.ChunkManager.RootPath(), common.SegmentStatslogPath, k, storage.CompoundStatsType.LogIdx())
|
|
|
|
} else {
|
|
|
|
k := metautil.JoinIDPath(collID, partID, segmentID, fieldID, logidx)
|
|
|
|
key = path.Join(m.ChunkManager.RootPath(), common.SegmentStatslogPath, k)
|
|
|
|
}
|
2022-10-31 17:41:34 +08:00
|
|
|
|
2023-05-29 10:21:28 +08:00
|
|
|
kvs[key] = pkStatsBlob.Value
|
2022-10-31 17:41:34 +08:00
|
|
|
field2Stats[fieldID] = &datapb.Binlog{
|
2022-10-20 14:33:27 +08:00
|
|
|
EntriesNum: 0,
|
2023-09-21 09:45:27 +08:00
|
|
|
TimestampFrom: 0, // TODO
|
|
|
|
TimestampTo: 0, // TODO,
|
2022-10-20 14:33:27 +08:00
|
|
|
LogPath: key,
|
2023-05-29 10:21:28 +08:00
|
|
|
LogSize: int64(len(pkStatsBlob.Value)),
|
2022-10-20 14:33:27 +08:00
|
|
|
}
|
2021-10-19 11:04:34 +08:00
|
|
|
}
|
|
|
|
|
2021-12-01 10:11:39 +08:00
|
|
|
m.handleInsertTask(segmentID, &flushBufferInsertTask{
|
2022-03-17 18:03:23 +08:00
|
|
|
ChunkManager: m.ChunkManager,
|
|
|
|
data: kvs,
|
2021-11-11 20:56:49 +08:00
|
|
|
}, field2Insert, field2Stats, flushed, dropped, pos)
|
2022-02-28 19:11:55 +08:00
|
|
|
|
2022-11-04 14:25:38 +08:00
|
|
|
metrics.DataNodeEncodeBufferLatency.WithLabelValues(fmt.Sprint(paramtable.GetNodeID())).Observe(float64(tr.ElapseSpan().Milliseconds()))
|
2023-05-29 10:21:28 +08:00
|
|
|
return stats, nil
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// notify flush manager del buffer data
|
|
|
|
func (m *rendezvousFlushManager) flushDelData(data *DelDataBuf, segmentID UniqueID,
|
2023-09-21 09:45:27 +08:00
|
|
|
pos *msgpb.MsgPosition,
|
|
|
|
) error {
|
2021-10-19 11:04:34 +08:00
|
|
|
// del signal with empty data
|
|
|
|
if data == nil || data.delData == nil {
|
2021-12-01 10:11:39 +08:00
|
|
|
m.handleDeleteTask(segmentID, &flushBufferDeleteTask{}, nil, pos)
|
2021-10-19 11:04:34 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-24 09:59:10 +08:00
|
|
|
collID, partID, err := m.getCollectionAndPartitionID(segmentID)
|
2021-10-19 11:04:34 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-10-24 09:59:10 +08:00
|
|
|
delCodec := storage.NewDeleteCodec()
|
2021-10-19 11:04:34 +08:00
|
|
|
|
2021-10-24 09:59:10 +08:00
|
|
|
blob, err := delCodec.Serialize(collID, partID, segmentID, data.delData)
|
2021-10-19 11:04:34 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-03-23 19:43:57 +08:00
|
|
|
logID, err := m.AllocOne()
|
2021-10-19 11:04:34 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Error("failed to alloc ID", zap.Error(err))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-25 15:56:51 +08:00
|
|
|
blobKey := metautil.JoinIDPath(collID, partID, segmentID, logID)
|
2022-08-25 19:32:53 +08:00
|
|
|
blobPath := path.Join(m.ChunkManager.RootPath(), common.SegmentDeltaLogPath, blobKey)
|
2022-03-17 18:03:23 +08:00
|
|
|
kvs := map[string][]byte{blobPath: blob.Value[:]}
|
2021-12-19 20:00:42 +08:00
|
|
|
data.LogSize = int64(len(blob.Value))
|
|
|
|
data.LogPath = blobPath
|
2022-03-02 15:35:55 +08:00
|
|
|
log.Info("delete blob path", zap.String("path", blobPath))
|
2021-12-01 10:11:39 +08:00
|
|
|
m.handleDeleteTask(segmentID, &flushBufferDeleteTask{
|
2022-03-17 18:03:23 +08:00
|
|
|
ChunkManager: m.ChunkManager,
|
|
|
|
data: kvs,
|
2021-10-19 11:04:34 +08:00
|
|
|
}, data, pos)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-25 20:17:34 +08:00
|
|
|
// injectFlush inject process before task finishes
|
2021-11-24 18:41:16 +08:00
|
|
|
func (m *rendezvousFlushManager) injectFlush(injection *taskInjection, segments ...UniqueID) {
|
|
|
|
go injection.waitForInjected()
|
2021-10-25 20:17:34 +08:00
|
|
|
for _, segmentID := range segments {
|
2022-01-27 10:23:40 +08:00
|
|
|
m.getFlushQueue(segmentID).inject(injection)
|
2021-10-25 20:17:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-19 11:04:34 +08:00
|
|
|
// fetch meta info for segment
|
2023-03-04 23:21:50 +08:00
|
|
|
func (m *rendezvousFlushManager) getSegmentMeta(segmentID UniqueID, pos *msgpb.MsgPosition) (UniqueID, UniqueID, *etcdpb.CollectionMeta, error) {
|
2021-10-19 11:04:34 +08:00
|
|
|
if !m.hasSegment(segmentID, true) {
|
2023-09-18 19:35:29 +08:00
|
|
|
return -1, -1, nil, merr.WrapErrSegmentNotFound(segmentID, "segment not found during flush")
|
2021-10-19 11:04:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// fetch meta information of segment
|
|
|
|
collID, partID, err := m.getCollectionAndPartitionID(segmentID)
|
|
|
|
if err != nil {
|
|
|
|
return -1, -1, nil, err
|
|
|
|
}
|
|
|
|
sch, err := m.getCollectionSchema(collID, pos.GetTimestamp())
|
|
|
|
if err != nil {
|
|
|
|
return -1, -1, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
meta := &etcdpb.CollectionMeta{
|
|
|
|
ID: collID,
|
|
|
|
Schema: sch,
|
|
|
|
}
|
|
|
|
return collID, partID, meta, nil
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
2021-12-01 10:11:39 +08:00
|
|
|
// waitForAllTaskQueue waits for all flush queues in dispatcher become empty
|
|
|
|
func (m *rendezvousFlushManager) waitForAllFlushQueue() {
|
|
|
|
var wg sync.WaitGroup
|
2023-07-24 10:23:01 +08:00
|
|
|
m.dispatcher.Range(func(segmentID int64, queue *orderFlushQueue) bool {
|
2021-12-01 10:11:39 +08:00
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
<-queue.tailCh
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
// startDropping changes flush manager into dropping mode
|
|
|
|
func (m *rendezvousFlushManager) startDropping() {
|
|
|
|
m.dropping.Store(true)
|
|
|
|
m.dropHandler.allFlushed = make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
<-m.dropHandler.allFlushed // all needed flush tasks are in flush manager now
|
|
|
|
m.waitForAllFlushQueue() // waits for all the normal flush queue done
|
|
|
|
m.dropHandler.dropFlushWg.Wait() // waits for all drop mode task done
|
|
|
|
m.dropHandler.Lock()
|
|
|
|
defer m.dropHandler.Unlock()
|
2021-12-02 16:39:33 +08:00
|
|
|
// apply injection if any
|
|
|
|
for _, pack := range m.dropHandler.packs {
|
|
|
|
q := m.getFlushQueue(pack.segmentID)
|
|
|
|
// queue will never be nil, sincde getFlushQueue will initialize one if not found
|
|
|
|
q.injectMut.Lock()
|
|
|
|
if q.postInjection != nil {
|
|
|
|
q.postInjection(pack)
|
|
|
|
}
|
|
|
|
q.injectMut.Unlock()
|
|
|
|
}
|
2021-12-01 10:11:39 +08:00
|
|
|
m.dropHandler.flushAndDrop(m.dropHandler.packs) // invoke drop & flush
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *rendezvousFlushManager) notifyAllFlushed() {
|
|
|
|
close(m.dropHandler.allFlushed)
|
|
|
|
}
|
|
|
|
|
2023-03-04 23:21:50 +08:00
|
|
|
func getSyncTaskID(pos *msgpb.MsgPosition) string {
|
2022-12-01 20:35:18 +08:00
|
|
|
// use msgID & timestamp to generate unique taskID, see also #20926
|
|
|
|
return fmt.Sprintf("%s%d", string(pos.GetMsgID()), pos.GetTimestamp())
|
|
|
|
}
|
|
|
|
|
2021-11-17 10:39:15 +08:00
|
|
|
// close cleans up all the left members
|
|
|
|
func (m *rendezvousFlushManager) close() {
|
2023-07-24 10:23:01 +08:00
|
|
|
m.dispatcher.Range(func(segmentID int64, queue *orderFlushQueue) bool {
|
2023-09-21 09:45:27 +08:00
|
|
|
// assertion ok
|
2021-11-17 10:39:15 +08:00
|
|
|
queue.injectMut.Lock()
|
2022-01-13 18:49:34 +08:00
|
|
|
for i := 0; i < len(queue.injectCh); i++ {
|
|
|
|
go queue.handleInject(<-queue.injectCh)
|
2021-11-17 10:39:15 +08:00
|
|
|
}
|
|
|
|
queue.injectMut.Unlock()
|
|
|
|
return true
|
|
|
|
})
|
2023-01-06 14:49:36 +08:00
|
|
|
m.waitForAllFlushQueue()
|
2023-08-16 10:57:32 +08:00
|
|
|
log.Ctx(context.Background()).Info("flush manager closed", zap.Int64("collectionID", m.Channel.getCollectionID()))
|
2021-11-17 10:39:15 +08:00
|
|
|
}
|
|
|
|
|
2021-10-18 10:38:35 +08:00
|
|
|
type flushBufferInsertTask struct {
|
2022-03-17 18:03:23 +08:00
|
|
|
storage.ChunkManager
|
|
|
|
data map[string][]byte
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// flushInsertData implements flushInsertTask
|
|
|
|
func (t *flushBufferInsertTask) flushInsertData() error {
|
2022-09-29 16:18:56 +08:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
2022-03-17 18:03:23 +08:00
|
|
|
if t.ChunkManager != nil && len(t.data) > 0 {
|
2022-02-28 19:11:55 +08:00
|
|
|
tr := timerecord.NewTimeRecorder("insertData")
|
2023-08-17 15:44:18 +08:00
|
|
|
group, ctx := errgroup.WithContext(ctx)
|
|
|
|
for key, data := range t.data {
|
|
|
|
key := key
|
|
|
|
data := data
|
|
|
|
group.Go(func() error {
|
|
|
|
return t.Write(ctx, key, data)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
err := group.Wait()
|
2022-11-04 14:25:38 +08:00
|
|
|
metrics.DataNodeSave2StorageLatency.WithLabelValues(fmt.Sprint(paramtable.GetNodeID()), metrics.InsertLabel).Observe(float64(tr.ElapseSpan().Milliseconds()))
|
2022-09-16 09:56:47 +08:00
|
|
|
if err == nil {
|
|
|
|
for _, d := range t.data {
|
2022-11-04 14:25:38 +08:00
|
|
|
metrics.DataNodeFlushedSize.WithLabelValues(fmt.Sprint(paramtable.GetNodeID()), metrics.InsertLabel).Add(float64(len(d)))
|
2022-09-16 09:56:47 +08:00
|
|
|
}
|
|
|
|
}
|
2022-02-28 19:11:55 +08:00
|
|
|
return err
|
2021-10-19 11:04:34 +08:00
|
|
|
}
|
2021-10-18 10:38:35 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type flushBufferDeleteTask struct {
|
2022-03-17 18:03:23 +08:00
|
|
|
storage.ChunkManager
|
|
|
|
data map[string][]byte
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// flushDeleteData implements flushDeleteTask
|
|
|
|
func (t *flushBufferDeleteTask) flushDeleteData() error {
|
2022-09-29 16:18:56 +08:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
2022-03-17 18:03:23 +08:00
|
|
|
if len(t.data) > 0 && t.ChunkManager != nil {
|
2022-02-28 19:11:55 +08:00
|
|
|
tr := timerecord.NewTimeRecorder("deleteData")
|
2022-09-29 16:18:56 +08:00
|
|
|
err := t.MultiWrite(ctx, t.data)
|
2022-11-04 14:25:38 +08:00
|
|
|
metrics.DataNodeSave2StorageLatency.WithLabelValues(fmt.Sprint(paramtable.GetNodeID()), metrics.DeleteLabel).Observe(float64(tr.ElapseSpan().Milliseconds()))
|
2022-09-16 09:56:47 +08:00
|
|
|
if err == nil {
|
|
|
|
for _, d := range t.data {
|
2022-11-04 14:25:38 +08:00
|
|
|
metrics.DataNodeFlushedSize.WithLabelValues(fmt.Sprint(paramtable.GetNodeID()), metrics.DeleteLabel).Add(float64(len(d)))
|
2022-09-16 09:56:47 +08:00
|
|
|
}
|
|
|
|
}
|
2022-02-28 19:11:55 +08:00
|
|
|
return err
|
2021-10-19 11:04:34 +08:00
|
|
|
}
|
2021-10-18 10:38:35 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRendezvousFlushManager create rendezvousFlushManager with provided allocator and kv
|
2023-03-23 19:43:57 +08:00
|
|
|
func NewRendezvousFlushManager(allocator allocator.Allocator, cm storage.ChunkManager, channel Channel, f notifyMetaFunc, drop flushAndDropFunc) *rendezvousFlushManager {
|
2021-12-01 10:11:39 +08:00
|
|
|
fm := &rendezvousFlushManager{
|
2023-03-23 19:43:57 +08:00
|
|
|
Allocator: allocator,
|
|
|
|
ChunkManager: cm,
|
|
|
|
notifyFunc: f,
|
|
|
|
Channel: channel,
|
2021-12-01 10:11:39 +08:00
|
|
|
dropHandler: dropHandler{
|
|
|
|
flushAndDrop: drop,
|
|
|
|
},
|
2023-07-24 10:23:01 +08:00
|
|
|
dispatcher: typeutil.NewConcurrentMap[int64, *orderFlushQueue](),
|
2021-12-01 10:11:39 +08:00
|
|
|
}
|
|
|
|
// start with normal mode
|
|
|
|
fm.dropping.Store(false)
|
|
|
|
return fm
|
|
|
|
}
|
|
|
|
|
|
|
|
func getFieldBinlogs(fieldID UniqueID, binlogs []*datapb.FieldBinlog) *datapb.FieldBinlog {
|
|
|
|
for _, binlog := range binlogs {
|
|
|
|
if fieldID == binlog.GetFieldID() {
|
|
|
|
return binlog
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func dropVirtualChannelFunc(dsService *dataSyncService, opts ...retry.Option) flushAndDropFunc {
|
|
|
|
return func(packs []*segmentFlushPack) {
|
|
|
|
req := &datapb.DropVirtualChannelRequest{
|
2022-10-21 15:57:28 +08:00
|
|
|
Base: commonpbutil.NewMsgBase(
|
2023-09-21 09:45:27 +08:00
|
|
|
commonpbutil.WithMsgType(0), // TODO msg type
|
|
|
|
commonpbutil.WithMsgID(0), // TODO msg id
|
2023-09-27 11:07:25 +08:00
|
|
|
commonpbutil.WithSourceID(dsService.serverID),
|
2022-10-21 15:57:28 +08:00
|
|
|
),
|
2021-12-01 10:11:39 +08:00
|
|
|
ChannelName: dsService.vchannelName,
|
|
|
|
}
|
|
|
|
|
|
|
|
segmentPack := make(map[UniqueID]*datapb.DropVirtualChannelSegment)
|
|
|
|
for _, pack := range packs {
|
|
|
|
segment, has := segmentPack[pack.segmentID]
|
|
|
|
if !has {
|
|
|
|
segment = &datapb.DropVirtualChannelSegment{
|
|
|
|
SegmentID: pack.segmentID,
|
|
|
|
CollectionID: dsService.collectionID,
|
|
|
|
}
|
|
|
|
|
|
|
|
segmentPack[pack.segmentID] = segment
|
|
|
|
}
|
|
|
|
for k, v := range pack.insertLogs {
|
|
|
|
fieldBinlogs := getFieldBinlogs(k, segment.Field2BinlogPaths)
|
|
|
|
if fieldBinlogs == nil {
|
|
|
|
segment.Field2BinlogPaths = append(segment.Field2BinlogPaths, &datapb.FieldBinlog{
|
|
|
|
FieldID: k,
|
2021-12-19 20:00:42 +08:00
|
|
|
Binlogs: []*datapb.Binlog{v},
|
2021-12-01 10:11:39 +08:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
fieldBinlogs.Binlogs = append(fieldBinlogs.Binlogs, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for k, v := range pack.statsLogs {
|
|
|
|
fieldStatsLogs := getFieldBinlogs(k, segment.Field2StatslogPaths)
|
|
|
|
if fieldStatsLogs == nil {
|
|
|
|
segment.Field2StatslogPaths = append(segment.Field2StatslogPaths, &datapb.FieldBinlog{
|
|
|
|
FieldID: k,
|
2021-12-19 20:00:42 +08:00
|
|
|
Binlogs: []*datapb.Binlog{v},
|
2021-12-01 10:11:39 +08:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
fieldStatsLogs.Binlogs = append(fieldStatsLogs.Binlogs, v)
|
|
|
|
}
|
|
|
|
}
|
2021-12-19 20:00:42 +08:00
|
|
|
segment.Deltalogs = append(segment.Deltalogs, &datapb.FieldBinlog{
|
|
|
|
Binlogs: pack.deltaLogs,
|
|
|
|
})
|
2022-10-18 15:33:26 +08:00
|
|
|
updates, _ := dsService.channel.getSegmentStatisticsUpdates(pack.segmentID)
|
2021-12-01 10:11:39 +08:00
|
|
|
segment.NumOfRows = updates.GetNumRows()
|
|
|
|
if pack.pos != nil {
|
|
|
|
if segment.CheckPoint == nil || pack.pos.Timestamp > segment.CheckPoint.Timestamp {
|
|
|
|
segment.CheckPoint = pack.pos
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-18 15:33:26 +08:00
|
|
|
startPos := dsService.channel.listNewSegmentsStartPositions()
|
2021-12-01 10:11:39 +08:00
|
|
|
// start positions for all new segments
|
2022-10-18 14:07:25 +08:00
|
|
|
for _, pos := range startPos {
|
2021-12-01 10:11:39 +08:00
|
|
|
segment, has := segmentPack[pos.GetSegmentID()]
|
|
|
|
if !has {
|
|
|
|
segment = &datapb.DropVirtualChannelSegment{
|
|
|
|
SegmentID: pos.GetSegmentID(),
|
|
|
|
CollectionID: dsService.collectionID,
|
|
|
|
}
|
|
|
|
|
|
|
|
segmentPack[pos.GetSegmentID()] = segment
|
|
|
|
}
|
|
|
|
segment.StartPosition = pos.GetStartPosition()
|
|
|
|
}
|
|
|
|
|
2022-06-20 13:40:12 +08:00
|
|
|
// assign segments to request
|
|
|
|
segments := make([]*datapb.DropVirtualChannelSegment, 0, len(segmentPack))
|
|
|
|
for _, segment := range segmentPack {
|
|
|
|
segments = append(segments, segment)
|
|
|
|
}
|
|
|
|
req.Segments = segments
|
|
|
|
|
2021-12-01 10:11:39 +08:00
|
|
|
err := retry.Do(context.Background(), func() error {
|
2023-10-13 09:55:34 +08:00
|
|
|
err := dsService.broker.DropVirtualChannel(context.Background(), req)
|
|
|
|
if err != nil {
|
|
|
|
// meta error, datanode handles a virtual channel does not belong here
|
|
|
|
if errors.Is(err, merr.ErrChannelNotFound) {
|
|
|
|
log.Warn("meta error found, skip sync and start to drop virtual channel", zap.String("channel", dsService.vchannelName))
|
|
|
|
return nil
|
|
|
|
}
|
2023-10-11 12:43:33 +08:00
|
|
|
return err
|
2022-05-27 16:20:00 +08:00
|
|
|
}
|
2022-10-18 15:33:26 +08:00
|
|
|
dsService.channel.transferNewSegments(lo.Map(startPos, func(pos *datapb.SegmentStartPosition, _ int) UniqueID {
|
2022-10-18 14:07:25 +08:00
|
|
|
return pos.GetSegmentID()
|
|
|
|
}))
|
2021-12-01 10:11:39 +08:00
|
|
|
return nil
|
|
|
|
}, opts...)
|
|
|
|
if err != nil {
|
|
|
|
log.Warn("failed to DropVirtualChannel", zap.String("channel", dsService.vchannelName), zap.Error(err))
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
for segID := range segmentPack {
|
2022-10-18 15:33:26 +08:00
|
|
|
dsService.channel.segmentFlushed(segID)
|
2021-12-01 10:11:39 +08:00
|
|
|
dsService.flushingSegCache.Remove(segID)
|
|
|
|
}
|
2021-10-18 10:38:35 +08:00
|
|
|
}
|
|
|
|
}
|
2021-11-15 17:19:10 +08:00
|
|
|
|
|
|
|
func flushNotifyFunc(dsService *dataSyncService, opts ...retry.Option) notifyMetaFunc {
|
|
|
|
return func(pack *segmentFlushPack) {
|
|
|
|
if pack.err != nil {
|
2021-12-20 12:22:43 +08:00
|
|
|
log.Error("flush pack with error, DataNode quit now", zap.Error(pack.err))
|
2021-11-15 17:19:10 +08:00
|
|
|
// TODO silverxia change to graceful stop datanode
|
|
|
|
panic(pack.err)
|
|
|
|
}
|
2022-01-10 15:45:34 +08:00
|
|
|
|
|
|
|
var (
|
|
|
|
fieldInsert = []*datapb.FieldBinlog{}
|
|
|
|
fieldStats = []*datapb.FieldBinlog{}
|
|
|
|
deltaInfos = make([]*datapb.FieldBinlog, 1)
|
|
|
|
checkPoints = []*datapb.CheckPoint{}
|
|
|
|
)
|
|
|
|
|
2021-11-15 17:19:10 +08:00
|
|
|
for k, v := range pack.insertLogs {
|
2021-12-19 20:00:42 +08:00
|
|
|
fieldInsert = append(fieldInsert, &datapb.FieldBinlog{FieldID: k, Binlogs: []*datapb.Binlog{v}})
|
2021-11-15 17:19:10 +08:00
|
|
|
}
|
|
|
|
for k, v := range pack.statsLogs {
|
2021-12-19 20:00:42 +08:00
|
|
|
fieldStats = append(fieldStats, &datapb.FieldBinlog{FieldID: k, Binlogs: []*datapb.Binlog{v}})
|
2021-11-15 17:19:10 +08:00
|
|
|
}
|
2022-01-10 15:45:34 +08:00
|
|
|
deltaInfos[0] = &datapb.FieldBinlog{Binlogs: pack.deltaLogs}
|
2021-11-15 17:19:10 +08:00
|
|
|
|
|
|
|
// only current segment checkpoint info,
|
2022-10-18 15:33:26 +08:00
|
|
|
updates, _ := dsService.channel.getSegmentStatisticsUpdates(pack.segmentID)
|
2021-11-15 17:19:10 +08:00
|
|
|
checkPoints = append(checkPoints, &datapb.CheckPoint{
|
|
|
|
SegmentID: pack.segmentID,
|
2023-02-27 10:41:46 +08:00
|
|
|
// this shouldn't be used because we are not sure this is aligned
|
2021-11-15 17:19:10 +08:00
|
|
|
NumOfRows: updates.GetNumRows(),
|
|
|
|
Position: pack.pos,
|
|
|
|
})
|
|
|
|
|
2022-10-18 15:33:26 +08:00
|
|
|
startPos := dsService.channel.listNewSegmentsStartPositions()
|
2021-11-17 17:57:11 +08:00
|
|
|
|
2022-03-02 15:35:55 +08:00
|
|
|
log.Info("SaveBinlogPath",
|
2021-11-15 17:19:10 +08:00
|
|
|
zap.Int64("SegmentID", pack.segmentID),
|
|
|
|
zap.Int64("CollectionID", dsService.collectionID),
|
2021-11-17 17:57:11 +08:00
|
|
|
zap.Any("startPos", startPos),
|
2023-01-06 14:49:36 +08:00
|
|
|
zap.Any("checkPoints", checkPoints),
|
2021-11-15 17:19:10 +08:00
|
|
|
zap.Int("Length of Field2BinlogPaths", len(fieldInsert)),
|
|
|
|
zap.Int("Length of Field2Stats", len(fieldStats)),
|
2022-01-10 15:45:34 +08:00
|
|
|
zap.Int("Length of Field2Deltalogs", len(deltaInfos[0].GetBinlogs())),
|
2021-11-25 09:43:15 +08:00
|
|
|
zap.String("vChannelName", dsService.vchannelName),
|
2021-11-15 17:19:10 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
req := &datapb.SaveBinlogPathsRequest{
|
2022-10-21 15:57:28 +08:00
|
|
|
Base: commonpbutil.NewMsgBase(
|
|
|
|
commonpbutil.WithMsgType(0),
|
|
|
|
commonpbutil.WithMsgID(0),
|
2023-09-27 11:07:25 +08:00
|
|
|
commonpbutil.WithSourceID(paramtable.GetNodeID()),
|
2022-10-21 15:57:28 +08:00
|
|
|
),
|
2021-11-15 17:19:10 +08:00
|
|
|
SegmentID: pack.segmentID,
|
|
|
|
CollectionID: dsService.collectionID,
|
|
|
|
Field2BinlogPaths: fieldInsert,
|
|
|
|
Field2StatslogPaths: fieldStats,
|
|
|
|
Deltalogs: deltaInfos,
|
|
|
|
|
|
|
|
CheckPoints: checkPoints,
|
|
|
|
|
2021-11-17 17:57:11 +08:00
|
|
|
StartPositions: startPos,
|
2021-11-15 17:19:10 +08:00
|
|
|
Flushed: pack.flushed,
|
|
|
|
Dropped: pack.dropped,
|
2023-06-21 21:26:42 +08:00
|
|
|
Channel: dsService.vchannelName,
|
2021-11-15 17:19:10 +08:00
|
|
|
}
|
|
|
|
err := retry.Do(context.Background(), func() error {
|
2023-10-13 09:55:34 +08:00
|
|
|
err := dsService.broker.SaveBinlogPaths(context.Background(), req)
|
2022-07-20 15:06:30 +08:00
|
|
|
// Segment not found during stale segment flush. Segment might get compacted already.
|
|
|
|
// Stop retry and still proceed to the end, ignoring this error.
|
2023-10-07 11:29:32 +08:00
|
|
|
if !pack.flushed && errors.Is(err, merr.ErrSegmentNotFound) {
|
2022-07-20 15:06:30 +08:00
|
|
|
log.Warn("stale segment not found, could be compacted",
|
2023-07-14 15:56:31 +08:00
|
|
|
zap.Int64("segmentID", pack.segmentID))
|
2022-07-20 15:06:30 +08:00
|
|
|
log.Warn("failed to SaveBinlogPaths",
|
2023-07-14 15:56:31 +08:00
|
|
|
zap.Int64("segmentID", pack.segmentID),
|
2023-10-07 11:29:32 +08:00
|
|
|
zap.Error(err))
|
2023-07-13 14:12:29 +08:00
|
|
|
return nil
|
2022-07-20 15:06:30 +08:00
|
|
|
}
|
2022-05-27 16:20:00 +08:00
|
|
|
// meta error, datanode handles a virtual channel does not belong here
|
2023-10-07 11:29:32 +08:00
|
|
|
if errors.IsAny(err, merr.ErrSegmentNotFound, merr.ErrChannelNotFound) {
|
2022-05-27 16:20:00 +08:00
|
|
|
log.Warn("meta error found, skip sync and start to drop virtual channel", zap.String("channel", dsService.vchannelName))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-10-07 11:29:32 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2021-11-15 17:19:10 +08:00
|
|
|
}
|
2022-10-18 14:07:25 +08:00
|
|
|
|
2022-10-18 15:33:26 +08:00
|
|
|
dsService.channel.transferNewSegments(lo.Map(startPos, func(pos *datapb.SegmentStartPosition, _ int) UniqueID {
|
2022-10-18 14:07:25 +08:00
|
|
|
return pos.GetSegmentID()
|
|
|
|
}))
|
2021-11-15 17:19:10 +08:00
|
|
|
return nil
|
|
|
|
}, opts...)
|
|
|
|
if err != nil {
|
2022-07-20 15:06:30 +08:00
|
|
|
log.Warn("failed to SaveBinlogPaths",
|
2023-07-14 15:56:31 +08:00
|
|
|
zap.Int64("segmentID", pack.segmentID),
|
2022-07-20 15:06:30 +08:00
|
|
|
zap.Error(err))
|
2021-11-15 17:19:10 +08:00
|
|
|
// TODO change to graceful stop
|
|
|
|
panic(err)
|
|
|
|
}
|
2023-04-17 10:58:30 +08:00
|
|
|
if pack.dropped {
|
|
|
|
dsService.channel.removeSegments(pack.segmentID)
|
|
|
|
} else if pack.flushed {
|
2022-10-18 15:33:26 +08:00
|
|
|
dsService.channel.segmentFlushed(pack.segmentID)
|
2021-11-15 17:19:10 +08:00
|
|
|
}
|
2023-01-06 14:49:36 +08:00
|
|
|
|
|
|
|
if dsService.flushListener != nil {
|
|
|
|
dsService.flushListener <- pack
|
|
|
|
}
|
2021-11-15 17:19:10 +08:00
|
|
|
dsService.flushingSegCache.Remove(req.GetSegmentID())
|
2022-11-10 22:13:04 +08:00
|
|
|
dsService.channel.evictHistoryInsertBuffer(req.GetSegmentID(), pack.pos)
|
|
|
|
dsService.channel.evictHistoryDeleteBuffer(req.GetSegmentID(), pack.pos)
|
2023-06-16 14:14:39 +08:00
|
|
|
segment := dsService.channel.getSegment(req.GetSegmentID())
|
2023-08-07 09:59:07 +08:00
|
|
|
dsService.channel.updateSingleSegmentMemorySize(req.GetSegmentID())
|
2023-06-16 14:14:39 +08:00
|
|
|
segment.setSyncing(false)
|
2023-05-29 10:21:28 +08:00
|
|
|
// dsService.channel.saveBinlogPath(fieldStats)
|
2021-11-15 17:19:10 +08:00
|
|
|
}
|
|
|
|
}
|