You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

388 lines
9.3 KiB

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package admin_test
import (
"testing"
. "github.com/pingcap/check"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/parser/terror"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/meta"
"github.com/pingcap/tidb/store/mockstore"
. "github.com/pingcap/tidb/util/admin"
"github.com/pingcap/tidb/util/mock"
"github.com/pingcap/tidb/util/testleak"
)
func TestT(t *testing.T) {
CustomVerboseFlag = true
TestingT(t)
}
var _ = Suite(&testSuite{})
type testSuite struct {
store kv.Storage
ctx *mock.Context
}
func (s *testSuite) SetUpSuite(c *C) {
testleak.BeforeTest()
var err error
s.store, err = mockstore.NewMockTikvStore()
c.Assert(err, IsNil)
s.ctx = mock.NewContext()
s.ctx.Store = s.store
}
func (s *testSuite) TearDownSuite(c *C) {
err := s.store.Close()
c.Assert(err, IsNil)
testleak.AfterTest(c)()
}
func (s *testSuite) TestGetDDLInfo(c *C) {
txn, err := s.store.Begin()
c.Assert(err, IsNil)
t := meta.NewMeta(txn)
dbInfo2 := &model.DBInfo{
ID: 2,
Name: model.NewCIStr("b"),
State: model.StateNone,
}
job := &model.Job{
SchemaID: dbInfo2.ID,
Type: model.ActionCreateSchema,
RowCount: 0,
}
job1 := &model.Job{
SchemaID: dbInfo2.ID,
Type: model.ActionAddIndex,
RowCount: 0,
}
err = t.EnQueueDDLJob(job)
c.Assert(err, IsNil)
info, err := GetDDLInfo(txn)
c.Assert(err, IsNil)
c.Assert(info.Jobs, HasLen, 1)
c.Assert(info.Jobs[0], DeepEquals, job)
c.Assert(info.ReorgHandle, Equals, int64(0))
// Two jobs.
t = meta.NewMeta(txn, meta.AddIndexJobListKey)
err = t.EnQueueDDLJob(job1)
c.Assert(err, IsNil)
info, err = GetDDLInfo(txn)
c.Assert(err, IsNil)
c.Assert(info.Jobs, HasLen, 2)
c.Assert(info.Jobs[0], DeepEquals, job)
c.Assert(info.Jobs[1], DeepEquals, job1)
c.Assert(info.ReorgHandle, Equals, int64(0))
err = txn.Rollback()
c.Assert(err, IsNil)
}
func (s *testSuite) TestGetDDLJobs(c *C) {
txn, err := s.store.Begin()
c.Assert(err, IsNil)
t := meta.NewMeta(txn)
cnt := 10
jobs := make([]*model.Job, cnt)
var currJobs2 []*model.Job
for i := 0; i < cnt; i++ {
jobs[i] = &model.Job{
ID: int64(i),
SchemaID: 1,
Type: model.ActionCreateTable,
}
err = t.EnQueueDDLJob(jobs[i])
c.Assert(err, IsNil)
currJobs, err1 := GetDDLJobs(txn)
c.Assert(err1, IsNil)
c.Assert(currJobs, HasLen, i+1)
currJobs2 = currJobs2[:0]
err = IterAllDDLJobs(txn, func(jobs []*model.Job) (b bool, e error) {
for _, job := range jobs {
if job.State == model.JobStateNone {
currJobs2 = append(currJobs2, job)
} else {
return true, nil
}
}
return false, nil
})
c.Assert(err, IsNil)
c.Assert(currJobs2, HasLen, i+1)
}
currJobs, err := GetDDLJobs(txn)
c.Assert(err, IsNil)
for i, job := range jobs {
c.Assert(job.ID, Equals, currJobs[i].ID)
c.Assert(job.SchemaID, Equals, int64(1))
c.Assert(job.Type, Equals, model.ActionCreateTable)
}
c.Assert(currJobs, DeepEquals, currJobs2)
err = txn.Rollback()
c.Assert(err, IsNil)
}
func isJobsSorted(jobs []*model.Job) bool {
if len(jobs) <= 1 {
return true
}
for i := 1; i < len(jobs); i++ {
if jobs[i].ID <= jobs[i-1].ID {
return false
}
}
return true
}
func enQueueDDLJobs(c *C, t *meta.Meta, jobType model.ActionType, start, end int) {
for i := start; i < end; i++ {
job := &model.Job{
ID: int64(i),
SchemaID: 1,
Type: jobType,
}
err := t.EnQueueDDLJob(job)
c.Assert(err, IsNil)
}
}
func (s *testSuite) TestGetDDLJobsIsSort(c *C) {
txn, err := s.store.Begin()
c.Assert(err, IsNil)
// insert 5 drop table jobs to DefaultJobListKey queue
t := meta.NewMeta(txn)
enQueueDDLJobs(c, t, model.ActionDropTable, 10, 15)
// insert 5 create table jobs to DefaultJobListKey queue
enQueueDDLJobs(c, t, model.ActionCreateTable, 0, 5)
// insert add index jobs to AddIndexJobListKey queue
t = meta.NewMeta(txn, meta.AddIndexJobListKey)
enQueueDDLJobs(c, t, model.ActionAddIndex, 5, 10)
currJobs, err := GetDDLJobs(txn)
c.Assert(err, IsNil)
c.Assert(currJobs, HasLen, 15)
isSort := isJobsSorted(currJobs)
c.Assert(isSort, Equals, true)
err = txn.Rollback()
c.Assert(err, IsNil)
}
func (s *testSuite) TestCancelJobs(c *C) {
txn, err := s.store.Begin()
c.Assert(err, IsNil)
t := meta.NewMeta(txn)
cnt := 10
ids := make([]int64, cnt)
for i := 0; i < cnt; i++ {
job := &model.Job{
ID: int64(i),
SchemaID: 1,
Type: model.ActionCreateTable,
}
if i == 0 {
job.State = model.JobStateDone
}
if i == 1 {
job.State = model.JobStateCancelled
}
ids[i] = int64(i)
err = t.EnQueueDDLJob(job)
c.Assert(err, IsNil)
}
errs, err := CancelJobs(txn, ids)
c.Assert(err, IsNil)
for i, err := range errs {
if i == 0 {
c.Assert(err, NotNil)
continue
}
c.Assert(err, IsNil)
}
errs, err = CancelJobs(txn, []int64{})
c.Assert(err, IsNil)
c.Assert(errs, IsNil)
errs, err = CancelJobs(txn, []int64{-1})
c.Assert(err, IsNil)
c.Assert(errs[0], NotNil)
c.Assert(errs[0].Error(), Matches, "*DDL Job:-1 not found")
// test cancel finish job.
job := &model.Job{
ID: 100,
SchemaID: 1,
Type: model.ActionCreateTable,
State: model.JobStateDone,
}
err = t.EnQueueDDLJob(job)
c.Assert(err, IsNil)
errs, err = CancelJobs(txn, []int64{100})
c.Assert(err, IsNil)
c.Assert(errs[0], NotNil)
c.Assert(errs[0].Error(), Matches, "*This job:100 is finished, so can't be cancelled")
// test can't cancelable job.
job.Type = model.ActionDropIndex
job.SchemaState = model.StateWriteOnly
job.State = model.JobStateRunning
job.ID = 101
err = t.EnQueueDDLJob(job)
c.Assert(err, IsNil)
errs, err = CancelJobs(txn, []int64{101})
c.Assert(err, IsNil)
c.Assert(errs[0], NotNil)
c.Assert(errs[0].Error(), Matches, "*This job:101 is almost finished, can't be cancelled now")
// When both types of jobs exist in the DDL queue,
// we first cancel the job with a larger ID.
job = &model.Job{
ID: 1000,
SchemaID: 1,
TableID: 2,
Type: model.ActionAddIndex,
}
job1 := &model.Job{
ID: 1001,
SchemaID: 1,
TableID: 2,
Type: model.ActionAddColumn,
}
job2 := &model.Job{
ID: 1002,
SchemaID: 1,
TableID: 2,
Type: model.ActionAddIndex,
}
job3 := &model.Job{
ID: 1003,
SchemaID: 1,
TableID: 2,
Type: model.ActionRepairTable,
}
err = t.EnQueueDDLJob(job, meta.AddIndexJobListKey)
c.Assert(err, IsNil)
err = t.EnQueueDDLJob(job1)
c.Assert(err, IsNil)
err = t.EnQueueDDLJob(job2, meta.AddIndexJobListKey)
c.Assert(err, IsNil)
err = t.EnQueueDDLJob(job3)
c.Assert(err, IsNil)
errs, err = CancelJobs(txn, []int64{job1.ID, job.ID, job2.ID, job3.ID})
c.Assert(err, IsNil)
for _, err := range errs {
c.Assert(err, IsNil)
}
err = txn.Rollback()
c.Assert(err, IsNil)
}
func (s *testSuite) TestGetHistoryDDLJobs(c *C) {
txn, err := s.store.Begin()
c.Assert(err, IsNil)
t := meta.NewMeta(txn)
cnt := 11
jobs := make([]*model.Job, cnt)
for i := 0; i < cnt; i++ {
jobs[i] = &model.Job{
ID: int64(i),
SchemaID: 1,
Type: model.ActionCreateTable,
}
err = t.AddHistoryDDLJob(jobs[i], true)
c.Assert(err, IsNil)
historyJobs, err1 := GetHistoryDDLJobs(txn, DefNumHistoryJobs)
c.Assert(err1, IsNil)
if i+1 > MaxHistoryJobs {
c.Assert(historyJobs, HasLen, MaxHistoryJobs)
} else {
c.Assert(historyJobs, HasLen, i+1)
}
}
delta := cnt - MaxHistoryJobs
historyJobs, err := GetHistoryDDLJobs(txn, DefNumHistoryJobs)
c.Assert(err, IsNil)
c.Assert(historyJobs, HasLen, MaxHistoryJobs)
l := len(historyJobs) - 1
for i, job := range historyJobs {
c.Assert(job.ID, Equals, jobs[delta+l-i].ID)
c.Assert(job.SchemaID, Equals, int64(1))
c.Assert(job.Type, Equals, model.ActionCreateTable)
}
var historyJobs2 []*model.Job
err = IterHistoryDDLJobs(txn, func(jobs []*model.Job) (b bool, e error) {
for _, job := range jobs {
historyJobs2 = append(historyJobs2, job)
if len(historyJobs2) == DefNumHistoryJobs {
return true, nil
}
}
return false, nil
})
c.Assert(err, IsNil)
c.Assert(historyJobs2, DeepEquals, historyJobs)
err = txn.Rollback()
c.Assert(err, IsNil)
}
func (s *testSuite) TestIsJobRollbackable(c *C) {
cases := []struct {
tp model.ActionType
state model.SchemaState
result bool
}{
{model.ActionDropIndex, model.StateNone, true},
{model.ActionDropIndex, model.StateDeleteOnly, false},
{model.ActionDropSchema, model.StateDeleteOnly, false},
{model.ActionDropColumn, model.StateDeleteOnly, false},
}
job := &model.Job{}
for _, ca := range cases {
job.Type = ca.tp
job.SchemaState = ca.state
re := IsJobRollbackable(job)
c.Assert(re == ca.result, IsTrue)
}
}
func (s *testSuite) TestError(c *C) {
kvErrs := []*terror.Error{
ErrDataInConsistent,
ErrDDLJobNotFound,
ErrCancelFinishedDDLJob,
ErrCannotCancelDDLJob,
}
for _, err := range kvErrs {
code := terror.ToSQLError(err).Code
c.Assert(code != mysql.ErrUnknown && code == uint16(err.Code()), IsTrue, Commentf("err: %v", err))
}
}