github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/tests/br_rawkv/client.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/hex" 7 "flag" 8 "fmt" 9 "hash/crc64" 10 "math/rand" 11 "strings" 12 "time" 13 14 "github.com/pingcap/errors" 15 "github.com/pingcap/log" 16 "github.com/tikv/client-go/v2/config" 17 "github.com/tikv/client-go/v2/rawkv" 18 "go.uber.org/zap" 19 ) 20 21 var ( 22 ca = flag.String("ca", "", "CA certificate path for TLS connection") 23 cert = flag.String("cert", "", "certificate path for TLS connection") 24 key = flag.String("key", "", "private key path for TLS connection") 25 pdAddr = flag.String("pd", "127.0.0.1:2379", "Address of PD") 26 runMode = flag.String("mode", "", "Mode. One of 'rand-gen', 'checksum', 'scan', 'diff', 'delete' and 'put'") 27 startKeyStr = flag.String("start-key", "", "Start key in hex") 28 endKeyStr = flag.String("end-key", "", "End key in hex") 29 keyMaxLen = flag.Int("key-max-len", 32, "Max length of keys for rand-gen mode") 30 concurrency = flag.Int("concurrency", 32, "Concurrency to run rand-gen") 31 duration = flag.Int("duration", 10, "duration(second) of rand-gen") 32 putDataStr = flag.String("put-data", "", "Kv pairs to put to the cluster in hex. "+ 33 "kv pairs are separated by commas, key and value in a pair are separated by a colon") 34 ) 35 36 func createClient(addr string) (*rawkv.Client, error) { 37 cli, err := rawkv.NewClient(context.TODO(), []string{addr}, config.Security{ 38 ClusterSSLCA: *ca, 39 ClusterSSLCert: *cert, 40 ClusterSSLKey: *key, 41 }) 42 return cli, errors.Trace(err) 43 } 44 45 func main() { 46 flag.Parse() 47 48 startKey, err := hex.DecodeString(*startKeyStr) 49 if err != nil { 50 log.Panic("Invalid startKey", zap.String("starkey", *startKeyStr), zap.Error(err)) 51 } 52 endKey, err := hex.DecodeString(*endKeyStr) 53 if err != nil { 54 log.Panic("Invalid endKey: %v, err: %+v", zap.String("endkey", *endKeyStr), zap.Error(err)) 55 } 56 // For "put" mode, the key range is not used. So no need to throw error here. 57 if len(endKey) == 0 && *runMode != "put" { 58 log.Panic("Empty endKey is not supported yet") 59 } 60 61 if *runMode == "test-rand-key" { 62 testRandKey(startKey, endKey, *keyMaxLen) 63 return 64 } 65 66 client, err := createClient(*pdAddr) 67 if err != nil { 68 log.Panic("Failed to create client", zap.String("pd", *pdAddr), zap.Error(err)) 69 } 70 71 switch *runMode { 72 case "rand-gen": 73 err = randGenWithDuration(client, startKey, endKey, *keyMaxLen, *concurrency, *duration) 74 case "checksum": 75 err = checksum(client, startKey, endKey) 76 case "scan": 77 err = scan(client, startKey, endKey) 78 case "delete": 79 err = deleteRange(client, startKey, endKey) 80 case "put": 81 err = put(client, *putDataStr) 82 } 83 84 if err != nil { 85 log.Panic("Error", zap.Error(err)) 86 } 87 } 88 89 func randGenWithDuration(client *rawkv.Client, startKey, endKey []byte, 90 maxLen int, concurrency int, duration int) error { 91 var err error 92 ok := make(chan struct{}) 93 go func() { 94 err = randGen(client, startKey, endKey, maxLen, concurrency) 95 ok <- struct{}{} 96 }() 97 select { 98 case <-time.After(time.Second * time.Duration(duration)): 99 case <-ok: 100 } 101 return errors.Trace(err) 102 } 103 104 func randGen(client *rawkv.Client, startKey, endKey []byte, maxLen int, concurrency int) error { 105 log.Info("Start rand-gen", zap.Int("maxlen", maxLen), 106 zap.String("startkey", hex.EncodeToString(startKey)), zap.String("endkey", hex.EncodeToString(endKey))) 107 log.Info("Rand-gen will keep running. Please Ctrl+C to stop manually.") 108 109 // Cannot generate shorter key than commonPrefix 110 commonPrefixLen := 0 111 for ; commonPrefixLen < len(startKey) && commonPrefixLen < len(endKey) && 112 startKey[commonPrefixLen] == endKey[commonPrefixLen]; commonPrefixLen++ { 113 continue 114 } 115 116 if maxLen < commonPrefixLen { 117 return errors.Errorf("maxLen (%v) < commonPrefixLen (%v)", maxLen, commonPrefixLen) 118 } 119 120 const batchSize = 32 121 122 errCh := make(chan error, concurrency) 123 for i := 0; i < concurrency; i++ { 124 go func() { 125 for { 126 keys := make([][]byte, 0, batchSize) 127 values := make([][]byte, 0, batchSize) 128 129 for i := 0; i < batchSize; i++ { 130 key := randKey(startKey, endKey, maxLen) 131 keys = append(keys, key) 132 value := randValue() 133 values = append(values, value) 134 } 135 136 err := client.BatchPut(context.TODO(), keys, values) 137 if err != nil { 138 errCh <- errors.Trace(err) 139 } 140 } 141 }() 142 } 143 144 err := <-errCh 145 if err != nil { 146 return errors.Trace(err) 147 } 148 149 return nil 150 } 151 152 func testRandKey(startKey, endKey []byte, maxLen int) { 153 for { 154 k := randKey(startKey, endKey, maxLen) 155 if bytes.Compare(k, startKey) < 0 || bytes.Compare(k, endKey) >= 0 { 156 panic(hex.EncodeToString(k)) 157 } 158 } 159 } 160 161 func randKey(startKey, endKey []byte, maxLen int) []byte { 162 Retry: 163 for { // Regenerate on fail 164 result := make([]byte, 0, maxLen) 165 166 upperUnbounded := false 167 lowerUnbounded := false 168 169 for i := 0; i < maxLen; i++ { 170 upperBound := 256 171 if !upperUnbounded { 172 if i >= len(endKey) { 173 // The generated key is the same as endKey which is invalid. Regenerate it. 174 continue Retry 175 } 176 upperBound = int(endKey[i]) + 1 177 } 178 179 lowerBound := 0 180 if !lowerUnbounded { 181 if i >= len(startKey) { 182 lowerUnbounded = true 183 } else { 184 lowerBound = int(startKey[i]) 185 } 186 } 187 188 if lowerUnbounded { 189 if rand.Intn(257) == 0 { 190 return result 191 } 192 } 193 194 value := rand.Intn(upperBound - lowerBound) 195 value += lowerBound 196 197 if value < upperBound-1 { 198 upperUnbounded = true 199 } 200 if value > lowerBound { 201 lowerUnbounded = true 202 } 203 204 result = append(result, uint8(value)) 205 } 206 207 return result 208 } 209 } 210 211 func randValue() []byte { 212 result := make([]byte, 0, 512) 213 for i := 0; i < 512; i++ { 214 value := rand.Intn(257) 215 if value == 256 { 216 if i > 0 { 217 return result 218 } 219 value-- 220 } 221 result = append(result, uint8(value)) 222 } 223 return result 224 } 225 226 func checksum(client *rawkv.Client, startKey, endKey []byte) error { 227 log.Info("Start checkcum on range", 228 zap.String("startkey", hex.EncodeToString(startKey)), zap.String("endkey", hex.EncodeToString(endKey))) 229 230 scanner := newRawKVScanner(client, startKey, endKey) 231 digest := crc64.New(crc64.MakeTable(crc64.ECMA)) 232 233 var res uint64 234 235 for { 236 k, v, err := scanner.Next() 237 if err != nil { 238 return errors.Trace(err) 239 } 240 if len(k) == 0 { 241 break 242 } 243 _, _ = digest.Write(k) 244 _, _ = digest.Write(v) 245 res ^= digest.Sum64() 246 } 247 248 log.Info("Checksum result", zap.Uint64("checksum", res)) 249 fmt.Printf("Checksum result: %016x\n", res) 250 return nil 251 } 252 253 func deleteRange(client *rawkv.Client, startKey, endKey []byte) error { 254 log.Info("Start delete data in range", 255 zap.String("startkey", hex.EncodeToString(startKey)), zap.String("endkey", hex.EncodeToString(endKey))) 256 return client.DeleteRange(context.TODO(), startKey, endKey) 257 } 258 259 func scan(client *rawkv.Client, startKey, endKey []byte) error { 260 log.Info("Start scanning data in range", 261 zap.String("startkey", hex.EncodeToString(startKey)), zap.String("endkey", hex.EncodeToString(endKey))) 262 263 scanner := newRawKVScanner(client, startKey, endKey) 264 265 var key []byte 266 for { 267 k, v, err := scanner.Next() 268 if err != nil { 269 return errors.Trace(err) 270 } 271 if len(k) == 0 { 272 break 273 } 274 fmt.Printf("key: %v, value: %v\n", hex.EncodeToString(k), hex.EncodeToString(v)) 275 if bytes.Compare(key, k) >= 0 { 276 log.Error("Scan result is not in order", 277 zap.String("Previous key", hex.EncodeToString(key)), zap.String("Current key", hex.EncodeToString(k))) 278 } 279 } 280 281 log.Info("Finished Scanning.") 282 return nil 283 } 284 285 func put(client *rawkv.Client, dataStr string) error { 286 keys := make([][]byte, 0) 287 values := make([][]byte, 0) 288 289 for _, pairStr := range strings.Split(dataStr, ",") { 290 pair := strings.Split(pairStr, ":") 291 if len(pair) != 2 { 292 return errors.Errorf("invalid kv pair string %q", pairStr) 293 } 294 295 key, err := hex.DecodeString(strings.Trim(pair[0], " ")) 296 if err != nil { 297 return errors.Annotatef(err, "invalid kv pair string %q", pairStr) 298 } 299 value, err := hex.DecodeString(strings.Trim(pair[1], " ")) 300 if err != nil { 301 return errors.Annotatef(err, "invalid kv pair string %q", pairStr) 302 } 303 304 keys = append(keys, key) 305 values = append(values, value) 306 } 307 308 log.Info("Put rawkv data", zap.ByteStrings("keys", keys), zap.ByteStrings("values", values)) 309 310 err := client.BatchPut(context.TODO(), keys, values) 311 return errors.Trace(err) 312 } 313 314 const defaultScanBatchSize = 128 315 316 type rawKVScanner struct { 317 client *rawkv.Client 318 batchSize int 319 320 currentKey []byte 321 endKey []byte 322 323 bufferKeys [][]byte 324 bufferValues [][]byte 325 bufferCursor int 326 noMore bool 327 } 328 329 func newRawKVScanner(client *rawkv.Client, startKey, endKey []byte) *rawKVScanner { 330 return &rawKVScanner{ 331 client: client, 332 batchSize: defaultScanBatchSize, 333 334 currentKey: startKey, 335 endKey: endKey, 336 337 noMore: false, 338 } 339 } 340 341 func (s *rawKVScanner) Next() ([]byte, []byte, error) { 342 if s.bufferCursor >= len(s.bufferKeys) { 343 if s.noMore { 344 return nil, nil, nil 345 } 346 347 s.bufferCursor = 0 348 349 batchSize := s.batchSize 350 var err error 351 s.bufferKeys, s.bufferValues, err = s.client.Scan(context.TODO(), s.currentKey, s.endKey, batchSize) 352 if err != nil { 353 return nil, nil, errors.Trace(err) 354 } 355 356 if len(s.bufferKeys) < batchSize { 357 s.noMore = true 358 } 359 360 if len(s.bufferKeys) == 0 { 361 return nil, nil, nil 362 } 363 364 bufferKey := s.bufferKeys[len(s.bufferKeys)-1] 365 bufferKey = append(bufferKey, 0) 366 s.currentKey = bufferKey 367 } 368 369 key := s.bufferKeys[s.bufferCursor] 370 value := s.bufferValues[s.bufferCursor] 371 s.bufferCursor++ 372 return key, value, nil 373 }