From 3056f41c48c39fa00ebd45814c19f8df88b4ada9 Mon Sep 17 00:00:00 2001 From: lhy1024 Date: Fri, 26 Dec 2025 23:37:25 +0800 Subject: [PATCH] ddl: fix affinity idempotency problem (#65291) ref pingcap/tidb#64938 --- pkg/ddl/affinity_test.go | 22 ++++++++++++++++++---- pkg/ddl/table.go | 8 ++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/pkg/ddl/affinity_test.go b/pkg/ddl/affinity_test.go index 5c85439a04..595330f239 100644 --- a/pkg/ddl/affinity_test.go +++ b/pkg/ddl/affinity_test.go @@ -173,9 +173,12 @@ func checkAffinityGroupsInPD(t *testing.T, do *domain.Domain, dbName, tbName str func TestAffinityPDInteraction(t *testing.T) { store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2, tp1, tp2") + dropTable := func() { + tk.MustExec("drop table if exists t1, t2, t3, tp1, tp2, tp3") + } + dropTable() + defer dropTable() // Test 1: Create table with affinity='table' tk.MustExec("create table t1(a int) affinity = 'table'") @@ -237,8 +240,19 @@ func TestAffinityPDInteraction(t *testing.T) { require.NoError(t, err) require.Empty(t, groups, "old partition's affinity group should be deleted after truncate partition") - // Cleanup - tk.MustExec("drop table if exists t2, tp1, tp2") + // Test 8: ALTER TABLE AFFINITY idempotency - partition -> partition + tk.MustExec("create table tp3(a int) affinity = 'partition' partition by hash(a) partitions 3") + checkAffinityGroupsInPD(t, dom, "test", "tp3", true) + // Set affinity to partition again (idempotent operation) + tk.MustExec("alter table tp3 affinity = 'partition'") + checkAffinityGroupsInPD(t, dom, "test", "tp3", true) + + // Test 9: ALTER TABLE AFFINITY idempotency - table -> table + tk.MustExec("create table t3(a int) affinity = 'table'") + checkAffinityGroupsInPD(t, dom, "test", "t3", true) + // Set affinity to table again (idempotent operation) + tk.MustExec("alter table t3 affinity = 'table'") + checkAffinityGroupsInPD(t, dom, "test", "t3", true) } func TestAffinityDropDatabase(t *testing.T) { diff --git a/pkg/ddl/table.go b/pkg/ddl/table.go index 35896d3de9..00aa2e9a3b 100644 --- a/pkg/ddl/table.go +++ b/pkg/ddl/table.go @@ -1778,9 +1778,13 @@ func onAlterTableAffinity(jobCtx *jobContext, job *model.Job) (ver int64, err er // Delete old affinity groups (best-effort cleanup - ignore errors) // ALTER TABLE AFFINITY: only delete when old table had affinity configuration // This ensures 'ALTER TABLE AFFINITY = 'none'' correctly cleans up stale affinity groups + // Skip deletion if the affinity level remains the same to ensure idempotency if oldTblInfo.Affinity != nil { - if err := deleteTableAffinityGroupsInPD(jobCtx, oldTblInfo, nil); err != nil { - logutil.DDLLogger().Error("failed to delete old affinity groups from PD", zap.Error(err), zap.Int64("tableID", oldTblInfo.ID)) + // Only delete if affinity is removed or level changed (same level means same group IDs) + if tblInfo.Affinity == nil || oldTblInfo.Affinity.Level != tblInfo.Affinity.Level { + if err := deleteTableAffinityGroupsInPD(jobCtx, oldTblInfo, nil); err != nil { + logutil.DDLLogger().Error("failed to delete old affinity groups from PD", zap.Error(err), zap.Int64("tableID", oldTblInfo.ID)) + } } }