Skip to content

Commit 009fbf2

Browse files
committed
statistics: handle deleted tables correctly in the PQ
Signed-off-by: Rustin170506 <[email protected]>
1 parent 6e22b8c commit 009fbf2

File tree

4 files changed

+60
-1
lines changed

4 files changed

+60
-1
lines changed

pkg/statistics/handle/autoanalyze/priorityqueue/analysis_job_factory.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ func (f *AnalysisJobFactory) CreateNonPartitionedTableAnalysisJob(
5757
tblInfo *model.TableInfo,
5858
tblStats *statistics.Table,
5959
) AnalysisJob {
60+
if tblStats == nil {
61+
return nil
62+
}
6063
if !tblStats.IsEligibleForAnalysis() {
6164
return nil
6265
}
@@ -92,6 +95,9 @@ func (f *AnalysisJobFactory) CreateStaticPartitionAnalysisJob(
9295
partitionID int64,
9396
partitionStats *statistics.Table,
9497
) AnalysisJob {
98+
if partitionStats == nil {
99+
return nil
100+
}
95101
if !partitionStats.IsEligibleForAnalysis() {
96102
return nil
97103
}
@@ -128,6 +134,9 @@ func (f *AnalysisJobFactory) CreateDynamicPartitionedTableAnalysisJob(
128134
globalTblStats *statistics.Table,
129135
partitionStats map[PartitionIDAndName]*statistics.Table,
130136
) AnalysisJob {
137+
if globalTblStats == nil {
138+
return nil
139+
}
131140
if !globalTblStats.IsEligibleForAnalysis() {
132141
return nil
133142
}

pkg/statistics/handle/autoanalyze/priorityqueue/queue.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,14 +644,16 @@ func (pq *AnalysisPriorityQueue) RefreshLastAnalysisDuration() {
644644
zap.Int64("tableID", job.GetTableID()),
645645
zap.String("job", job.String()),
646646
)
647-
// TODO: Remove this after handling the DDL event.
647+
// Delete the job from the queue since its table is missing. This is a safeguard -
648+
// DDL events should have already cleaned up jobs for dropped tables.
648649
err := pq.syncFields.inner.delete(job)
649650
if err != nil {
650651
statslogutil.StatsLogger().Error("Failed to delete job from priority queue",
651652
zap.Error(err),
652653
zap.String("job", job.String()),
653654
)
654655
}
656+
continue
655657
}
656658
indicators.LastAnalysisDuration = jobFactory.GetTableLastAnalyzeDuration(tableStats)
657659
job.SetIndicators(indicators)

pkg/statistics/handle/autoanalyze/priorityqueue/queue_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"testing"
2020
"time"
2121

22+
"github.com/pingcap/tidb/pkg/meta/model"
2223
pmodel "github.com/pingcap/tidb/pkg/parser/model"
2324
"github.com/pingcap/tidb/pkg/sessionctx"
2425
"github.com/pingcap/tidb/pkg/statistics"
@@ -608,3 +609,47 @@ func TestPQCanBeClosedAndReInitialized(t *testing.T) {
608609
// Check if the priority queue is initialized.
609610
require.True(t, pq.IsInitialized())
610611
}
612+
613+
func TestPQHandlesTableDeletionGracefully(t *testing.T) {
614+
store, dom := testkit.CreateMockStoreAndDomain(t)
615+
handle := dom.StatsHandle()
616+
617+
tk := testkit.NewTestKit(t, store)
618+
tk.MustExec("use test")
619+
tk.MustExec("create table t1 (a int)")
620+
tk.MustExec("insert into t1 values (1)")
621+
statistics.AutoAnalyzeMinCnt = 0
622+
defer func() {
623+
statistics.AutoAnalyzeMinCnt = 1000
624+
}()
625+
626+
ctx := context.Background()
627+
require.NoError(t, handle.DumpStatsDeltaToKV(true))
628+
require.NoError(t, handle.Update(ctx, dom.InfoSchema()))
629+
pq := priorityqueue.NewAnalysisPriorityQueue(handle)
630+
defer pq.Close()
631+
require.NoError(t, pq.Initialize())
632+
633+
// Check the priority queue is not empty.
634+
l, err := pq.Len()
635+
require.NoError(t, err)
636+
require.NotEqual(t, 0, l)
637+
638+
tbl, err := dom.InfoSchema().TableByName(ctx, pmodel.NewCIStr("test"), pmodel.NewCIStr("t1"))
639+
require.NoError(t, err)
640+
641+
// Drop the table and mock the table stats is removed from the cache.
642+
tk.MustExec("drop table t1")
643+
deleteEvent := findEvent(handle.DDLEventCh(), model.ActionDropTable)
644+
require.NotNil(t, deleteEvent)
645+
require.NoError(t, handle.HandleDDLEvent(deleteEvent))
646+
require.NoError(t, handle.Update(ctx, dom.InfoSchema()))
647+
648+
// Make sure handle.Get() returns false.
649+
_, ok := handle.Get(tbl.Meta().ID)
650+
require.False(t, ok)
651+
652+
require.NotPanics(t, func() {
653+
pq.RefreshLastAnalysisDuration()
654+
})
655+
}

pkg/statistics/handle/autoanalyze/refresher/refresher.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,5 +262,8 @@ func (*Refresher) OnBecomeOwner() {
262262
// OnRetireOwner is used to handle the event when the current TiDB instance retires from being the stats owner.
263263
func (r *Refresher) OnRetireOwner() {
264264
// Stop the worker and close the queue.
265+
// Note: we have to guarantee that the worker is stopped before closing the queue.
266+
// Otherwise, the worker may still access the queue after it is closed.
267+
r.worker.Stop()
265268
r.jobs.Close()
266269
}

0 commit comments

Comments
 (0)