Skip to content

Commit b6a0381

Browse files
crazycs520zimulala
authored andcommitted
ddl: fix cancel drop index error (pingcap#8504) (pingcap#9495)
1 parent 128052d commit b6a0381

File tree

9 files changed

+206
-64
lines changed

9 files changed

+206
-64
lines changed

ddl/ddl_api.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/pingcap/tidb/table"
3737
"github.com/pingcap/tidb/types"
3838
"github.com/pingcap/tidb/util/charset"
39+
"github.com/pingcap/tidb/util/schemautil"
3940
)
4041

4142
func (d *ddl) CreateSchema(ctx sessionctx.Context, schema model.CIStr, charsetInfo *ast.CharsetOpt) (err error) {
@@ -1720,7 +1721,7 @@ func (d *ddl) CreateIndex(ctx sessionctx.Context, ti ast.Ident, unique bool, ind
17201721
indexName = getAnonymousIndex(t, idxColNames[0].Column.Name)
17211722
}
17221723

1723-
if indexInfo := findIndexByName(indexName.L, t.Meta().Indices); indexInfo != nil {
1724+
if indexInfo := schemautil.FindIndexByName(indexName.L, t.Meta().Indices); indexInfo != nil {
17241725
return ErrDupKeyName.Gen("index already exist %s", indexName)
17251726
}
17261727

@@ -1844,7 +1845,7 @@ func (d *ddl) DropIndex(ctx sessionctx.Context, ti ast.Ident, indexName model.CI
18441845
return errors.Trace(infoschema.ErrTableNotExists.GenByArgs(ti.Schema, ti.Name))
18451846
}
18461847

1847-
if indexInfo := findIndexByName(indexName.L, t.Meta().Indices); indexInfo == nil {
1848+
if indexInfo := schemautil.FindIndexByName(indexName.L, t.Meta().Indices); indexInfo == nil {
18481849
return ErrCantDropFieldOrKey.Gen("index %s doesn't exist", indexName)
18491850
}
18501851

ddl/ddl_db_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646
"github.com/pingcap/tidb/types"
4747
"github.com/pingcap/tidb/util/admin"
4848
"github.com/pingcap/tidb/util/mock"
49+
"github.com/pingcap/tidb/util/schemautil"
4950
"github.com/pingcap/tidb/util/testkit"
5051
"github.com/pingcap/tidb/util/testleak"
5152
"github.com/pingcap/tidb/util/testutil"
@@ -506,6 +507,101 @@ func (s *testDBSuite) TestCancelAddIndex1(c *C) {
506507
s.mustExec(c, "alter table t drop index idx_c2")
507508
}
508509

510+
// TestCancelDropIndex tests cancel ddl job which type is drop index.
511+
func (s *testDBSuite) TestCancelDropIndex(c *C) {
512+
s.tk = testkit.NewTestKit(c, s.store)
513+
s.mustExec(c, "use test_db")
514+
s.mustExec(c, "drop table if exists t")
515+
s.mustExec(c, "create table t(c1 int, c2 int)")
516+
defer s.mustExec(c, "drop table t;")
517+
for i := 0; i < 5; i++ {
518+
s.mustExec(c, "insert into t values (?, ?)", i, i)
519+
}
520+
521+
testCases := []struct {
522+
needAddIndex bool
523+
jobState model.JobState
524+
JobSchemaState model.SchemaState
525+
cancelSucc bool
526+
}{
527+
// model.JobStateNone means the jobs is canceled before the first run.
528+
{true, model.JobStateNone, model.StateNone, true},
529+
{false, model.JobStateRunning, model.StateWriteOnly, true},
530+
{false, model.JobStateRunning, model.StateDeleteOnly, false},
531+
{true, model.JobStateRunning, model.StateDeleteReorganization, false},
532+
}
533+
534+
var checkErr error
535+
oldReorgWaitTimeout := ddl.ReorgWaitTimeout
536+
ddl.ReorgWaitTimeout = 50 * time.Millisecond
537+
defer func() { ddl.ReorgWaitTimeout = oldReorgWaitTimeout }()
538+
hook := &ddl.TestDDLCallback{}
539+
var jobID int64
540+
testCase := &testCases[0]
541+
hook.OnJobRunBeforeExported = func(job *model.Job) {
542+
if job.Type == model.ActionDropIndex && job.State == testCase.jobState && job.SchemaState == testCase.JobSchemaState {
543+
jobID = job.ID
544+
jobIDs := []int64{job.ID}
545+
hookCtx := mock.NewContext()
546+
hookCtx.Store = s.store
547+
err := hookCtx.NewTxn()
548+
if err != nil {
549+
checkErr = errors.Trace(err)
550+
return
551+
}
552+
txn, err := hookCtx.Txn(true)
553+
if err != nil {
554+
checkErr = errors.Trace(err)
555+
return
556+
}
557+
errs, err := admin.CancelJobs(txn, jobIDs)
558+
if err != nil {
559+
checkErr = errors.Trace(err)
560+
return
561+
}
562+
563+
if errs[0] != nil {
564+
checkErr = errors.Trace(errs[0])
565+
return
566+
}
567+
568+
checkErr = txn.Commit(context.Background())
569+
}
570+
}
571+
s.dom.DDL().SetHook(hook)
572+
for i := range testCases {
573+
testCase = &testCases[i]
574+
if testCase.needAddIndex {
575+
s.mustExec(c, "alter table t add index idx_c2(c2)")
576+
}
577+
rs, err := s.tk.Exec("alter table t drop index idx_c2")
578+
if rs != nil {
579+
rs.Close()
580+
}
581+
582+
t := s.testGetTable(c, "t")
583+
indexInfo := schemautil.FindIndexByName("idx_c2", t.Meta().Indices)
584+
if testCase.cancelSucc {
585+
c.Assert(checkErr, IsNil)
586+
c.Assert(err, NotNil)
587+
c.Assert(err.Error(), Equals, "[ddl:12]cancelled DDL job")
588+
589+
c.Assert(indexInfo, NotNil)
590+
c.Assert(indexInfo.State, Equals, model.StatePublic)
591+
} else {
592+
err1 := admin.ErrCannotCancelDDLJob.GenByArgs(jobID)
593+
c.Assert(err, IsNil)
594+
c.Assert(checkErr, NotNil)
595+
c.Assert(checkErr.Error(), Equals, err1.Error())
596+
597+
c.Assert(indexInfo, IsNil)
598+
}
599+
}
600+
s.dom.DDL().SetHook(&ddl.TestDDLCallback{})
601+
s.mustExec(c, "alter table t add index idx_c2(c2)")
602+
s.mustExec(c, "alter table t drop index idx_c2")
603+
}
604+
509605
func (s *testDBSuite) TestAddAnonymousIndex(c *C) {
510606
s.tk = testkit.NewTestKit(c, s.store)
511607
s.tk.MustExec("use " + s.schemaName)

ddl/ddl_worker.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/pingcap/tidb/sessionctx/binloginfo"
2626
"github.com/pingcap/tidb/terror"
2727
"github.com/pingcap/tidb/util"
28+
"github.com/pingcap/tidb/util/schemautil"
2829
log "github.com/sirupsen/logrus"
2930
"golang.org/x/net/context"
3031
)
@@ -571,7 +572,7 @@ func (d *ddl) cleanAddIndexQueueJobs(txn kv.Transaction) error {
571572
if err != nil {
572573
return errors.Trace(err)
573574
}
574-
indexInfo := findIndexByName(indexName.L, tblInfo.Indices)
575+
indexInfo := schemautil.FindIndexByName(indexName.L, tblInfo.Indices)
575576
_, err = convertAddIdxJob2RollbackJob(m, job, tblInfo, indexInfo, nil)
576577
if err == nil {
577578
_, err = m.DeQueueDDLJob()

ddl/ddl_worker_test.go

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -469,30 +469,26 @@ type testCancelJob struct {
469469

470470
func buildCancelJobTests(firstID int64) []testCancelJob {
471471
err := errCancelledDDLJob
472-
errs := []error{err}
473472
noErrs := []error{nil}
474473
tests := []testCancelJob{
475474
{act: model.ActionAddIndex, jobIDs: []int64{firstID + 1}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly, ddlRetErr: err},
476475
{act: model.ActionAddIndex, jobIDs: []int64{firstID + 2}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly, ddlRetErr: err},
477476
{act: model.ActionAddIndex, jobIDs: []int64{firstID + 3}, cancelRetErrs: noErrs, cancelState: model.StateWriteReorganization, ddlRetErr: err},
478477
{act: model.ActionAddIndex, jobIDs: []int64{firstID + 4}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenByArgs(firstID + 4)}, cancelState: model.StatePublic, ddlRetErr: err},
479478

480-
// TODO: after fix drop index and create table rollback bug, the below test cases maybe need to change.
481-
{act: model.ActionDropIndex, jobIDs: []int64{firstID + 5}, cancelRetErrs: errs, cancelState: model.StateWriteOnly, ddlRetErr: err},
482-
{act: model.ActionDropIndex, jobIDs: []int64{firstID + 6}, cancelRetErrs: errs, cancelState: model.StateDeleteOnly, ddlRetErr: err},
483-
{act: model.ActionDropIndex, jobIDs: []int64{firstID + 7}, cancelRetErrs: errs, cancelState: model.StateDeleteReorganization, ddlRetErr: err},
484-
{act: model.ActionDropIndex, jobIDs: []int64{firstID + 8}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenByArgs(firstID + 8)}, cancelState: model.StateNone, ddlRetErr: err},
479+
// Test cancel drop index job , see TestCancelDropIndex.
485480

486481
// TODO: add create table back after we fix the cancel bug.
487482
//{act: model.ActionCreateTable, jobIDs: []int64{firstID + 9}, cancelRetErrs: noErrs, cancelState: model.StatePublic, ddlRetErr: err},
488-
{act: model.ActionAddColumn, jobIDs: []int64{firstID + 9}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly, ddlRetErr: err},
489-
{act: model.ActionAddColumn, jobIDs: []int64{firstID + 10}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly, ddlRetErr: err},
490-
{act: model.ActionAddColumn, jobIDs: []int64{firstID + 11}, cancelRetErrs: noErrs, cancelState: model.StateWriteReorganization, ddlRetErr: err},
491-
{act: model.ActionAddColumn, jobIDs: []int64{firstID + 12}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenByArgs(firstID + 12)}, cancelState: model.StatePublic, ddlRetErr: err},
492-
493-
{act: model.ActionDropColumn, jobIDs: []int64{firstID + 13}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenByArgs(firstID + 13)}, cancelState: model.StateWriteOnly, ddlRetErr: err},
494-
{act: model.ActionDropColumn, jobIDs: []int64{firstID + 14}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenByArgs(firstID + 14)}, cancelState: model.StateDeleteOnly, ddlRetErr: err},
495-
{act: model.ActionDropColumn, jobIDs: []int64{firstID + 15}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenByArgs(firstID + 15)}, cancelState: model.StateDeleteReorganization, ddlRetErr: err},
483+
484+
{act: model.ActionAddColumn, jobIDs: []int64{firstID + 5}, cancelRetErrs: noErrs, cancelState: model.StateDeleteOnly, ddlRetErr: err},
485+
{act: model.ActionAddColumn, jobIDs: []int64{firstID + 6}, cancelRetErrs: noErrs, cancelState: model.StateWriteOnly, ddlRetErr: err},
486+
{act: model.ActionAddColumn, jobIDs: []int64{firstID + 7}, cancelRetErrs: noErrs, cancelState: model.StateWriteReorganization, ddlRetErr: err},
487+
{act: model.ActionAddColumn, jobIDs: []int64{firstID + 8}, cancelRetErrs: []error{admin.ErrCancelFinishedDDLJob.GenByArgs(firstID + 8)}, cancelState: model.StatePublic, ddlRetErr: err},
488+
489+
{act: model.ActionDropColumn, jobIDs: []int64{firstID + 9}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenByArgs(firstID + 9)}, cancelState: model.StateWriteOnly, ddlRetErr: err},
490+
{act: model.ActionDropColumn, jobIDs: []int64{firstID + 10}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenByArgs(firstID + 10)}, cancelState: model.StateDeleteOnly, ddlRetErr: err},
491+
{act: model.ActionDropColumn, jobIDs: []int64{firstID + 11}, cancelRetErrs: []error{admin.ErrCannotCancelDDLJob.GenByArgs(firstID + 11)}, cancelState: model.StateDeleteReorganization, ddlRetErr: err},
496492
}
497493

498494
return tests
@@ -626,23 +622,8 @@ func (s *testDDLSuite) TestCancelJob(c *C) {
626622
c.Assert(txn.Commit(context.Background()), IsNil)
627623
s.checkAddIdx(c, d, dbInfo.ID, tblInfo.ID, idxOrigName, true)
628624

629-
// for dropping index
630-
idxName := []interface{}{model.NewCIStr("idx")}
631-
test = &tests[4]
632-
doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, model.ActionDropIndex, idxName, &test.cancelState)
633-
c.Check(errors.ErrorStack(checkErr), Equals, "")
634-
test = &tests[5]
635-
doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, model.ActionDropIndex, idxName, &test.cancelState)
636-
c.Check(errors.ErrorStack(checkErr), Equals, "")
637-
test = &tests[6]
638-
doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, model.ActionDropIndex, idxName, &test.cancelState)
639-
c.Check(errors.ErrorStack(checkErr), Equals, "")
640-
test = &tests[7]
641-
testDropIndex(c, ctx, d, dbInfo, tblInfo, "idx")
642-
c.Check(errors.ErrorStack(checkErr), Equals, "")
643-
644625
// for add column
645-
test = &tests[8]
626+
test = &tests[4]
646627
addingColName := "colA"
647628
newColumnDef := &ast.ColumnDef{
648629
Name: &ast.ColumnName{Name: model.NewCIStr(addingColName)},
@@ -655,39 +636,38 @@ func (s *testDDLSuite) TestCancelJob(c *C) {
655636
doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, model.ActionAddColumn, addColumnArgs, &cancelState)
656637
c.Check(errors.ErrorStack(checkErr), Equals, "")
657638
s.checkAddColumn(c, d, dbInfo.ID, tblInfo.ID, addingColName, false)
658-
test = &tests[9]
639+
test = &tests[5]
659640
doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, model.ActionAddColumn, addColumnArgs, &cancelState)
660641
c.Check(errors.ErrorStack(checkErr), Equals, "")
661642
s.checkAddColumn(c, d, dbInfo.ID, tblInfo.ID, addingColName, false)
662-
test = &tests[10]
643+
test = &tests[6]
663644
doDDLJobErrWithSchemaState(ctx, d, c, dbInfo.ID, tblInfo.ID, model.ActionAddColumn, addColumnArgs, &cancelState)
664645
c.Check(errors.ErrorStack(checkErr), Equals, "")
665646
s.checkAddColumn(c, d, dbInfo.ID, tblInfo.ID, addingColName, false)
666-
test = &tests[11]
647+
test = &tests[7]
667648
testAddColumn(c, ctx, d, dbInfo, tblInfo, addColumnArgs)
668649
c.Check(errors.ErrorStack(checkErr), Equals, "")
669650
s.checkAddColumn(c, d, dbInfo.ID, tblInfo.ID, addingColName, true)
670651

671652
// for drop column.
672-
test = &tests[12]
653+
test = &tests[8]
673654
dropColName := "c1"
674655
dropColumnArgs := []interface{}{model.NewCIStr(dropColName)}
675656
s.checkCancelDropColumn(c, d, dbInfo.ID, tblInfo.ID, dropColName, false)
676657
testCancelDropColumn(c, ctx, d, dbInfo, tblInfo, dropColumnArgs)
677658
c.Check(errors.ErrorStack(checkErr), Equals, "")
678659
s.checkCancelDropColumn(c, d, dbInfo.ID, tblInfo.ID, dropColName, true)
679660

680-
test = &tests[13]
681-
682-
dropColName = "c2"
661+
test = &tests[9]
662+
dropColName = "c3"
683663
dropColumnArgs = []interface{}{model.NewCIStr(dropColName)}
684664
s.checkCancelDropColumn(c, d, dbInfo.ID, tblInfo.ID, dropColName, false)
685665
testCancelDropColumn(c, ctx, d, dbInfo, tblInfo, dropColumnArgs)
686666
c.Check(errors.ErrorStack(checkErr), Equals, "")
687667
s.checkCancelDropColumn(c, d, dbInfo.ID, tblInfo.ID, dropColName, true)
688668

689-
test = &tests[14]
690-
dropColName = "c3"
669+
test = &tests[10]
670+
dropColName = "c4"
691671
dropColumnArgs = []interface{}{model.NewCIStr(dropColName)}
692672
s.checkCancelDropColumn(c, d, dbInfo.ID, tblInfo.ID, dropColName, false)
693673
testCancelDropColumn(c, ctx, d, dbInfo, tblInfo, dropColumnArgs)

ddl/index.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/pingcap/tidb/tablecodec"
3535
"github.com/pingcap/tidb/types"
3636
"github.com/pingcap/tidb/util"
37+
"github.com/pingcap/tidb/util/schemautil"
3738
log "github.com/sirupsen/logrus"
3839
)
3940

@@ -206,7 +207,7 @@ func (d *ddl) onCreateIndex(t *meta.Meta, job *model.Job) (ver int64, err error)
206207
return ver, errors.Trace(err)
207208
}
208209

209-
indexInfo := findIndexByName(indexName.L, tblInfo.Indices)
210+
indexInfo := schemautil.FindIndexByName(indexName.L, tblInfo.Indices)
210211
if indexInfo != nil && indexInfo.State == model.StatePublic {
211212
job.State = model.JobStateCancelled
212213
return ver, ErrDupKeyName.Gen("index already exist %s", indexName)
@@ -319,7 +320,7 @@ func (d *ddl) onDropIndex(t *meta.Meta, job *model.Job) (ver int64, _ error) {
319320
return ver, errors.Trace(err)
320321
}
321322

322-
indexInfo := findIndexByName(indexName.L, tblInfo.Indices)
323+
indexInfo := schemautil.FindIndexByName(indexName.L, tblInfo.Indices)
323324
if indexInfo == nil {
324325
job.State = model.JobStateCancelled
325326
return ver, ErrCantDropFieldOrKey.Gen("index %s doesn't exist", indexName)
@@ -961,15 +962,6 @@ func (d *ddl) addTableIndex(t table.Table, indexInfo *model.IndexInfo, reorgInfo
961962
return d.backfillKVRangesIndex(t, workers, kvRanges, job, reorgInfo)
962963
}
963964

964-
func findIndexByName(idxName string, indices []*model.IndexInfo) *model.IndexInfo {
965-
for _, idx := range indices {
966-
if idx.Name.L == idxName {
967-
return idx
968-
}
969-
}
970-
return nil
971-
}
972-
973965
func allocateIndexID(tblInfo *model.TableInfo) int64 {
974966
tblInfo.MaxIndexID++
975967
return tblInfo.MaxIndexID

ddl/rollingback.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/pingcap/tidb/kv"
2121
"github.com/pingcap/tidb/meta"
2222
"github.com/pingcap/tidb/model"
23+
"github.com/pingcap/tidb/util/schemautil"
2324
log "github.com/sirupsen/logrus"
2425
)
2526

@@ -63,7 +64,7 @@ func convertNotStartAddIdxJob2RollbackJob(t *meta.Meta, job *model.Job, occuredE
6364
job.State = model.JobStateCancelled
6465
return ver, errors.Trace(err)
6566
}
66-
indexInfo := findIndexByName(indexName.L, tblInfo.Indices)
67+
indexInfo := schemautil.FindIndexByName(indexName.L, tblInfo.Indices)
6768
if indexInfo == nil {
6869
job.State = model.JobStateCancelled
6970
return ver, errCancelledDDLJob
@@ -106,6 +107,51 @@ func rollingbackAddColumn(t *meta.Meta, job *model.Job) (ver int64, err error) {
106107
}
107108
return ver, errCancelledDDLJob
108109
}
110+
111+
func rollingbackDropIndex(t *meta.Meta, job *model.Job) (ver int64, err error) {
112+
schemaID := job.SchemaID
113+
tblInfo, err := getTableInfo(t, job, schemaID)
114+
if err != nil {
115+
return ver, errors.Trace(err)
116+
}
117+
118+
var indexName model.CIStr
119+
err = job.DecodeArgs(&indexName)
120+
if err != nil {
121+
job.State = model.JobStateCancelled
122+
return ver, errors.Trace(err)
123+
}
124+
125+
indexInfo := schemautil.FindIndexByName(indexName.L, tblInfo.Indices)
126+
if indexInfo == nil {
127+
job.State = model.JobStateCancelled
128+
return ver, ErrCantDropFieldOrKey.Gen("index %s doesn't exist", indexName)
129+
}
130+
131+
originalState := indexInfo.State
132+
switch indexInfo.State {
133+
case model.StateDeleteOnly, model.StateDeleteReorganization, model.StateNone:
134+
// We can not rollback now, so just continue to drop index.
135+
// Normally won't fetch here, because there is check when cancel ddl jobs. see function: isJobRollbackable.
136+
job.State = model.JobStateRunning
137+
return ver, nil
138+
case model.StatePublic, model.StateWriteOnly:
139+
job.State = model.JobStateRollbackDone
140+
indexInfo.State = model.StatePublic
141+
default:
142+
return ver, ErrInvalidIndexState.Gen("invalid index state %v", indexInfo.State)
143+
}
144+
145+
job.SchemaState = indexInfo.State
146+
job.Args = []interface{}{indexInfo.Name}
147+
ver, err = updateVersionAndTableInfo(t, job, tblInfo, originalState != indexInfo.State)
148+
if err != nil {
149+
return ver, errors.Trace(err)
150+
}
151+
job.FinishTableJob(model.JobStateRollbackDone, model.StatePublic, ver, tblInfo)
152+
return ver, errCancelledDDLJob
153+
}
154+
109155
func rollingbackAddindex(d *ddl, t *meta.Meta, job *model.Job) (ver int64, err error) {
110156
// If the value of SnapshotVer isn't zero, it means the work is backfilling the indexes.
111157
if job.SchemaState == model.StateWriteReorganization && job.SnapshotVer != 0 {
@@ -160,6 +206,8 @@ func convertJob2RollbackJob(d *ddl, t *meta.Meta, job *model.Job) (ver int64, er
160206
ver, err = rollingbackAddindex(d, t, job)
161207
case model.ActionDropColumn:
162208
ver, err = rollingbackDropColumn(t, job)
209+
case model.ActionDropIndex:
210+
ver, err = rollingbackDropIndex(t, job)
163211
default:
164212
job.State = model.JobStateCancelled
165213
err = errCancelledDDLJob

0 commit comments

Comments
 (0)