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 }