github.com/KinWaiYuen/client-go/v2@v2.5.4/rawkv/rawkv.go (about) 1 // Copyright 2021 TiKV Authors 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // NOTE: The code in this file is based on code from the 16 // TiDB project, licensed under the Apache License v 2.0 17 // 18 // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/rawkv.go 19 // 20 21 // Copyright 2016 PingCAP, Inc. 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package rawkv 36 37 import ( 38 "bytes" 39 "context" 40 "time" 41 42 "github.com/KinWaiYuen/client-go/v2/config" 43 tikverr "github.com/KinWaiYuen/client-go/v2/error" 44 "github.com/KinWaiYuen/client-go/v2/internal/client" 45 "github.com/KinWaiYuen/client-go/v2/internal/kvrpc" 46 "github.com/KinWaiYuen/client-go/v2/internal/locate" 47 "github.com/KinWaiYuen/client-go/v2/internal/logutil" 48 "github.com/KinWaiYuen/client-go/v2/internal/retry" 49 "github.com/KinWaiYuen/client-go/v2/metrics" 50 "github.com/KinWaiYuen/client-go/v2/tikvrpc" 51 "github.com/pingcap/errors" 52 "github.com/pingcap/kvproto/pkg/kvrpcpb" 53 pd "github.com/tikv/pd/client" 54 "go.uber.org/zap" 55 ) 56 57 var ( 58 // MaxRawKVScanLimit is the maximum scan limit for rawkv Scan. 59 MaxRawKVScanLimit = 10240 60 // ErrMaxScanLimitExceeded is returned when the limit for rawkv Scan is to large. 61 ErrMaxScanLimitExceeded = errors.New("limit should be less than MaxRawKVScanLimit") 62 ) 63 64 const ( 65 // rawBatchPutSize is the maximum size limit for rawkv each batch put request. 66 rawBatchPutSize = 16 * 1024 67 // rawBatchPairCount is the maximum limit for rawkv each batch get/delete request. 68 rawBatchPairCount = 512 69 ) 70 71 // Client is a client of TiKV server which is used as a key-value storage, 72 // only GET/PUT/DELETE commands are supported. 73 type Client struct { 74 clusterID uint64 75 regionCache *locate.RegionCache 76 pdClient pd.Client 77 rpcClient client.Client 78 atomic bool 79 } 80 81 // SetAtomicForCAS sets atomic mode for CompareAndSwap 82 func (c *Client) SetAtomicForCAS(b bool) *Client { 83 c.atomic = b 84 return c 85 } 86 87 // NewClient creates a client with PD cluster addrs. 88 func NewClient(ctx context.Context, pdAddrs []string, security config.Security, opts ...pd.ClientOption) (*Client, error) { 89 pdCli, err := pd.NewClient(pdAddrs, pd.SecurityOption{ 90 CAPath: security.ClusterSSLCA, 91 CertPath: security.ClusterSSLCert, 92 KeyPath: security.ClusterSSLKey, 93 }, opts...) 94 if err != nil { 95 return nil, errors.Trace(err) 96 } 97 return &Client{ 98 clusterID: pdCli.GetClusterID(ctx), 99 regionCache: locate.NewRegionCache(pdCli), 100 pdClient: pdCli, 101 rpcClient: client.NewRPCClient(client.WithSecurity(security)), 102 }, nil 103 } 104 105 // Close closes the client. 106 func (c *Client) Close() error { 107 if c.pdClient != nil { 108 c.pdClient.Close() 109 } 110 if c.regionCache != nil { 111 c.regionCache.Close() 112 } 113 if c.rpcClient == nil { 114 return nil 115 } 116 return c.rpcClient.Close() 117 } 118 119 // ClusterID returns the TiKV cluster ID. 120 func (c *Client) ClusterID() uint64 { 121 return c.clusterID 122 } 123 124 // Get queries value with the key. When the key does not exist, it returns `nil, nil`. 125 func (c *Client) Get(ctx context.Context, key []byte) ([]byte, error) { 126 start := time.Now() 127 defer func() { metrics.RawkvCmdHistogramWithGet.Observe(time.Since(start).Seconds()) }() 128 129 req := tikvrpc.NewRequest(tikvrpc.CmdRawGet, &kvrpcpb.RawGetRequest{Key: key}) 130 resp, _, err := c.sendReq(ctx, key, req, false) 131 if err != nil { 132 return nil, errors.Trace(err) 133 } 134 if resp.Resp == nil { 135 return nil, errors.Trace(tikverr.ErrBodyMissing) 136 } 137 cmdResp := resp.Resp.(*kvrpcpb.RawGetResponse) 138 if cmdResp.GetError() != "" { 139 return nil, errors.New(cmdResp.GetError()) 140 } 141 if len(cmdResp.Value) == 0 { 142 return nil, nil 143 } 144 return cmdResp.Value, nil 145 } 146 147 const rawkvMaxBackoff = 20000 148 149 // BatchGet queries values with the keys. 150 func (c *Client) BatchGet(ctx context.Context, keys [][]byte) ([][]byte, error) { 151 start := time.Now() 152 defer func() { 153 metrics.RawkvCmdHistogramWithBatchGet.Observe(time.Since(start).Seconds()) 154 }() 155 156 bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil) 157 resp, err := c.sendBatchReq(bo, keys, tikvrpc.CmdRawBatchGet) 158 if err != nil { 159 return nil, errors.Trace(err) 160 } 161 162 if resp.Resp == nil { 163 return nil, errors.Trace(tikverr.ErrBodyMissing) 164 } 165 cmdResp := resp.Resp.(*kvrpcpb.RawBatchGetResponse) 166 167 keyToValue := make(map[string][]byte, len(keys)) 168 for _, pair := range cmdResp.Pairs { 169 keyToValue[string(pair.Key)] = pair.Value 170 } 171 172 values := make([][]byte, len(keys)) 173 for i, key := range keys { 174 values[i] = keyToValue[string(key)] 175 } 176 return values, nil 177 } 178 179 // PutWithTTL stores a key-value pair to TiKV with a time-to-live duration. 180 func (c *Client) PutWithTTL(ctx context.Context, key, value []byte, ttl uint64) error { 181 start := time.Now() 182 defer func() { metrics.RawkvCmdHistogramWithBatchPut.Observe(time.Since(start).Seconds()) }() 183 metrics.RawkvSizeHistogramWithKey.Observe(float64(len(key))) 184 metrics.RawkvSizeHistogramWithValue.Observe(float64(len(value))) 185 186 if len(value) == 0 { 187 return errors.New("empty value is not supported") 188 } 189 190 req := tikvrpc.NewRequest(tikvrpc.CmdRawPut, &kvrpcpb.RawPutRequest{ 191 Key: key, 192 Value: value, 193 Ttl: ttl, 194 ForCas: c.atomic, 195 }) 196 resp, _, err := c.sendReq(ctx, key, req, false) 197 if err != nil { 198 return errors.Trace(err) 199 } 200 if resp.Resp == nil { 201 return errors.Trace(tikverr.ErrBodyMissing) 202 } 203 cmdResp := resp.Resp.(*kvrpcpb.RawPutResponse) 204 if cmdResp.GetError() != "" { 205 return errors.New(cmdResp.GetError()) 206 } 207 return nil 208 } 209 210 // GetKeyTTL get the TTL of a raw key from TiKV if key exists 211 func (c *Client) GetKeyTTL(ctx context.Context, key []byte) (*uint64, error) { 212 var ttl uint64 213 metrics.RawkvSizeHistogramWithKey.Observe(float64(len(key))) 214 req := tikvrpc.NewRequest(tikvrpc.CmdGetKeyTTL, &kvrpcpb.RawGetKeyTTLRequest{ 215 Key: key, 216 }) 217 resp, _, err := c.sendReq(ctx, key, req, false) 218 219 if err != nil { 220 return nil, errors.Trace(err) 221 } 222 if resp.Resp == nil { 223 return nil, errors.Trace(tikverr.ErrBodyMissing) 224 } 225 226 cmdResp := resp.Resp.(*kvrpcpb.RawGetKeyTTLResponse) 227 if cmdResp.GetError() != "" { 228 return nil, errors.New(cmdResp.GetError()) 229 } 230 231 if cmdResp.GetNotFound() { 232 return nil, nil 233 } 234 235 ttl = cmdResp.GetTtl() 236 return &ttl, nil 237 } 238 239 // Put stores a key-value pair to TiKV. 240 func (c *Client) Put(ctx context.Context, key, value []byte) error { 241 return c.PutWithTTL(ctx, key, value, 0) 242 } 243 244 // BatchPut stores key-value pairs to TiKV. 245 func (c *Client) BatchPut(ctx context.Context, keys, values [][]byte, ttls []uint64) error { 246 start := time.Now() 247 defer func() { 248 metrics.RawkvCmdHistogramWithBatchPut.Observe(time.Since(start).Seconds()) 249 }() 250 251 if len(keys) != len(values) { 252 return errors.New("the len of keys is not equal to the len of values") 253 } 254 if len(ttls) > 0 && len(keys) != len(ttls) { 255 return errors.New("the len of ttls is not equal to the len of values") 256 } 257 for _, value := range values { 258 if len(value) == 0 { 259 return errors.New("empty value is not supported") 260 } 261 } 262 bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil) 263 err := c.sendBatchPut(bo, keys, values, ttls) 264 return errors.Trace(err) 265 } 266 267 // Delete deletes a key-value pair from TiKV. 268 func (c *Client) Delete(ctx context.Context, key []byte) error { 269 start := time.Now() 270 defer func() { metrics.RawkvCmdHistogramWithDelete.Observe(time.Since(start).Seconds()) }() 271 272 req := tikvrpc.NewRequest(tikvrpc.CmdRawDelete, &kvrpcpb.RawDeleteRequest{ 273 Key: key, 274 ForCas: c.atomic, 275 }) 276 req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds()) 277 resp, _, err := c.sendReq(ctx, key, req, false) 278 if err != nil { 279 return errors.Trace(err) 280 } 281 if resp.Resp == nil { 282 return errors.Trace(tikverr.ErrBodyMissing) 283 } 284 cmdResp := resp.Resp.(*kvrpcpb.RawDeleteResponse) 285 if cmdResp.GetError() != "" { 286 return errors.New(cmdResp.GetError()) 287 } 288 return nil 289 } 290 291 // BatchDelete deletes key-value pairs from TiKV. 292 func (c *Client) BatchDelete(ctx context.Context, keys [][]byte) error { 293 start := time.Now() 294 defer func() { 295 metrics.RawkvCmdHistogramWithBatchDelete.Observe(time.Since(start).Seconds()) 296 }() 297 298 bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil) 299 resp, err := c.sendBatchReq(bo, keys, tikvrpc.CmdRawBatchDelete) 300 if err != nil { 301 return errors.Trace(err) 302 } 303 if resp.Resp == nil { 304 return errors.Trace(tikverr.ErrBodyMissing) 305 } 306 cmdResp := resp.Resp.(*kvrpcpb.RawBatchDeleteResponse) 307 if cmdResp.GetError() != "" { 308 return errors.New(cmdResp.GetError()) 309 } 310 return nil 311 } 312 313 // DeleteRange deletes all key-value pairs in a range from TiKV. 314 func (c *Client) DeleteRange(ctx context.Context, startKey []byte, endKey []byte) error { 315 start := time.Now() 316 var err error 317 defer func() { 318 var label = "delete_range" 319 if err != nil { 320 label += "_error" 321 } 322 metrics.TiKVRawkvCmdHistogram.WithLabelValues(label).Observe(time.Since(start).Seconds()) 323 }() 324 325 // Process each affected region respectively 326 for !bytes.Equal(startKey, endKey) { 327 var resp *tikvrpc.Response 328 var actualEndKey []byte 329 resp, actualEndKey, err = c.sendDeleteRangeReq(ctx, startKey, endKey) 330 if err != nil { 331 return errors.Trace(err) 332 } 333 if resp.Resp == nil { 334 return errors.Trace(tikverr.ErrBodyMissing) 335 } 336 cmdResp := resp.Resp.(*kvrpcpb.RawDeleteRangeResponse) 337 if cmdResp.GetError() != "" { 338 return errors.New(cmdResp.GetError()) 339 } 340 startKey = actualEndKey 341 } 342 343 return nil 344 } 345 346 // DeleteRange deletes all key-value pairs in a range from TiKV. 347 func (c *Client) BatchDeleteRange(ctx context.Context, startKey []byte, endKey []byte, batchSize int) error { 348 start := time.Now() 349 tmpEndKey := endKey 350 tmpStartKey := startKey 351 var err error 352 defer func() { 353 var label = "delete_range_batch" 354 if err != nil { 355 label += "_error" 356 } 357 metrics.TiKVRawkvCmdHistogram.WithLabelValues(label).Observe(time.Since(start).Seconds()) 358 }() 359 bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil) 360 361 var i int = 0 362 var keyLocations []locate.KeyLocation = make([]locate.KeyLocation, batchSize) 363 for !bytes.Equal(tmpStartKey, tmpEndKey) { 364 for i = 0; i < batchSize; i++ { 365 loc, err := c.regionCache.LocateKey(bo, tmpStartKey) 366 if err != nil { 367 return err 368 } 369 370 paramRegion := locate.NewRegionVerID(loc.Region.GetID(), loc.Region.GetConfVer(), loc.Region.GetVer()) 371 paramStartKey := loc.StartKey 372 paramEndKey := loc.EndKey 373 logutil.BgLogger().Info("doBatchDeleteRangeReq in channel err", zap.Any("loc", paramRegion), zap.ByteString("start", paramStartKey), zap.ByteString("end", paramEndKey), 374 zap.Uint64("loc id", loc.Region.GetID()), zap.Uint64("loc confver", loc.Region.GetConfVer()), zap.Uint64("ver", loc.Region.GetVer()), zap.String("content", paramRegion.String())) 375 if len(loc.EndKey) > 0 && bytes.Compare(loc.EndKey, tmpEndKey) < 0 { 376 keyLocations = append(keyLocations, locate.KeyLocation{StartKey: paramStartKey, EndKey: paramEndKey, Region: paramRegion}) 377 tmpStartKey = loc.EndKey 378 } else { 379 keyLocations = append(keyLocations, locate.KeyLocation{StartKey: tmpStartKey, EndKey: tmpEndKey, Region: paramRegion}) 380 tmpStartKey = tmpEndKey 381 break 382 } 383 } 384 385 bo, cancel := bo.Fork() 386 ch := make(chan error, len(keyLocations)) 387 for _, batch := range keyLocations { 388 batch1 := batch 389 go func() { 390 batch2 := batch1 391 singleBatchBackoffer, singleBatchCancel := bo.Fork() 392 defer singleBatchCancel() 393 ch <- c.doBatchDeleteRangeReq(singleBatchBackoffer, batch2) 394 }() 395 } 396 397 for i := 0; i < len(keyLocations); i++ { 398 if e := <-ch; e != nil { 399 cancel() 400 // catch the first error 401 if err == nil { 402 err = errors.WithStack(e) 403 } 404 } 405 } 406 if err != nil { 407 logutil.BgLogger().Error("doBatchDeleteRangeReq in channel err", zap.Error(err)) 408 } 409 } 410 411 return nil 412 } 413 414 func (c *Client) doBatchDeleteRangeReq(bo *retry.Backoffer, request locate.KeyLocation) error { 415 logutil.BgLogger().Info("doBatchDeleteRangeReq", zap.Any("request", request), zap.String("content", request.String())) 416 if request.StartKey == nil || request.EndKey == nil { 417 return nil 418 } 419 var req *tikvrpc.Request = tikvrpc.NewRequest(tikvrpc.CmdRawDeleteRange, &kvrpcpb.RawDeleteRangeRequest{ 420 StartKey: request.StartKey, 421 EndKey: request.EndKey, 422 }) 423 424 sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient) 425 426 req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds()) 427 // resp, err := sender.SendReq(bo, req, batch.RegionID, client.ReadTimeoutShort) 428 resp, err := sender.SendReq(bo, req, request.Region, client.ReadTimeoutShort) 429 if err != nil { 430 logutil.BgLogger().Error("snendReq err", zap.Any("req", request), zap.Error(err)) 431 return err 432 } 433 regionErr, err := resp.GetRegionError() 434 if err != nil { 435 logutil.BgLogger().Error("getRegion err", zap.Any("req", request), zap.Error(err)) 436 return err 437 } 438 if regionErr != nil { 439 err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String())) 440 if err != nil { 441 logutil.BgLogger().Error("backoff err", zap.Any("req", request), zap.Error(err)) 442 return err 443 } 444 return c.doBatchDeleteRangeReq(bo, request) 445 } 446 return nil 447 } 448 449 // Scan queries continuous kv pairs in range [startKey, endKey), up to limit pairs. 450 // If endKey is empty, it means unbounded. 451 // If you want to exclude the startKey or include the endKey, push a '\0' to the key. For example, to scan 452 // (startKey, endKey], you can write: 453 // `Scan(ctx, push(startKey, '\0'), push(endKey, '\0'), limit)`. 454 func (c *Client) Scan(ctx context.Context, startKey, endKey []byte, limit int) (keys [][]byte, values [][]byte, err error) { 455 start := time.Now() 456 defer func() { metrics.RawkvCmdHistogramWithRawScan.Observe(time.Since(start).Seconds()) }() 457 458 if limit > MaxRawKVScanLimit { 459 return nil, nil, errors.Trace(ErrMaxScanLimitExceeded) 460 } 461 462 for len(keys) < limit && (len(endKey) == 0 || bytes.Compare(startKey, endKey) < 0) { 463 req := tikvrpc.NewRequest(tikvrpc.CmdRawScan, &kvrpcpb.RawScanRequest{ 464 StartKey: startKey, 465 EndKey: endKey, 466 Limit: uint32(limit - len(keys)), 467 }) 468 resp, loc, err := c.sendReq(ctx, startKey, req, false) 469 if err != nil { 470 return nil, nil, errors.Trace(err) 471 } 472 if resp.Resp == nil { 473 return nil, nil, errors.Trace(tikverr.ErrBodyMissing) 474 } 475 cmdResp := resp.Resp.(*kvrpcpb.RawScanResponse) 476 for _, pair := range cmdResp.Kvs { 477 keys = append(keys, pair.Key) 478 values = append(values, pair.Value) 479 } 480 startKey = loc.EndKey 481 if len(startKey) == 0 { 482 break 483 } 484 } 485 return 486 } 487 488 // ReverseScan queries continuous kv pairs in range [endKey, startKey), up to limit pairs. 489 // Direction is different from Scan, upper to lower. 490 // If endKey is empty, it means unbounded. 491 // If you want to include the startKey or exclude the endKey, push a '\0' to the key. For example, to scan 492 // (endKey, startKey], you can write: 493 // `ReverseScan(ctx, push(startKey, '\0'), push(endKey, '\0'), limit)`. 494 // It doesn't support Scanning from "", because locating the last Region is not yet implemented. 495 func (c *Client) ReverseScan(ctx context.Context, startKey, endKey []byte, limit int) (keys [][]byte, values [][]byte, err error) { 496 start := time.Now() 497 defer func() { 498 metrics.RawkvCmdHistogramWithRawReversScan.Observe(time.Since(start).Seconds()) 499 }() 500 501 if limit > MaxRawKVScanLimit { 502 return nil, nil, errors.Trace(ErrMaxScanLimitExceeded) 503 } 504 505 for len(keys) < limit && bytes.Compare(startKey, endKey) > 0 { 506 req := tikvrpc.NewRequest(tikvrpc.CmdRawScan, &kvrpcpb.RawScanRequest{ 507 StartKey: startKey, 508 EndKey: endKey, 509 Limit: uint32(limit - len(keys)), 510 Reverse: true, 511 }) 512 resp, loc, err := c.sendReq(ctx, startKey, req, true) 513 if err != nil { 514 return nil, nil, errors.Trace(err) 515 } 516 if resp.Resp == nil { 517 return nil, nil, errors.Trace(tikverr.ErrBodyMissing) 518 } 519 cmdResp := resp.Resp.(*kvrpcpb.RawScanResponse) 520 for _, pair := range cmdResp.Kvs { 521 keys = append(keys, pair.Key) 522 values = append(values, pair.Value) 523 } 524 startKey = loc.StartKey 525 if len(startKey) == 0 { 526 break 527 } 528 } 529 return 530 } 531 532 // CompareAndSwap results in an atomic compare-and-set operation for the given key while SetAtomicForCAS(true) 533 // If the value retrieved is equal to previousValue, newValue is written. 534 // It returns the previous value and whether the value is successfully swapped. 535 // 536 // If SetAtomicForCAS(false), it will returns an error because 537 // CAS operations enforce the client should operate in atomic mode. 538 // 539 // NOTE: This feature is experimental. It depends on the single-row transaction mechanism of TiKV which is conflict 540 // with the normal write operation in rawkv mode. If multiple clients exist, it's up to the clients the sync the atomic mode flag. 541 // If some clients write in atomic mode but the other don't, the linearizability of TiKV will be violated. 542 func (c *Client) CompareAndSwap(ctx context.Context, key, previousValue, newValue []byte, ttl uint64) ([]byte, bool, error) { 543 if !c.atomic { 544 return nil, false, errors.Trace(errors.New("using CompareAndSwap without enable atomic mode")) 545 } 546 547 if len(newValue) == 0 { 548 return nil, false, errors.New("empty value is not supported") 549 } 550 551 reqArgs := kvrpcpb.RawCASRequest{ 552 Key: key, 553 Value: newValue, 554 } 555 if previousValue == nil { 556 reqArgs.PreviousNotExist = true 557 } else { 558 reqArgs.PreviousValue = previousValue 559 } 560 561 if ttl > 0 { 562 reqArgs.Ttl = ttl 563 } 564 565 req := tikvrpc.NewRequest(tikvrpc.CmdRawCompareAndSwap, &reqArgs) 566 req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds()) 567 resp, _, err := c.sendReq(ctx, key, req, false) 568 if err != nil { 569 return nil, false, errors.Trace(err) 570 } 571 if resp.Resp == nil { 572 return nil, false, errors.Trace(tikverr.ErrBodyMissing) 573 } 574 575 cmdResp := resp.Resp.(*kvrpcpb.RawCASResponse) 576 if cmdResp.GetError() != "" { 577 return nil, false, errors.New(cmdResp.GetError()) 578 } 579 580 if cmdResp.PreviousNotExist { 581 return nil, cmdResp.Succeed, nil 582 } 583 return cmdResp.PreviousValue, cmdResp.Succeed, nil 584 } 585 586 func (c *Client) sendReq(ctx context.Context, key []byte, req *tikvrpc.Request, reverse bool) (*tikvrpc.Response, *locate.KeyLocation, error) { 587 bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil) 588 sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient) 589 for { 590 var loc *locate.KeyLocation 591 var err error 592 if reverse { 593 loc, err = c.regionCache.LocateEndKey(bo, key) 594 } else { 595 loc, err = c.regionCache.LocateKey(bo, key) 596 } 597 if err != nil { 598 return nil, nil, errors.Trace(err) 599 } 600 resp, err := sender.SendReq(bo, req, loc.Region, client.ReadTimeoutShort) 601 if err != nil { 602 return nil, nil, errors.Trace(err) 603 } 604 regionErr, err := resp.GetRegionError() 605 if err != nil { 606 return nil, nil, errors.Trace(err) 607 } 608 if regionErr != nil { 609 err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String())) 610 if err != nil { 611 return nil, nil, errors.Trace(err) 612 } 613 continue 614 } 615 return resp, loc, nil 616 } 617 } 618 619 func (c *Client) sendBatchReq(bo *retry.Backoffer, keys [][]byte, cmdType tikvrpc.CmdType) (*tikvrpc.Response, error) { // split the keys 620 groups, _, err := c.regionCache.GroupKeysByRegion(bo, keys, nil) 621 if err != nil { 622 return nil, errors.Trace(err) 623 } 624 625 var batches []kvrpc.Batch 626 for regionID, groupKeys := range groups { 627 batches = kvrpc.AppendKeyBatches(batches, regionID, groupKeys, rawBatchPairCount) 628 } 629 bo, cancel := bo.Fork() 630 ches := make(chan kvrpc.BatchResult, len(batches)) 631 for _, batch := range batches { 632 batch1 := batch 633 go func() { 634 singleBatchBackoffer, singleBatchCancel := bo.Fork() 635 defer singleBatchCancel() 636 ches <- c.doBatchReq(singleBatchBackoffer, batch1, cmdType) 637 }() 638 } 639 640 var firstError error 641 var resp *tikvrpc.Response 642 switch cmdType { 643 case tikvrpc.CmdRawBatchGet: 644 resp = &tikvrpc.Response{Resp: &kvrpcpb.RawBatchGetResponse{}} 645 case tikvrpc.CmdRawBatchDelete: 646 resp = &tikvrpc.Response{Resp: &kvrpcpb.RawBatchDeleteResponse{}} 647 } 648 for i := 0; i < len(batches); i++ { 649 singleResp, ok := <-ches 650 if ok { 651 if singleResp.Error != nil { 652 cancel() 653 if firstError == nil { 654 firstError = singleResp.Error 655 } 656 } else if cmdType == tikvrpc.CmdRawBatchGet { 657 cmdResp := singleResp.Resp.(*kvrpcpb.RawBatchGetResponse) 658 resp.Resp.(*kvrpcpb.RawBatchGetResponse).Pairs = append(resp.Resp.(*kvrpcpb.RawBatchGetResponse).Pairs, cmdResp.Pairs...) 659 } 660 } 661 } 662 663 return resp, firstError 664 } 665 666 func (c *Client) doBatchReq(bo *retry.Backoffer, batch kvrpc.Batch, cmdType tikvrpc.CmdType) kvrpc.BatchResult { 667 var req *tikvrpc.Request 668 switch cmdType { 669 case tikvrpc.CmdRawBatchGet: 670 req = tikvrpc.NewRequest(cmdType, &kvrpcpb.RawBatchGetRequest{ 671 Keys: batch.Keys, 672 }) 673 case tikvrpc.CmdRawBatchDelete: 674 req = tikvrpc.NewRequest(cmdType, &kvrpcpb.RawBatchDeleteRequest{ 675 Keys: batch.Keys, 676 ForCas: c.atomic, 677 }) 678 } 679 680 sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient) 681 req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds()) 682 resp, err := sender.SendReq(bo, req, batch.RegionID, client.ReadTimeoutShort) 683 684 batchResp := kvrpc.BatchResult{} 685 if err != nil { 686 batchResp.Error = errors.Trace(err) 687 return batchResp 688 } 689 regionErr, err := resp.GetRegionError() 690 if err != nil { 691 batchResp.Error = errors.Trace(err) 692 return batchResp 693 } 694 if regionErr != nil { 695 err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String())) 696 if err != nil { 697 batchResp.Error = errors.Trace(err) 698 return batchResp 699 } 700 resp, err = c.sendBatchReq(bo, batch.Keys, cmdType) 701 batchResp.Response = resp 702 batchResp.Error = err 703 return batchResp 704 } 705 706 switch cmdType { 707 case tikvrpc.CmdRawBatchGet: 708 batchResp.Response = resp 709 case tikvrpc.CmdRawBatchDelete: 710 if resp.Resp == nil { 711 batchResp.Error = errors.Trace(tikverr.ErrBodyMissing) 712 return batchResp 713 } 714 cmdResp := resp.Resp.(*kvrpcpb.RawBatchDeleteResponse) 715 if cmdResp.GetError() != "" { 716 batchResp.Error = errors.New(cmdResp.GetError()) 717 return batchResp 718 } 719 batchResp.Response = resp 720 } 721 return batchResp 722 } 723 724 // sendDeleteRangeReq sends a raw delete range request and returns the response and the actual endKey. 725 // If the given range spans over more than one regions, the actual endKey is the end of the first region. 726 // We can't use sendReq directly, because we need to know the end of the region before we send the request 727 // TODO: Is there any better way to avoid duplicating code with func `sendReq` ? 728 func (c *Client) sendDeleteRangeReq(ctx context.Context, startKey []byte, endKey []byte) (*tikvrpc.Response, []byte, error) { 729 bo := retry.NewBackofferWithVars(ctx, rawkvMaxBackoff, nil) 730 sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient) 731 for { 732 loc, err := c.regionCache.LocateKey(bo, startKey) 733 if err != nil { 734 return nil, nil, errors.Trace(err) 735 } 736 737 actualEndKey := endKey 738 if len(loc.EndKey) > 0 && bytes.Compare(loc.EndKey, endKey) < 0 { 739 actualEndKey = loc.EndKey 740 } 741 742 req := tikvrpc.NewRequest(tikvrpc.CmdRawDeleteRange, &kvrpcpb.RawDeleteRangeRequest{ 743 StartKey: startKey, 744 EndKey: actualEndKey, 745 }) 746 747 req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds()) 748 resp, err := sender.SendReq(bo, req, loc.Region, client.ReadTimeoutShort) 749 if err != nil { 750 return nil, nil, errors.Trace(err) 751 } 752 regionErr, err := resp.GetRegionError() 753 if err != nil { 754 return nil, nil, errors.Trace(err) 755 } 756 if regionErr != nil { 757 err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String())) 758 if err != nil { 759 return nil, nil, errors.Trace(err) 760 } 761 continue 762 } 763 return resp, actualEndKey, nil 764 } 765 } 766 767 func (c *Client) sendBatchPut(bo *retry.Backoffer, keys, values [][]byte, ttls []uint64) error { 768 keyToValue := make(map[string][]byte, len(keys)) 769 keyTottl := make(map[string]uint64, len(keys)) 770 for i, key := range keys { 771 keyToValue[string(key)] = values[i] 772 if len(ttls) > 0 { 773 keyTottl[string(key)] = ttls[i] 774 } 775 } 776 groups, _, err := c.regionCache.GroupKeysByRegion(bo, keys, nil) 777 if err != nil { 778 return errors.Trace(err) 779 } 780 var batches []kvrpc.Batch 781 // split the keys by size and RegionVerID 782 for regionID, groupKeys := range groups { 783 batches = kvrpc.AppendBatches(batches, regionID, groupKeys, keyToValue, keyTottl, rawBatchPutSize) 784 } 785 bo, cancel := bo.Fork() 786 ch := make(chan error, len(batches)) 787 for _, batch := range batches { 788 batch1 := batch 789 go func() { 790 singleBatchBackoffer, singleBatchCancel := bo.Fork() 791 defer singleBatchCancel() 792 ch <- c.doBatchPut(singleBatchBackoffer, batch1) 793 }() 794 } 795 796 for i := 0; i < len(batches); i++ { 797 if e := <-ch; e != nil { 798 cancel() 799 // catch the first error 800 if err == nil { 801 err = e 802 } 803 } 804 } 805 return errors.Trace(err) 806 } 807 808 func (c *Client) doBatchPut(bo *retry.Backoffer, batch kvrpc.Batch) error { 809 kvPair := make([]*kvrpcpb.KvPair, 0, len(batch.Keys)) 810 for i, key := range batch.Keys { 811 kvPair = append(kvPair, &kvrpcpb.KvPair{Key: key, Value: batch.Values[i]}) 812 } 813 814 req := tikvrpc.NewRequest(tikvrpc.CmdRawBatchPut, 815 &kvrpcpb.RawBatchPutRequest{Pairs: kvPair, ForCas: c.atomic, Ttls: batch.TTLs}) 816 817 sender := locate.NewRegionRequestSender(c.regionCache, c.rpcClient) 818 req.MaxExecutionDurationMs = uint64(client.MaxWriteExecutionTime.Milliseconds()) 819 resp, err := sender.SendReq(bo, req, batch.RegionID, client.ReadTimeoutShort) 820 if err != nil { 821 return errors.Trace(err) 822 } 823 regionErr, err := resp.GetRegionError() 824 if err != nil { 825 return errors.Trace(err) 826 } 827 if regionErr != nil { 828 err := bo.Backoff(retry.BoRegionMiss, errors.New(regionErr.String())) 829 if err != nil { 830 return errors.Trace(err) 831 } 832 // recursive call 833 return c.sendBatchPut(bo, batch.Keys, batch.Values, batch.TTLs) 834 } 835 836 if resp.Resp == nil { 837 return errors.Trace(tikverr.ErrBodyMissing) 838 } 839 cmdResp := resp.Resp.(*kvrpcpb.RawBatchPutResponse) 840 if cmdResp.GetError() != "" { 841 return errors.New(cmdResp.GetError()) 842 } 843 return nil 844 }