github.com/pingcap/chaos@v0.0.0-20190710112158-c86faf4b3719/db/tidb/long_fork.go (about)

     1  package tidb
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"encoding/binary"
     7  	"encoding/json"
     8  	"fmt"
     9  	"hash/fnv"
    10  	"log"
    11  	"math/rand"
    12  	"sort"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/pingcap/chaos/pkg/core"
    17  	"github.com/pingcap/chaos/pkg/history"
    18  )
    19  
    20  const (
    21  	lfRead      = "read"
    22  	lfWrite     = "write"
    23  	lfGroupSize = 10
    24  )
    25  
    26  type lfRequest struct {
    27  	Kind string
    28  	// length=1 for write
    29  	Keys []uint64
    30  }
    31  type lfResponse struct {
    32  	Ok      bool
    33  	Unknown bool
    34  	Keys    []uint64
    35  	Values  []sql.NullInt64
    36  }
    37  
    38  var (
    39  	lfState = struct {
    40  		mu      sync.Mutex
    41  		nextKey uint64
    42  		workers map[string]uint64
    43  	}{
    44  		mu:      sync.Mutex{},
    45  		nextKey: 0,
    46  		workers: make(map[string]uint64),
    47  	}
    48  )
    49  
    50  type longForkClient struct {
    51  	db         *sql.DB
    52  	r          *rand.Rand
    53  	tableCount int
    54  	node       string
    55  }
    56  
    57  func lfTableNames(tableCount int) []string {
    58  	names := make([]string, 0, tableCount)
    59  	for i := 0; i < tableCount; i++ {
    60  		names = append(names, fmt.Sprintf("txn_lf_%d", i))
    61  	}
    62  	return names
    63  }
    64  
    65  func lfKey2Table(tableCount int, key uint64) string {
    66  	b := make([]byte, 8)
    67  	binary.PutUvarint(b, key)
    68  	h := fnv.New32a()
    69  	h.Write(b)
    70  	hash := int(h.Sum32())
    71  	return fmt.Sprintf("txn_lf_%d", hash%tableCount)
    72  }
    73  
    74  func (c *longForkClient) SetUp(ctx context.Context, nodes []string, node string) error {
    75  	c.r = rand.New(rand.NewSource(time.Now().UnixNano()))
    76  	db, err := sql.Open("mysql", fmt.Sprintf("root@tcp(%s:4000)/test", node))
    77  	if err != nil {
    78  		return err
    79  	}
    80  	c.db = db
    81  
    82  	db.SetMaxIdleConns(1 + c.tableCount)
    83  
    84  	// Do SetUp in the first node
    85  	if node != nodes[0] {
    86  		return nil
    87  	}
    88  
    89  	log.Printf("begin to create %v tables on node %s", c.tableCount, node)
    90  	for _, tableName := range lfTableNames(c.tableCount) {
    91  		log.Printf("try to drop table %s", tableName)
    92  		if _, err = db.ExecContext(ctx,
    93  			fmt.Sprintf("drop table if exists %s", tableName)); err != nil {
    94  			return err
    95  		}
    96  		query := "create table if not exists %s (id int not null primary key,sk int not null,val int)"
    97  		if _, err = db.ExecContext(ctx, fmt.Sprintf(query, tableName)); err != nil {
    98  			return err
    99  		}
   100  		log.Printf("created table %s", tableName)
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  func (c *longForkClient) TearDown(ctx context.Context, nodes []string, node string) error {
   107  	return c.db.Close()
   108  }
   109  
   110  func (c *longForkClient) Invoke(ctx context.Context, node string, r interface{}) interface{} {
   111  	arg := r.(lfRequest)
   112  	if arg.Kind == lfWrite {
   113  
   114  		key := arg.Keys[0]
   115  		query := fmt.Sprintf("insert into %s (id, sk, val) values (?, ?, ?) on duplicate key update val = ?", lfKey2Table(c.tableCount, key))
   116  		_, err := c.db.ExecContext(ctx, query, key, key, 1, 1)
   117  		if err != nil {
   118  			return lfResponse{Ok: false}
   119  		}
   120  		values := make([]sql.NullInt64, 0)
   121  		return lfResponse{Ok: true, Keys: []uint64{key}, Values: values}
   122  
   123  	} else if arg.Kind == lfRead {
   124  
   125  		txn, err := c.db.Begin()
   126  		defer txn.Rollback()
   127  		if err != nil {
   128  			return lfResponse{Ok: false}
   129  		}
   130  
   131  		values := make([]sql.NullInt64, len(arg.Keys))
   132  		for i, key := range arg.Keys {
   133  			query := fmt.Sprintf("select (val) from %s where id = ?", lfKey2Table(c.tableCount, key))
   134  			err := txn.QueryRowContext(ctx, query, key).Scan(&values[i])
   135  			if err != nil {
   136  				if err == sql.ErrNoRows {
   137  					values[i] = sql.NullInt64{Valid: false}
   138  				} else {
   139  					return lfResponse{Ok: false}
   140  				}
   141  			}
   142  		}
   143  
   144  		if err = txn.Commit(); err != nil {
   145  			return lfResponse{Unknown: true, Keys: arg.Keys, Values: values}
   146  		}
   147  		return lfResponse{Ok: true, Keys: arg.Keys, Values: values}
   148  
   149  	} else {
   150  		panic(fmt.Sprintf("unknown req %v", r))
   151  	}
   152  }
   153  
   154  func (c *longForkClient) NextRequest() interface{} {
   155  	lfState.mu.Lock()
   156  	defer lfState.mu.Unlock()
   157  
   158  	key, present := lfState.workers[c.node]
   159  	if present {
   160  		delete(lfState.workers, c.node)
   161  		return lfRequest{Kind: lfRead, Keys: makeKeysInGroup(c.r, lfGroupSize, key)}
   162  	}
   163  
   164  	if c.r.Int()%2 == 0 {
   165  		if size := len(lfState.workers); size > 0 {
   166  			others := make([]uint64, size)
   167  			idx := 0
   168  			for _, value := range lfState.workers {
   169  				others[idx] = value
   170  				idx++
   171  			}
   172  			key := others[c.r.Intn(size)]
   173  			return lfRequest{Kind: lfRead, Keys: makeKeysInGroup(c.r, lfGroupSize, key)}
   174  		}
   175  	}
   176  
   177  	key = lfState.nextKey
   178  	lfState.nextKey++
   179  	lfState.workers[c.node] = key
   180  	return lfRequest{Kind: lfWrite, Keys: []uint64{key}}
   181  }
   182  
   183  func (c *longForkClient) DumpState(ctx context.Context) (interface{}, error) {
   184  	return nil, nil
   185  }
   186  
   187  func makeKeysInGroup(r *rand.Rand, groupSize uint64, key uint64) []uint64 {
   188  	lower := key - key%groupSize
   189  	base := r.Perm(int(groupSize))
   190  	result := make([]uint64, groupSize)
   191  	for i, num := range base {
   192  		result[i] = uint64(num) + lower
   193  	}
   194  	return result
   195  }
   196  
   197  // LongForkClientCreator creates long fork test clients for tidb.
   198  type LongForkClientCreator struct {
   199  }
   200  
   201  // Create creates a new longForkClient.
   202  func (LongForkClientCreator) Create(node string) core.Client {
   203  	return &longForkClient{
   204  		tableCount: 7,
   205  		node:       node,
   206  	}
   207  }
   208  
   209  type lfParser struct{}
   210  
   211  func (p lfParser) OnRequest(data json.RawMessage) (interface{}, error) {
   212  	r := lfRequest{}
   213  	err := json.Unmarshal(data, &r)
   214  	return r, err
   215  }
   216  
   217  func (p lfParser) OnResponse(data json.RawMessage) (interface{}, error) {
   218  	r := lfResponse{}
   219  	err := json.Unmarshal(data, &r)
   220  	// I have no idea why we need this
   221  	if r.Unknown {
   222  		return nil, err
   223  	}
   224  	return r, err
   225  }
   226  
   227  func (p lfParser) OnNoopResponse() interface{} {
   228  	return lfResponse{Unknown: true}
   229  }
   230  
   231  func (p lfParser) OnState(data json.RawMessage) (interface{}, error) {
   232  	return nil, nil
   233  }
   234  
   235  // LongForkParser parses a history of long fork test.
   236  func LongForkParser() history.RecordParser {
   237  	return lfParser{}
   238  }
   239  
   240  type lfChecker struct{}
   241  
   242  func ensureNoLongForks(ops []core.Operation, groupSize int) (bool, error) {
   243  	// why we cannot have something like map<vec<T>,T> in golang?
   244  	keyset := make(map[string][]uint64)
   245  	groups := make(map[string][][]sql.NullInt64)
   246  	for _, op := range ops {
   247  		if op.Action != core.ReturnOperation {
   248  			continue
   249  		}
   250  		res := op.Data.(lfResponse)
   251  		// you con not get request from the response...
   252  		if len(res.Values) == 0 {
   253  			// it's a write
   254  			continue
   255  		}
   256  		if !res.Ok || res.Unknown {
   257  			continue
   258  		}
   259  		if len(res.Keys) != groupSize || len(res.Values) != groupSize {
   260  			return false, fmt.Errorf("The read respond should have %v keys and %v values, but it has %v keys and %v values",
   261  				groupSize, groupSize, len(res.Keys), len(res.Values))
   262  		}
   263  		type pair struct {
   264  			key   uint64
   265  			value sql.NullInt64
   266  		}
   267  		//sort key
   268  		pairs := make([]pair, groupSize)
   269  		for i := 0; i < groupSize; i++ {
   270  			pairs[i] = pair{key: res.Keys[i], value: res.Values[i]}
   271  		}
   272  		sort.Slice(pairs, func(i, j int) bool { return pairs[i].key < pairs[j].key })
   273  		keys := make([]uint64, groupSize)
   274  		values := make([]sql.NullInt64, groupSize)
   275  		for i := 0; i < groupSize; i++ {
   276  			keys[i] = pairs[i].key
   277  			values[i] = pairs[i].value
   278  		}
   279  		str := fmt.Sprintf("%v", keys)
   280  		groups[str] = append(groups[str], values)
   281  		keyset[str] = keys
   282  	}
   283  	for str, results := range groups {
   284  		keys := keyset[str]
   285  		count := len(results)
   286  		for p := 0; p < count; p++ {
   287  			for q := p + 1; q < count; q++ {
   288  				values1 := results[p]
   289  				values2 := results[q]
   290  				//compare!
   291  				var result int
   292  				for i := 0; i < groupSize; i++ {
   293  					present1 := values1[i].Valid
   294  					present2 := values2[i].Valid
   295  					if present1 && !present2 {
   296  						if result > 0 {
   297  							log.Printf("Detected fork in history, read to %v returns %v and %v", keys, values1, values2)
   298  							return false, nil
   299  						}
   300  						result = -1
   301  					}
   302  					if !present1 && present2 {
   303  						if result < 0 {
   304  							log.Printf("Detected fork in history, read to %v returns %v and %v", keys, values1, values2)
   305  							return false, nil
   306  						}
   307  						result = 1
   308  					}
   309  					if present1 && present2 {
   310  						if values1[i] != values2[i] {
   311  							return false, fmt.Errorf("The key %v was write twice since it had two different values %v and %v",
   312  								keys[i], values1[i], values2[i])
   313  						}
   314  					}
   315  				}
   316  			}
   317  		}
   318  	}
   319  	return true, nil
   320  }
   321  
   322  func ensureNoMultipleWritesToOneKey(ops []core.Operation) (bool, error) {
   323  	keySet := make(map[uint64]bool)
   324  	for _, op := range ops {
   325  		if op.Action != core.InvokeOperation {
   326  			continue
   327  		}
   328  		req := op.Data.(lfRequest)
   329  		if req.Kind != lfWrite {
   330  			continue
   331  		}
   332  		for _, key := range req.Keys {
   333  			if _, prs := keySet[key]; prs {
   334  				return false, fmt.Errorf("The key %v was written twice", key)
   335  			}
   336  			keySet[key] = true
   337  		}
   338  	}
   339  	return true, nil
   340  }
   341  
   342  func (lfChecker) Check(_ core.Model, ops []core.Operation) (bool, error) {
   343  	if ok, err := ensureNoMultipleWritesToOneKey(ops); err != nil {
   344  		return false, err
   345  	} else if !ok {
   346  		return false, nil
   347  	}
   348  	if ok, err := ensureNoLongForks(ops, lfGroupSize); err != nil {
   349  		return false, err
   350  	} else if !ok {
   351  		return false, nil
   352  	}
   353  	return true, nil
   354  }
   355  
   356  func (lfChecker) Name() string {
   357  	return "tidb_long_fork_checker"
   358  }
   359  
   360  // LongForkChecker checks the long fork test history.
   361  func LongForkChecker() core.Checker {
   362  	return lfChecker{}
   363  }