Zhen Ye a2ce70d252
fix: ddl framework bug patch (#45290)
issue: #45080, #45274, #45285

- LoadCollection doesn't ignore the ignorable request, for false field
array.
- CreatIndex doesn't ignore the ignorable request, for wrong index.
- index meta is not thread safe.
- lost parameter check of DDL.
- DDL Ack scheduler may get stuck and DDL is block until next incoming
DDL.
- lost parameter checker of ddl

---------

Signed-off-by: chyezh <chyezh@outlook.com>
2025-11-04 22:25:33 +08:00

127 lines
3.8 KiB
Go

package broadcaster
import (
"sort"
"time"
"go.uber.org/zap"
"github.com/milvus-io/milvus/pkg/v2/log"
"github.com/milvus-io/milvus/pkg/v2/util/paramtable"
"github.com/milvus-io/milvus/pkg/v2/util/syncutil"
)
// tombstoneItem is a tombstone item with expired time.
type tombstoneItem struct {
broadcastID uint64
createTime time.Time // the time when the tombstone is created, when recovery, the createTime will be reset to the current time, but it's ok.
}
// tombstoneScheduler is a scheduler for the tombstone.
type tombstoneScheduler struct {
log.Binder
notifier *syncutil.AsyncTaskNotifier[struct{}]
pending chan uint64
bm *broadcastTaskManager
tombstones []tombstoneItem
}
// newTombstoneScheduler creates a new tombstone scheduler.
func newTombstoneScheduler(logger *log.MLogger) *tombstoneScheduler {
ts := &tombstoneScheduler{
notifier: syncutil.NewAsyncTaskNotifier[struct{}](),
pending: make(chan uint64),
}
ts.SetLogger(logger)
return ts
}
// Initialize initializes the tombstone scheduler.
func (s *tombstoneScheduler) Initialize(bm *broadcastTaskManager, tombstoneBroadcastIDs []uint64) {
sort.Slice(tombstoneBroadcastIDs, func(i, j int) bool {
return tombstoneBroadcastIDs[i] < tombstoneBroadcastIDs[j]
})
s.bm = bm
s.tombstones = make([]tombstoneItem, 0, len(tombstoneBroadcastIDs))
for _, broadcastID := range tombstoneBroadcastIDs {
s.tombstones = append(s.tombstones, tombstoneItem{
broadcastID: broadcastID,
createTime: time.Now(),
})
}
go s.background()
}
// AddPending adds a pending tombstone to the scheduler.
func (s *tombstoneScheduler) AddPending(broadcastID uint64) {
select {
case <-s.notifier.Context().Done():
panic("unreachable: tombstone scheduler is closing when adding pending tombstone")
case s.pending <- broadcastID:
}
}
// Close closes the tombstone scheduler.
func (s *tombstoneScheduler) Close() {
s.notifier.Cancel()
s.notifier.BlockUntilFinish()
}
// background is the background goroutine of the tombstone scheduler.
func (s *tombstoneScheduler) background() {
defer func() {
s.notifier.Finish(struct{}{})
s.Logger().Info("tombstone scheduler background exit")
}()
s.Logger().Info("tombstone scheduler background start")
tombstoneGCInterval := paramtable.Get().StreamingCfg.WALBroadcasterTombstoneCheckInternal.GetAsDurationByParse()
ticker := time.NewTicker(tombstoneGCInterval)
defer ticker.Stop()
for {
s.triggerGCTombstone()
select {
case <-s.notifier.Context().Done():
return
case broadcastID := <-s.pending:
s.tombstones = append(s.tombstones, tombstoneItem{
broadcastID: broadcastID,
createTime: time.Now(),
})
case <-ticker.C:
}
}
}
// triggerGCTombstone triggers the garbage collection of the tombstone.
func (s *tombstoneScheduler) triggerGCTombstone() {
maxTombstoneLifetime := paramtable.Get().StreamingCfg.WALBroadcasterTombstoneMaxLifetime.GetAsDurationByParse()
maxTombstoneCount := paramtable.Get().StreamingCfg.WALBroadcasterTombstoneMaxCount.GetAsInt()
expiredTime := time.Now().Add(-maxTombstoneLifetime)
expiredOffset := 0
if len(s.tombstones) > maxTombstoneCount {
expiredOffset = len(s.tombstones) - maxTombstoneCount
}
s.Logger().Info("triggerGCTombstone",
zap.Int("tombstone count", len(s.tombstones)),
zap.Int("expired offset", expiredOffset),
zap.Time("expired time", expiredTime))
for idx, tombstone := range s.tombstones {
// drop tombstone until the expired time or until the expired offset.
if idx >= expiredOffset && tombstone.createTime.After(expiredTime) {
s.tombstones = s.tombstones[idx:]
return
}
if err := s.bm.DropTombstone(s.notifier.Context(), tombstone.broadcastID); err != nil {
s.Logger().Error("failed to drop tombstone", zap.Error(err))
s.tombstones = s.tombstones[idx:]
return
}
}
// all the tombstones are dropped, reset the tombstones.
s.tombstones = make([]tombstoneItem, 0)
}