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

     1  package tidb
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"encoding/json"
     7  	"fmt"
     8  	"hash/fnv"
     9  	"log"
    10  	"math/rand"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/pingcap/chaos/pkg/generator"
    15  	"github.com/pingcap/chaos/pkg/history"
    16  
    17  	"github.com/pingcap/chaos/pkg/core"
    18  )
    19  
    20  const (
    21  	tablePrefix = "seq_"
    22  	tpRead      = "read"
    23  	tpWrite     = "write"
    24  )
    25  
    26  type seqRequest struct {
    27  	Tp string
    28  	K  int
    29  }
    30  
    31  type seqResponse struct {
    32  	Ok      bool
    33  	K       int
    34  	V       []string
    35  	Unknown bool
    36  }
    37  
    38  var (
    39  	_ core.UnknownResponse = (*seqResponse)(nil)
    40  
    41  	queue = struct {
    42  		mu    sync.Mutex
    43  		q     []int
    44  		count int
    45  		r     *rand.Rand
    46  	}{
    47  		mu: sync.Mutex{},
    48  		q:  make([]int, 0, 10), // TODO: make the cap configurable.
    49  		r:  rand.New(rand.NewSource(time.Now().UnixNano())),
    50  	}
    51  )
    52  
    53  // IsUnknown implements UnknownResponse interface
    54  func (r seqResponse) IsUnknown() bool {
    55  	return r.Unknown
    56  }
    57  
    58  type sequentialClient struct {
    59  	db         *sql.DB
    60  	tableCount int
    61  	keyCount   int
    62  	gen        generator.Generator
    63  }
    64  
    65  // SequentialClientCreator creates a bank test client for tidb.
    66  type SequentialClientCreator struct {
    67  }
    68  
    69  func genRequest() interface{} {
    70  	queue.mu.Lock()
    71  	defer queue.mu.Unlock()
    72  	c := cap(queue.q)
    73  	l := len(queue.q)
    74  	k := queue.count
    75  	// The first len(queue.q) requests are all write requests.
    76  	if k < c {
    77  		goto WRITE
    78  	} else if l == c {
    79  		// Read if the queue is full
    80  		goto READ
    81  	} else if l <= 3 {
    82  		// Write if the queue is empty
    83  		goto WRITE
    84  	} else if queue.r.Int()%2 == 1 {
    85  		goto READ
    86  	} else {
    87  		goto WRITE
    88  	}
    89  
    90  READ:
    91  	// Read
    92  	// Pop the front
    93  	k, queue.q = queue.q[0], append([]int{}, queue.q[1:]...)
    94  	return seqRequest{Tp: tpRead, K: k}
    95  WRITE:
    96  	// Write
    97  	// Push the end
    98  	queue.count++
    99  	queue.q = append(queue.q, k)
   100  	return seqRequest{Tp: tpWrite, K: k}
   101  }
   102  
   103  // Create creates a new SequentialClient.
   104  func (SequentialClientCreator) Create(node string) core.Client {
   105  	return &sequentialClient{
   106  		tableCount: 3,
   107  		keyCount:   5,
   108  		gen:        generator.Stagger(time.Millisecond*10, genRequest),
   109  	}
   110  }
   111  
   112  func (c *sequentialClient) tableNames() []string {
   113  	names := make([]string, 0, c.tableCount)
   114  	for i := 0; i < c.tableCount; i++ {
   115  		names = append(names, fmt.Sprintf("%s%d", tablePrefix, i))
   116  	}
   117  	return names
   118  }
   119  
   120  func subkeys(keyCount int, k int) []string {
   121  	ks := make([]string, 0, keyCount)
   122  	for i := 0; i < keyCount; i++ {
   123  		ks = append(ks, fmt.Sprintf("%d_%d", k, i))
   124  	}
   125  	return ks
   126  }
   127  
   128  func hash(s string) int {
   129  	h := fnv.New32a()
   130  	h.Write([]byte(s))
   131  	return int(h.Sum32())
   132  }
   133  
   134  func key2table(tableCount int, key string) string {
   135  	return fmt.Sprintf("%s%d", tablePrefix, hash(key)%tableCount)
   136  }
   137  
   138  // SetUp sets up the client.
   139  func (c *sequentialClient) SetUp(ctx context.Context, nodes []string, node string) error {
   140  	db, err := sql.Open("mysql", fmt.Sprintf("root@tcp(%s:4000)/test", node))
   141  	if err != nil {
   142  		return err
   143  	}
   144  	c.db = db
   145  
   146  	db.SetMaxIdleConns(1 + c.tableCount)
   147  
   148  	// Do SetUp in the first node
   149  	if node != nodes[0] {
   150  		return nil
   151  	}
   152  
   153  	tableNames := c.tableNames()
   154  	log.Printf("begin to create table %v on node %s", tableNames, node)
   155  	for _, tableName := range tableNames {
   156  		log.Printf("try to drop table %s", tableName)
   157  		if _, err = db.ExecContext(ctx,
   158  			fmt.Sprintf(" drop table if exists %s", tableName)); err != nil {
   159  			return err
   160  		}
   161  		sql := `create table if not exists %s (tkey varchar(255) primary key)`
   162  		if _, err = db.ExecContext(ctx, fmt.Sprintf(sql, tableName)); err != nil {
   163  			return err
   164  		}
   165  		log.Printf("created table %s", tableName)
   166  	}
   167  
   168  	return nil
   169  }
   170  
   171  // TearDown tears down the client.
   172  func (c *sequentialClient) TearDown(ctx context.Context, nodes []string, node string) error {
   173  	return c.db.Close()
   174  }
   175  
   176  // Invoke invokes a request to the database.
   177  // Mostly, the return Response should implement UnknownResponse interface
   178  func (c *sequentialClient) Invoke(ctx context.Context, node string, r interface{}) interface{} {
   179  	req := r.(seqRequest)
   180  	ks := subkeys(c.keyCount, req.K)
   181  	if req.Tp == tpWrite {
   182  		for _, k := range ks {
   183  			sql := fmt.Sprintf("insert into %s values (?)", key2table(c.tableCount, k))
   184  			_, err := c.db.ExecContext(ctx, sql, k)
   185  			if err != nil {
   186  				// TODO: retry on conflict
   187  				log.Println(err)
   188  				return seqResponse{Ok: false}
   189  			}
   190  		}
   191  		return seqResponse{Ok: true}
   192  	} else if req.Tp == tpRead {
   193  		vs := make([]string, 0, len(ks))
   194  		for i := len(ks) - 1; i >= 0; i-- {
   195  			k := ks[i]
   196  			sql := fmt.Sprintf("select tkey from %s where tkey = ?", key2table(c.tableCount, k))
   197  			var v string
   198  			err := c.db.QueryRowContext(ctx, sql, k).Scan(&v)
   199  			if err != nil {
   200  				// TODO: retry on conflict
   201  				log.Println(err)
   202  				return seqResponse{Ok: false}
   203  			}
   204  			vs = append(vs, v)
   205  		}
   206  		resp := seqResponse{
   207  			Ok: true,
   208  			K:  req.K,
   209  			V:  vs,
   210  		}
   211  		return resp
   212  	} else {
   213  		panic(fmt.Sprintf("unknown req %v", req))
   214  	}
   215  }
   216  
   217  // NextRequest generates a request for latter Invoke.
   218  func (c *sequentialClient) NextRequest() interface{} {
   219  	return c.gen()
   220  }
   221  
   222  // DumpState the database state(also the model's state)
   223  func (c *sequentialClient) DumpState(ctx context.Context) (interface{}, error) {
   224  	return nil, nil
   225  }
   226  
   227  // NewSequentialChecker returns a new sequentialChecker.
   228  func NewSequentialChecker() core.Checker {
   229  	return sequentialChecker{}
   230  }
   231  
   232  // sequentialChecker checks whether a history is sequential.
   233  type sequentialChecker struct{}
   234  
   235  // Check checks the sequential history.
   236  func (sequentialChecker) Check(_ core.Model, ops []core.Operation) (bool, error) {
   237  	for _, op := range ops {
   238  		if op.Action == core.ReturnOperation {
   239  			resp := op.Data.(seqResponse)
   240  			if !resp.Unknown && resp.Ok {
   241  				foundNoneEmpty := false
   242  				for _, s := range resp.V {
   243  					if !foundNoneEmpty {
   244  						foundNoneEmpty = s != ""
   245  					} else if s == "" {
   246  						log.Printf("Find no sequential op %+v", resp)
   247  						return false, nil
   248  					}
   249  				}
   250  			}
   251  		}
   252  	}
   253  	return true, nil
   254  }
   255  
   256  // Name returns the name of the verifier.
   257  func (sequentialChecker) Name() string {
   258  	return "sequential_checker"
   259  }
   260  
   261  type parser struct{}
   262  
   263  // OnRequest impls history.RecordParser.OnRequest
   264  func (p parser) OnRequest(data json.RawMessage) (interface{}, error) {
   265  	r := seqRequest{}
   266  	err := json.Unmarshal(data, &r)
   267  	return r, err
   268  }
   269  
   270  // OnResponse impls history.RecordParser.OnRequest
   271  func (p parser) OnResponse(data json.RawMessage) (interface{}, error) {
   272  	r := seqResponse{}
   273  	err := json.Unmarshal(data, &r)
   274  	if r.Unknown {
   275  		return nil, err
   276  	}
   277  	return r, err
   278  }
   279  
   280  // OnNoopResponse impls history.RecordParser.OnRequest
   281  func (p parser) OnNoopResponse() interface{} {
   282  	return seqResponse{Unknown: true}
   283  }
   284  
   285  func (p parser) OnState(data json.RawMessage) (interface{}, error) {
   286  	return nil, nil
   287  }
   288  
   289  // NewSequentialParser returns a parser parses a history of bank operations.
   290  func NewSequentialParser() history.RecordParser {
   291  	return parser{}
   292  }