milvus/internal/datanode/syncmgr/key_lock_dispatcher_test.go
congqixia a68b32134a
fix: Verify sync task target segment and retry if not match (#30500)
See also #27675 #30469

For a sync task, the segment could be compacted during sync task. In
previous implementation, this sync task will hold only the old segment
id as KeyLock, in which case compaction on compacted to segment may run
in parallel with delta sync of this sync task.

This PR introduces sync target segment verification logic. It shall
check target segment lock it's holding beforing actually syncing logic.
If this check failed, sync task shall return`errTargetSegementNotMatch`
error and make manager re-fetch the current target segment id.

Signed-off-by: Congqi Xia <congqi.xia@zilliz.com>
2024-02-05 11:33:43 +08:00

106 lines
2.0 KiB
Go

package syncmgr
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
"go.uber.org/atomic"
)
/*
type mockTask struct {
targetID int64
ch chan struct{}
err error
}
func (t *mockTask) done() {
close(t.ch)
}
func (t *mockTask) CalcTargetSegment() (int64, error) {
return t.targetID, t.err
}
func (t *mockTask) SegmentID() int64 { panic("no implementation") }
func (t *mockTask) Checkpoint() *msgpb.MsgPosition { panic("no implementation") }
func (t *mockTask) StartPosition() *msgpb.MsgPosition { panic("no implementation") }
func (t *mockTask) ChannelName() string { panic("no implementation") }
func (t *mockTask) Run() error {
<-t.ch
return t.err
}
func newMockTask(err error) *mockTask {
return &mockTask{
err: err,
ch: make(chan struct{}),
}
}*/
type KeyLockDispatcherSuite struct {
suite.Suite
}
func (s *KeyLockDispatcherSuite) TestKeyLock() {
d := newKeyLockDispatcher[int64](2)
done := make(chan struct{})
t1 := NewMockTask(s.T())
t1.EXPECT().Run().Run(func() {
<-done
}).Return(nil)
t2 := NewMockTask(s.T())
t2.EXPECT().Run().Return(nil)
sig := atomic.NewBool(false)
d.Submit(1, t1)
go func() {
d.Submit(1, t2)
sig.Store(true)
}()
s.False(sig.Load(), "task 2 will never be submit before task 1 done")
close(done)
s.Eventually(sig.Load, time.Second, time.Millisecond*100)
}
func (s *KeyLockDispatcherSuite) TestCap() {
d := newKeyLockDispatcher[int64](1)
t1 := NewMockTask(s.T())
t2 := NewMockTask(s.T())
done := make(chan struct{})
t1.EXPECT().Run().Run(func() {
<-done
}).Return(nil)
t2.EXPECT().Run().Return(nil)
sig := atomic.NewBool(false)
d.Submit(1, t1)
go func() {
// defer t2.done()
d.Submit(2, t2)
sig.Store(true)
}()
s.False(sig.Load(), "task 2 will never be submit before task 1 done")
close(done)
s.Eventually(sig.Load, time.Second, time.Millisecond*100)
}
func TestKeyLockDispatcher(t *testing.T) {
suite.Run(t, new(KeyLockDispatcherSuite))
}