pingcap/tidb
DDL framework
DDL (data-definition language) statements in TiDB are not executed inline. They are turned into asynchronous jobs that a single elected owner drives through a state machine. This is what makes online schema change safe across many TiDB instances.
Purpose
pkg/ddl/ implements:
- The job submission API (
CREATE TABLE,ALTER TABLE,DROP TABLE, …). - A scheduler that picks up jobs across the cluster.
- A worker pool that runs each job through its state machine.
- Reorg logic for index/column backfills.
- Multi-version schema reload coordination so all TiDB nodes converge on the new infoschema.
- Specialised flows for partitioning, BDR (bidirectional replication), placement policies, TiFlash replicas, and online ADD INDEX with distributed reorg.
The DDL framework's source-of-truth runbook lives at docs/agents/ddl/README.md.
Directory layout
pkg/ddl/
├── ddl.go # Public API: NewDDL, RegisterStatsHandle, etc.
├── job_submitter.go # Submits a job to the cluster job table
├── job_scheduler.go # Picks the next job to run on this owner
├── job_worker.go # Drives a job through its state machine
├── ddl_running_jobs.go # Per-instance running-job state
├── ddl_workerpool.go # Worker pool for parallel small jobs
├── ddl_history.go # Records completed/failed jobs in mysql.tidb_ddl_history
├── owner_mgr.go # Owner election helper
├── dist_owner.go # Distributed-owner selection for big jobs
├── ddl_tiflash_api.go # TiFlash replica management
├── reorg.go, reorg_util.go # Backfill state machine
├── backfilling*.go # Online ADD INDEX backfill, distributed reorg
├── delete_range.go # Background deletion after DROP/TRUNCATE
├── job_worker_*.go, job_submitter_*.go # Tests
├── add_column.go, column.go, modify_column.go # Column DDL flows
├── index.go, index_*.go # Index DDL flows
├── partition.go # Partition DDL flows (≈190 KB)
├── create_table.go, schema.go # Database/table creation
├── foreign_key.go # FK validation and constraints
├── placement_policy.go # Placement policies
├── resource_group.go # Resource group DDL
├── bdr.go # Bidirectional replication safety checks
├── ttl.go # TTL job hooks
├── multi_schema_change.go # Atomic multi-statement ALTER TABLE
├── rollingback.go # Rollback paths for every job type
├── sanity_check.go # Cross-cuts run before execution
├── schema_version.go # Coordinates infoschema reload
├── schema.go, schemaver/ # Schema-version tracking
├── stat.go # ADMIN SHOW DDL STATS
├── notifier/, schematracker/, serverstate/, session/
├── ingest/ # Lightning-style ingestion path used by ADD INDEX
├── label/, logutil/, mock/, options.go, table*.go, ttl_test.go,
└── tests/, testargsv1/, util/, copr/Top-level doc.go (pkg/ddl/doc.go) is required reading per AGENTS.md's pre-flight checklist.
Key abstractions
| Type | File | Purpose |
|---|---|---|
ddl.DDL |
pkg/ddl/ddl.go |
Public façade. Each TiDB instance has one. |
ddl.DDLJobSubmitter |
pkg/ddl/job_submitter.go |
Translates SQL into a model.Job and inserts it into the cluster job table. |
ddl.JobScheduler |
pkg/ddl/job_scheduler.go |
The DDL owner runs this; it picks the next runnable job. |
ddl.worker |
pkg/ddl/job_worker.go |
Drives a single job: state machine, transactions, infoschema bumps. |
ddl.reorgInfo |
pkg/ddl/reorg.go |
Backfill state and progress checkpointing. |
model.Job |
pkg/meta/model/job.go |
Persistent job record (state, args, schema/table IDs, error). |
ddl.OwnerManager |
pkg/ddl/owner_mgr.go |
Tracks the current DDL owner via pkg/owner/ (etcd-backed). |
ddl.JobType |
pkg/meta/model/ |
Enumeration of every supported DDL action. |
Job lifecycle
stateDiagram-v2 [*] --> Queueing : SQL submitted Queueing --> Running : owner picks up Running --> Reorganizing : if needs backfill Reorganizing --> Done : backfill complete Running --> Done : non-reorg jobs Running --> RollingBack : error / cancel RollingBack --> Cancelled Done --> [*] Cancelled --> [*]
Implementation map:
- Submission:
ddl.SchemaSyncervalidates against the current infoschema;job_submitter.gowrites the job intomysql.tidb_ddl_joband returns to the user. The user's session blocks on a notification channel until the job finishes. - Scheduling: only the elected DDL owner runs jobs.
owner_mgr.gouses etcd viapkg/owner/to elect one.job_scheduler.gopicks the next runnable job (general or reorg queue). - Execution:
job_worker.gois the state-machine driver. It runs each step in a TiKV transaction, advances the job's state, and updates infoschema versions viaschema_version.go. - Reorg: For ADD INDEX, MODIFY COLUMN type changes, and similar,
reorg.gocoordinates a row-by-row backfill. For large indexes, the distributed reorg path inbackfilling_dist_*.gopartitions the keyspace across TiDB nodes via the DXF framework. - Schema-version bump: when a job advances,
schema_version.gowrites a new schema version into etcd. Other TiDB instances reload their infoschema (pkg/domain/domain.goloadInfoSchema). - Completion / rollback:
rollingback.godefines the rollback path for each job type.ddl_history.goarchives the result tomysql.tidb_ddl_history.
DDL job types
pkg/meta/model enumerates every action: ActionCreateSchema, ActionDropSchema, ActionCreateTable, ActionAddColumn, ActionDropColumn, ActionModifyColumn, ActionAddIndex, ActionDropIndex, ActionAddForeignKey, ActionAlterTablePartitioning, ActionExchangeTablePartition, ActionFlashbackCluster, ActionMultiSchemaChange, etc. Each has a dedicated implementation file (add_column.go, index.go, partition.go, …) that the worker dispatches to.
DXF and distributed reorg
For very large indexes, the DXF (Distributed eXecution Framework) in pkg/dxf/ lets multiple TiDB nodes share the backfill workload. The DDL side wires into DXF via:
backfilling_dist_executor.go— the executor that runs on each participating node.backfilling_dist_scheduler.go— partitions the key space and dispatches subtasks.backfilling_import_cloud.go— uses cloud storage to ship sorted SST files between nodes.
This path is enabled per-job via tidb_ddl_enable_fast_reorg and friends.
Online safety
DDL respects multiple invariants enforced in code:
- Schema version waiting:
schema_version.gowaits for all live TiDB nodes to reload before advancing certain states. The maximum wait is bounded; otherwise the job pauses withWaitSchemaSyncederrors. - BDR pre-flight:
bdr.gorejects DDL types that aren't safe under bi-directional replication. - Sanity checks:
sanity_check.goruns before execution (e.g., reject column drop if it's referenced by an FK). - Reorg checkpointing:
reorg.goperiodically writes progress so a crash can resume. - Cancel paths:
cancel_test.goexercises every job type for proper cancellation.
Multi-schema change and BDR
multi_schema_change.go lets a single ALTER TABLE perform several sub-actions atomically. bdr.go ensures these aren't dispatched in BDR clusters when they would break replication.
Tests
pkg/ddl/tests/ is a large directory of subtests (create, index, partition, reorg, cancel, schemaver, …). The DDL-specific test guidance is in docs/agents/ddl/ and .agents/skills/tidb-test-guidelines.
Integration points
- SQL frontend:
pkg/executor/ddl.goandpkg/executor/operate_ddl_jobs.gotranslate SQL intoDDL.DoDDLJob. - Meta:
pkg/meta/persists schema definitions. - Infoschema:
pkg/infoschema/is the in-memory schema cache; reloads happen viapkg/domain/domain.go. - Statistics: stats tied to dropped/altered columns are invalidated through
pkg/statistics/handle/. - Lightning: distributed reorg uses
pkg/lightning/backend/to write SST files for ingestion. - Owner / etcd:
pkg/owner/provides leader election;pkg/util/etcd/wraps the etcd client.
Entry points for modification
The DDL module's contributor checklist (from AGENTS.md):
- Read
docs/agents/ddl/README.mdfirst. It is the authoritative map of the execution framework. Treat anything else underdocs/agents/ddl/*as hypothesis until verified. - Pick the job type and locate its file (
add_column.go,index.go,partition.go, …). - Define new states only by extending the existing state-machine vocabulary; rollback paths must be filled in
rollingback.go. - If your change adds a new top-level DDL action, register it in
pkg/meta/model/action.goand add a case injob_worker.go. - Tests must include a cancel test in
pkg/ddl/tests/and a regression integration test intests/integrationtest/t/ddl/.... - If implementation drifts from the docs in
docs/agents/ddl/, update the docs in the same PR.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.