github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/ddl/reorg.go (about)

     1  // Copyright 2015 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package ddl
    15  
    16  import (
    17  	"fmt"
    18  	"time"
    19  
    20  	"github.com/insionng/yougam/libraries/juju/errors"
    21  	"github.com/insionng/yougam/libraries/pingcap/tidb/context"
    22  	"github.com/insionng/yougam/libraries/pingcap/tidb/kv"
    23  	"github.com/insionng/yougam/libraries/pingcap/tidb/meta"
    24  	"github.com/insionng/yougam/libraries/pingcap/tidb/model"
    25  	"github.com/insionng/yougam/libraries/pingcap/tidb/terror"
    26  )
    27  
    28  var _ context.Context = &reorgContext{}
    29  
    30  // reorgContext implements context.Context interface for reorganization use.
    31  type reorgContext struct {
    32  	store kv.Storage
    33  	m     map[fmt.Stringer]interface{}
    34  	txn   kv.Transaction
    35  }
    36  
    37  func (c *reorgContext) GetTxn(forceNew bool) (kv.Transaction, error) {
    38  	if forceNew {
    39  		if c.txn != nil {
    40  			if err := c.txn.Commit(); err != nil {
    41  				return nil, errors.Trace(err)
    42  			}
    43  			c.txn = nil
    44  		}
    45  	}
    46  
    47  	if c.txn != nil {
    48  		return c.txn, nil
    49  	}
    50  
    51  	txn, err := c.store.Begin()
    52  	if err != nil {
    53  		return nil, errors.Trace(err)
    54  	}
    55  
    56  	c.txn = txn
    57  	return c.txn, nil
    58  }
    59  
    60  func (c *reorgContext) FinishTxn(rollback bool) error {
    61  	if c.txn == nil {
    62  		return nil
    63  	}
    64  
    65  	var err error
    66  	if rollback {
    67  		err = c.txn.Rollback()
    68  	} else {
    69  		err = c.txn.Commit()
    70  	}
    71  
    72  	c.txn = nil
    73  
    74  	return errors.Trace(err)
    75  }
    76  
    77  func (c *reorgContext) SetValue(key fmt.Stringer, value interface{}) {
    78  	c.m[key] = value
    79  }
    80  
    81  func (c *reorgContext) Value(key fmt.Stringer) interface{} {
    82  	return c.m[key]
    83  }
    84  
    85  func (c *reorgContext) ClearValue(key fmt.Stringer) {
    86  	delete(c.m, key)
    87  }
    88  
    89  func (d *ddl) newReorgContext() context.Context {
    90  	c := &reorgContext{
    91  		store: d.store,
    92  		m:     make(map[fmt.Stringer]interface{}),
    93  	}
    94  
    95  	return c
    96  }
    97  
    98  const waitReorgTimeout = 10 * time.Second
    99  
   100  func (d *ddl) runReorgJob(f func() error) error {
   101  	if d.reorgDoneCh == nil {
   102  		// start a reorganization job
   103  		d.wait.Add(1)
   104  		d.reorgDoneCh = make(chan error, 1)
   105  		go func() {
   106  			defer d.wait.Done()
   107  			d.reorgDoneCh <- f()
   108  		}()
   109  	}
   110  
   111  	waitTimeout := waitReorgTimeout
   112  	// if d.lease is 0, we are using a local storage,
   113  	// and we can wait the reorganization to be done here.
   114  	// if d.lease > 0, we don't need to wait here because
   115  	// we will wait 2 * lease outer and try checking again,
   116  	// so we use a very little timeout here.
   117  	if d.lease > 0 {
   118  		waitTimeout = 1 * time.Millisecond
   119  	}
   120  
   121  	// wait reorganization job done or timeout
   122  	select {
   123  	case err := <-d.reorgDoneCh:
   124  		d.reorgDoneCh = nil
   125  		return errors.Trace(err)
   126  	case <-d.quitCh:
   127  		// we return errWaitReorgTimeout here too, so that outer loop will break.
   128  		return errWaitReorgTimeout
   129  	case <-time.After(waitTimeout):
   130  		// if timeout, we will return, check the owner and retry to wait job done again.
   131  		return errWaitReorgTimeout
   132  	}
   133  }
   134  
   135  func (d *ddl) isReorgRunnable(txn kv.Transaction) error {
   136  	if d.isClosed() {
   137  		// worker is closed, can't run reorganization.
   138  		return errors.Trace(errInvalidWorker.Gen("worker is closed"))
   139  	}
   140  
   141  	t := meta.NewMeta(txn)
   142  	owner, err := t.GetDDLJobOwner()
   143  	if err != nil {
   144  		return errors.Trace(err)
   145  	} else if owner == nil || owner.OwnerID != d.uuid {
   146  		// if no owner, we will try later, so here just return error.
   147  		// or another server is owner, return error too.
   148  		return errors.Trace(errNotOwner)
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  func (d *ddl) delKeysWithPrefix(prefix kv.Key) error {
   155  	for {
   156  		keys := make([]kv.Key, 0, maxBatchSize)
   157  		err := kv.RunInNewTxn(d.store, true, func(txn kv.Transaction) error {
   158  			if err1 := d.isReorgRunnable(txn); err1 != nil {
   159  				return errors.Trace(err1)
   160  			}
   161  
   162  			iter, err := txn.Seek(prefix)
   163  			if err != nil {
   164  				return errors.Trace(err)
   165  			}
   166  
   167  			defer iter.Close()
   168  			for i := 0; i < maxBatchSize; i++ {
   169  				if iter.Valid() && iter.Key().HasPrefix(prefix) {
   170  					keys = append(keys, iter.Key().Clone())
   171  					err = iter.Next()
   172  					if err != nil {
   173  						return errors.Trace(err)
   174  					}
   175  				} else {
   176  					break
   177  				}
   178  			}
   179  
   180  			for _, key := range keys {
   181  				err := txn.Delete(key)
   182  				// must skip ErrNotExist
   183  				// if key doesn't exist, skip this error.
   184  				if err != nil && !terror.ErrorEqual(err, kv.ErrNotExist) {
   185  					return errors.Trace(err)
   186  				}
   187  			}
   188  
   189  			return nil
   190  		})
   191  
   192  		if err != nil {
   193  			return errors.Trace(err)
   194  		}
   195  
   196  		// delete no keys, return.
   197  		if len(keys) == 0 {
   198  			return nil
   199  		}
   200  	}
   201  }
   202  
   203  type reorgInfo struct {
   204  	*model.Job
   205  	Handle int64
   206  	d      *ddl
   207  	first  bool
   208  }
   209  
   210  func (d *ddl) getReorgInfo(t *meta.Meta, job *model.Job) (*reorgInfo, error) {
   211  	var err error
   212  
   213  	info := &reorgInfo{
   214  		Job:   job,
   215  		d:     d,
   216  		first: job.SnapshotVer == 0,
   217  	}
   218  
   219  	if info.first {
   220  		// get the current version for reorganization if we don't have
   221  		var ver kv.Version
   222  		ver, err = d.store.CurrentVersion()
   223  		if err != nil {
   224  			return nil, errors.Trace(err)
   225  		} else if ver.Ver <= 0 {
   226  			return nil, errInvalidStoreVer.Gen("invalid storage current version %d", ver.Ver)
   227  		}
   228  
   229  		job.SnapshotVer = ver.Ver
   230  	} else {
   231  		info.Handle, err = t.GetDDLReorgHandle(job)
   232  		if err != nil {
   233  			return nil, errors.Trace(err)
   234  		}
   235  	}
   236  
   237  	if info.Handle > 0 {
   238  		// we have already handled this handle, so use next
   239  		info.Handle++
   240  	}
   241  
   242  	return info, errors.Trace(err)
   243  }
   244  
   245  func (r *reorgInfo) UpdateHandle(txn kv.Transaction, handle int64) error {
   246  	t := meta.NewMeta(txn)
   247  	return errors.Trace(t.UpdateDDLReorgHandle(r.Job, handle))
   248  }