docs/agents/ddl/08-partition-ddl.md
This doc focuses on TiDB partition DDL job types:
model.ActionAddTablePartitionmodel.ActionDropTablePartitionmodel.ActionTruncateTablePartitionmodel.ActionReorganizePartitionmodel.ActionExchangeTablePartitionmodel.ActionAlterTablePartitioning, model.ActionRemovePartitioningPartition DDL is subtle because correctness depends on multi-version semantics (different TiDB nodes observe different schema versions), plus global indexes, placement rules, and (optionally) TiFlash replicas.
SQL → DDL module:
pkg/executor/ddl.go (type DDLExec, (*DDLExec).Next)pkg/ddl/executor.go (search Action*Partition job creation)Job execution (owner worker):
pkg/ddl/job_worker.go (case model.ActionAddTablePartition, ...Drop..., ...Truncate..., ...Reorganize..., ...Exchange...)pkg/ddl/partition.goSchema diff helpers (used by schema sync/apply diff):
pkg/ddl/schema_version.go:SetSchemaDiffForExchangeTablePartitionpkg/ddl/schema_version.go:SetSchemaDiffForTruncateTablePartitionpkg/ddl/schema_version.go:SetSchemaDiffForDropTablePartitionpkg/ddl/schema_version.go:SetSchemaDiffForReorganizePartitionPartition DDL usually does not store “state per partition definition”. Instead, it persists intermediate state via:
job.SchemaState (drives the worker state machine)tblInfo.Partition.DDLState / tblInfo.Partition.DDLAction (persisted so other components can interpret current behavior)tblInfo.Partition.AddingDefinitions (new partitions being introduced)tblInfo.Partition.DroppingDefinitions (partitions being removed)tblInfo.Partition.DDLChangedIndex (old/new index mapping)Many correctness rules are implemented as “double write / filter reads” behaviors keyed off these fields (see comments inside pkg/ddl/partition.go).
ActionAddTablePartition)pkg/ddl/partition.go:onAddTablePartitionmodel.TablePartitionArgs (pkg/meta/model/job_args.go)State machine (job.SchemaState):
StateNone → StateReplicaOnly → Done (table becomes StatePublic)Key steps:
StateNone, TiDB:
tblInfo.Partition.AddingDefinitions (updateAddingPartitionInfo)Partition.DDLState = StateReplicaOnly and placement/label updates before writes startStateReplicaOnly, TiDB may block until TiFlash replicas for new partitions are ready:
pkg/ddl/partition.go:checkPartitionReplicainfosync.ConfigureTiFlashPDForPartitionsAddingDefinitions into Definitions (updatePartitionInfo)preSplitAndScatterPartition.DDLState/DDLAction and finish the jobActionDropTablePartition)pkg/ddl/partition.go:onDropTablePartitionmodel.TablePartitionArgsState machine (job.SchemaState):
StatePublic → StateWriteOnly → StateDeleteOnly → StateDeleteReorganization → Done (StateNone)Why so many states:
Global-index cleanup (only when the table has global indexes):
pkg/ddl/partition.go:cleanGlobalIndexEntriesFromDroppedPartitionspkg/ddl/partition.go:getReorgInfoFromPartitionspkg/ddl/reorg.go / pkg/ddl/column.go:updatePhysicalTableRow (partition worker type differs by job)Finalization:
DroppingDefinitions, sets args.OldPhysicalTblIDs (for schema diff / delete-range)notifier.NewDropPartitionEvent (via asyncNotifyEvent)pkg/ddl/delete_range.go (ActionDropTablePartition cases)ActionTruncateTablePartition)pkg/ddl/partition.go:onTruncateTablePartitionmodel.TruncateTableArgs (uses OldPartitionIDs/NewPartitionIDs)State machine (job.SchemaState):
StatePublic → StateWriteOnly → StateDeleteOnly → StateDeleteReorganization → Done (StateNone)Key behaviors:
pkg/ddl/partition.go:replaceTruncatePartitionspi.NewPartitionIDs and pi.DroppingDefinitions are used by global-index filtering logic (see comments near onTruncateTablePartition)cleanGlobalIndexEntriesFromDroppedPartitionspkg/ddl/delete_range.go (ActionTruncateTablePartition cases)ActionReorganizePartition)This is the “heavy” partition DDL: it copies data into a new set of partitions and may recreate (global/unique) indexes.
pkg/ddl/partition.go:onReorganizePartitionpkg/ddl/partition.go:doPartitionReorgWork (forces job.ReorgMeta.ReorgTp = ReorgTypeTxn)State machine (job.SchemaState):
StateNone
AddingDefinitions + DroppingDefinitionsPartition.DDLChangedIndex)StateDeleteOnly → StateWriteOnly → StateWriteReorganization
StateWriteReorganization
doPartitionReorgWork → pkg/ddl/partition.go:doPartitionReorgWorkStateDeleteReorganization
StatePublic
StateDeleteOnly to avoid orphan inserts racing with delete-range GCStateNone)
TableInfoOldPhysicalTblIDs, OldGlobalIndexes, NewPartitionIDs) for delete-range and stats updatenewStatsDDLEventForJobPartitioning changes:
ActionAlterTablePartitioning / ActionRemovePartitioning reuse the same pipeline and may Drop+Create the table with a new table ID:
StatePublic branch in onReorganizePartition where metaMut.DropTableOrView + CreateTableOrView is used.ActionExchangeTablePartition)pkg/ddl/partition.go:onExchangeTablePartitionmodel.ExchangeTablePartitionArgsHigh-level flow:
StateWriteOnly) is used to make the non-partitioned table enforce the partition constraint window:
ExchangePartitionInfo on involved tables before the swapcheckExchangePartitionRecordValidation (when WITH VALIDATION)rollbackExchangeTablePartition)Partition DDL often needs PD side effects:
pkg/ddl/partition.go:alterTablePartitionBundles, pkg/ddl/partition.go:droppedPartitionBundlespkg/ddl/partition.go:alterTableLabelRule / dropLabelRulesinfosync.ConfigureTiFlashPDForPartitionscheckPartitionReplicaThese should generally happen before the state that starts writing to the new partitions, so a retry/rollback doesn’t leave partial placement state.
SQL:
ADMIN SHOW DDL JOBSADMIN SHOW DDL JOB QUERIESADMIN CANCEL DDL JOBS <job_id>Tables:
mysql.tidb_ddl_jobmysql.tidb_ddl_historymysql.tidb_ddl_reorgTests (good starting points):
pkg/ddl/partition_test.gopkg/ddl/notifier/testkit_test.go (covers multiple partition actions)AddingDefinitions / DroppingDefinitions and Partition.DDLState are part of multi-version correctness; don’t clear them too early.doPartitionReorgWork forces transactional reorg (ReorgTypeTxn); don’t assume fast-reorg/ingest behavior.