Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions logservice/logpuller/priority_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ type regionPriorityTask struct {
priority regionTaskPriority
}

// NewRegionPriorityTask creates a new priority task for region
func NewRegionPriorityTask(regionInfo regionInfo, currentTs, sequence uint64) *regionPriorityTask {
func newRegionPriorityTask(regionInfo regionInfo, currentTs, sequence uint64) *regionPriorityTask {
task := &regionPriorityTask{
sequence: sequence,
heapIndex: 0, // 0 means not in heap
Expand All @@ -61,11 +60,6 @@ func (pt *regionPriorityTask) updateRegion(regionInfo regionInfo, currentTs uint
pt.priority = priority
}

// GetRegionInfo returns the underlying regionInfo
func (pt *regionPriorityTask) GetRegionInfo() regionInfo {
return pt.regionInfo
}

func (pt *regionPriorityTask) canUseMaxWindow() bool {
return pt.priority != normalRegionPriority
}
Expand Down
18 changes: 9 additions & 9 deletions logservice/logpuller/priority_task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ func TestRegionPriorityTaskQueueOrder(t *testing.T) {
currentTime := time.Now()
currentTs := oracle.GoTimeToTS(currentTime)

normalTask := NewRegionPriorityTask(
normalTask := newRegionPriorityTask(
newPriorityTestRegion(1, oracle.GoTimeToTS(currentTime.Add(-time.Hour)), false),
currentTs, 3,
)
lowLagTask := NewRegionPriorityTask(
lowLagTask := newRegionPriorityTask(
newPriorityTestRegion(2, oracle.GoTimeToTS(currentTime.Add(-10*time.Minute)), false),
currentTs, 2,
)
initializedTask := NewRegionPriorityTask(
initializedTask := newRegionPriorityTask(
newPriorityTestRegion(3, oracle.GoTimeToTS(currentTime.Add(-time.Hour)), true),
currentTs, 1,
)
Expand All @@ -78,8 +78,8 @@ func TestRegionPriorityTaskFIFOWithinPriority(t *testing.T) {
currentTs := oracle.GoTimeToTS(currentTime)
checkpointTs := oracle.GoTimeToTS(currentTime.Add(-time.Hour))

first := NewRegionPriorityTask(newPriorityTestRegion(1, checkpointTs, false), currentTs, 1)
second := NewRegionPriorityTask(newPriorityTestRegion(2, checkpointTs, false), currentTs, 2)
first := newRegionPriorityTask(newPriorityTestRegion(1, checkpointTs, false), currentTs, 1)
second := newRegionPriorityTask(newPriorityTestRegion(2, checkpointTs, false), currentTs, 2)

require.True(t, queue.Push(second))
require.True(t, queue.Push(first))
Expand All @@ -96,17 +96,17 @@ func TestRegionPriorityTaskLowLagBoundary(t *testing.T) {
currentTime := time.Now()
currentTs := oracle.GoTimeToTS(currentTime)

belowThreshold := NewRegionPriorityTask(newPriorityTestRegion(
belowThreshold := newRegionPriorityTask(newPriorityTestRegion(
1,
oracle.GoTimeToTS(currentTime.Add(-lowLagRegionThreshold+time.Millisecond)),
false,
), currentTs, 1)
atThreshold := NewRegionPriorityTask(newPriorityTestRegion(
atThreshold := newRegionPriorityTask(newPriorityTestRegion(
2,
oracle.GoTimeToTS(currentTime.Add(-lowLagRegionThreshold)),
false,
), currentTs, 2)
futureCheckpoint := NewRegionPriorityTask(newPriorityTestRegion(
futureCheckpoint := newRegionPriorityTask(newPriorityTestRegion(
3,
oracle.GoTimeToTS(currentTime.Add(time.Second)),
false,
Expand All @@ -121,7 +121,7 @@ func TestRegionPriorityTaskRefreshesPriorityBetweenStages(t *testing.T) {
checkpointTime := time.Now()
checkpointTs := oracle.GoTimeToTS(checkpointTime)
region := newPriorityTestRegion(1, checkpointTs, false)
task := NewRegionPriorityTask(region, oracle.GoTimeToTS(checkpointTime.Add(time.Minute)), 1)
task := newRegionPriorityTask(region, oracle.GoTimeToTS(checkpointTime.Add(time.Minute)), 1)
require.Equal(t, lowLagRegionPriority, task.priority)

task.updateRegion(region, oracle.GoTimeToTS(checkpointTime.Add(time.Hour)))
Expand Down
5 changes: 2 additions & 3 deletions logservice/logpuller/region_admission_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ func createTestRegionInfo(subID SubscriptionID, regionID uint64) regionInfo {
span,
nil,
&subscribedSpan{subID: subID, startTs: 100, span: span},
false,
)
}

Expand All @@ -55,7 +54,7 @@ func submitRegionForAdmission(
currentTs uint64,
) {
t.Helper()
task := NewRegionPriorityTask(region, currentTs, region.verID.GetID())
task := newRegionPriorityTask(region, currentTs, region.verID.GetID())
require.True(t, controller.submit(task))
}

Expand Down Expand Up @@ -193,7 +192,7 @@ func TestRegionAdmissionControllerClose(t *testing.T) {
controller := newRegionAdmissionController(1, 1)
controller.close()
region := prepareRegionForAdmission(createTestRegionInfo(1, 1), 1)
require.False(t, controller.submit(NewRegionPriorityTask(region, 1, 1)))
require.False(t, controller.submit(newRegionPriorityTask(region, 1, 1)))

_, err := controller.pop(context.Background(), nil)
require.ErrorIs(t, err, context.Canceled)
Expand Down
1 change: 0 additions & 1 deletion logservice/logpuller/region_event_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ func TestHandleEventEntryEventOutOfOrder(t *testing.T) {
span,
&tikv.RPCContext{},
subSpan,
false,
)
region.lockedRangeState = &regionlock.LockedRangeState{}
state := newRegionFeedState(region, 1, worker, nil)
Expand Down
60 changes: 39 additions & 21 deletions logservice/logpuller/region_failure_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,45 @@ var (

// regionFailureHandler handles failed regions and owns retry and reschedule decisions.
type regionFailureHandler struct {
cache *errCache
client *subscriptionClient
cache *errCache
regionCache *tikv.RegionCache

onTableDrained func(*subscribedSpan)
scheduleRegionRequest func(context.Context, regionInfo)
scheduleRangeRequest func(context.Context, rangeTask)
}

func newRegionFailureHandler(client *subscriptionClient) *regionFailureHandler {
func newRegionFailureHandler(
regionCache *tikv.RegionCache,
onTableDrained func(*subscribedSpan),
scheduleRegionRequest func(context.Context, regionInfo),
scheduleRangeRequest func(context.Context, rangeTask),
) *regionFailureHandler {
return &regionFailureHandler{
cache: newErrCache(),
client: client,
cache: newErrCache(),
regionCache: regionCache,
onTableDrained: onTableDrained,
scheduleRegionRequest: scheduleRegionRequest,
scheduleRangeRequest: scheduleRangeRequest,
}
}

func (r *regionFailureHandler) retryRange(ctx context.Context, errInfo regionErrorInfo) {
r.scheduleRangeRequest(ctx, rangeTask{
span: errInfo.span,
subscribedSpan: errInfo.subscribedSpan,
wasInitialized: errInfo.wasInitialized,
})
}

// Report admits a region failure into the recovery pipeline. It releases the
// corresponding range lock before enqueueing the failure so new range tasks are
// not blocked by stale region ownership.
func (r *regionFailureHandler) Report(errInfo regionErrorInfo) {
if errInfo.subscribedSpan.rangeLock.UnlockRange(
errInfo.span.StartKey, errInfo.span.EndKey,
errInfo.verID.GetID(), errInfo.verID.GetVer(), errInfo.resolvedTs()) {
r.client.onTableDrained(errInfo.subscribedSpan)
r.onTableDrained(errInfo.subscribedSpan)
return
}
r.cache.add(errInfo)
Expand All @@ -72,7 +92,6 @@ func (r *regionFailureHandler) Run(ctx context.Context) error {
for {
select {
case <-ctx.Done():
log.Info("subscription client handle errors and exit")
return ctx.Err()
case errInfo := <-r.cache.errCh:
if err := r.handleError(ctx, errInfo); err != nil {
Expand Down Expand Up @@ -100,28 +119,28 @@ func (r *regionFailureHandler) handleError(ctx context.Context, errInfo regionEr
innerErr := eerr.err
if notLeader := innerErr.GetNotLeader(); notLeader != nil {
metricFeedNotLeaderCounter.Inc()
r.client.regionCache.UpdateLeader(errInfo.verID, notLeader.GetLeader(), errInfo.rpcCtx.AccessIdx)
r.client.scheduleRegionRequest(ctx, errInfo.regionInfo)
r.regionCache.UpdateLeader(errInfo.verID, notLeader.GetLeader(), errInfo.rpcCtx.AccessIdx)
r.scheduleRegionRequest(ctx, errInfo.regionInfo)
return nil
}
if innerErr.GetEpochNotMatch() != nil {
metricFeedEpochNotMatchCounter.Inc()
r.client.scheduleRangeRequest(ctx, errInfo.span, errInfo.subscribedSpan, errInfo.filterLoop, errInfo.wasInitialized)
r.retryRange(ctx, errInfo)
return nil
}
if innerErr.GetRegionNotFound() != nil {
metricFeedRegionNotFoundCounter.Inc()
r.client.scheduleRangeRequest(ctx, errInfo.span, errInfo.subscribedSpan, errInfo.filterLoop, errInfo.wasInitialized)
r.retryRange(ctx, errInfo)
return nil
}
if innerErr.GetCongested() != nil {
metricKvCongestedCounter.Inc()
r.client.scheduleRegionRequest(ctx, errInfo.regionInfo)
r.scheduleRegionRequest(ctx, errInfo.regionInfo)
return nil
}
if innerErr.GetServerIsBusy() != nil {
metricKvIsBusyCounter.Inc()
r.client.scheduleRegionRequest(ctx, errInfo.regionInfo)
r.scheduleRegionRequest(ctx, errInfo.regionInfo)
return nil
}
if duplicated := innerErr.GetDuplicateRequest(); duplicated != nil {
Expand All @@ -140,31 +159,31 @@ func (r *regionFailureHandler) handleError(ctx context.Context, errInfo regionEr
zap.Uint64("subscriptionID", uint64(errInfo.subscribedSpan.subID)),
zap.Stringer("error", innerErr))
metricFeedUnknownErrorCounter.Inc()
r.client.scheduleRegionRequest(ctx, errInfo.regionInfo)
r.scheduleRegionRequest(ctx, errInfo.regionInfo)
return nil
case *rpcCtxUnavailableErr:
metricFeedRPCCtxUnavailable.Inc()
r.client.scheduleRangeRequest(ctx, errInfo.span, errInfo.subscribedSpan, errInfo.filterLoop, errInfo.wasInitialized)
r.retryRange(ctx, errInfo)
return nil
case *getStoreErr:
metricGetStoreErr.Inc()
bo := tikv.NewBackoffer(ctx, tikvRequestMaxBackoff)
// cannot get the store the region belongs to, so we need to reload the region.
r.client.regionCache.OnSendFail(bo, errInfo.rpcCtx, true, err)
r.client.scheduleRangeRequest(ctx, errInfo.span, errInfo.subscribedSpan, errInfo.filterLoop, errInfo.wasInitialized)
r.regionCache.OnSendFail(bo, errInfo.rpcCtx, true, err)
r.retryRange(ctx, errInfo)
return nil
case *storeStreamErr:
metricStoreSendRequestErr.Inc()
bo := tikv.NewBackoffer(ctx, tikvRequestMaxBackoff)
r.client.regionCache.OnSendFail(bo, errInfo.rpcCtx, regionScheduleReload, err)
r.client.scheduleRegionRequest(ctx, errInfo.regionInfo)
r.regionCache.OnSendFail(bo, errInfo.rpcCtx, regionScheduleReload, err)
r.scheduleRegionRequest(ctx, errInfo.regionInfo)
return nil
case *requestCancelledErr:
// the corresponding subscription has been unsubscribed, just ignore.
return nil
default:
// TODO(qupeng): for some errors it's better to just deregister the region from TiKVs.
log.Warn("subscription client meets an internal error, fail the changefeed",
log.Warn("region failure cannot be recovered, fail the changefeed",
zap.Uint64("subscriptionID", uint64(errInfo.subscribedSpan.subID)),
zap.Error(err))
return err
Expand Down Expand Up @@ -223,7 +242,6 @@ func (e *errCache) dispatchBatch(ctx context.Context, limit int) (int, error) {
for _, errInfo := range batch {
select {
case <-ctx.Done():
log.Info("subscription client dispatch err cache done")
return 0, ctx.Err()
case e.errCh <- errInfo:
}
Expand Down
Loading