code: func (d *ddl) start(ctx context.Context, ctxPool *pools.ResourcePool) {
  	logutil.Logger(ddlLogCtx).Info("[ddl] start DDL", zap.String("ID", d.uuid), zap.Bool("runWorker", RunWorker))
  	d.quitCh = make(chan struct{})
  
  	// If RunWorker is true, we need campaign owner and do DDL job.
  	// Otherwise, we needn't do that.
  	if RunWorker {
  		err := d.ownerManager.CampaignOwner(ctx)
  		terror.Log(errors.Trace(err))
  
  		d.workers = make(map[workerType]*worker, 2)
  		d.sessPool = newSessionPool(ctxPool)
  		d.delRangeMgr = d.newDeleteRangeManager(ctxPool == nil)
  		d.workers[generalWorker] = newWorker(generalWorker, d.store, d.sessPool, d.delRangeMgr)
  		d.workers[addIdxWorker] = newWorker(addIdxWorker, d.store, d.sessPool, d.delRangeMgr)
  		for _, worker := range d.workers {
  			worker.wg.Add(1)
  			w := worker
  			go tidbutil.WithRecovery(
  				func() { w.start(d.ddlCtx) },
  				func(r interface{}) {
  					if r != nil {
  						logutil.Logger(w.logCtx).Error("[ddl] DDL worker meet panic", zap.String("ID", d.uuid))
  						metrics.PanicCounter.WithLabelValues(metrics.LabelDDL).Inc()
  					}
  				})
  			metrics.DDLCounter.WithLabelValues(fmt.Sprintf("%s_%s", metrics.CreateDDL, worker.String())).Inc()
  
  			// When the start function is called, we will send a fake job to let worker
  			// checks owner firstly and try to find whether a job exists and run.
  			asyncNotify(worker.ddlJobCh)
  		}
  	}
  }
docs: // start campaigns the owner and starts workers.
  // ctxPool is used for the worker's delRangeManager and creates sessions.