github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/roachpb/batch.go (about) 1 // Copyright 2014 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package roachpb 12 13 import ( 14 "bytes" 15 "context" 16 "fmt" 17 "strings" 18 19 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/lock" 20 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 21 "github.com/cockroachdb/cockroach/pkg/util/hlc" 22 "github.com/cockroachdb/errors" 23 ) 24 25 //go:generate go run -tags gen-batch gen_batch.go 26 27 // SetActiveTimestamp sets the correct timestamp at which the request is to be 28 // carried out. For transactional requests, ba.Timestamp must be zero initially 29 // and it will be set to txn.ReadTimestamp (note though this mostly impacts 30 // reads; writes use txn.Timestamp). For non-transactional requests, if no 31 // timestamp is specified, nowFn is used to create and set one. 32 func (ba *BatchRequest) SetActiveTimestamp(nowFn func() hlc.Timestamp) error { 33 if txn := ba.Txn; txn != nil { 34 if ba.Timestamp != (hlc.Timestamp{}) { 35 return errors.New("transactional request must not set batch timestamp") 36 } 37 38 // The batch timestamp is the timestamp at which reads are performed. We set 39 // this to the txn's read timestamp, even if the txn's provisional 40 // commit timestamp has been forwarded, so that all reads within a txn 41 // observe the same snapshot of the database regardless of how the 42 // provisional commit timestamp evolves. 43 // 44 // Note that writes will be performed at the provisional commit timestamp, 45 // txn.Timestamp, regardless of the batch timestamp. 46 ba.Timestamp = txn.ReadTimestamp 47 } else { 48 // When not transactional, allow empty timestamp and use nowFn instead 49 if ba.Timestamp == (hlc.Timestamp{}) { 50 ba.Timestamp = nowFn() 51 } 52 } 53 return nil 54 } 55 56 // UpdateTxn updates the batch transaction from the supplied one in 57 // a copy-on-write fashion, i.e. without mutating an existing 58 // Transaction struct. 59 func (ba *BatchRequest) UpdateTxn(o *Transaction) { 60 if o == nil { 61 return 62 } 63 o.AssertInitialized(context.TODO()) 64 if ba.Txn == nil { 65 ba.Txn = o 66 return 67 } 68 clonedTxn := ba.Txn.Clone() 69 clonedTxn.Update(o) 70 ba.Txn = clonedTxn 71 } 72 73 // IsLeaseRequest returns whether the batch consists of a single RequestLease 74 // request. Note that TransferLease requests return false. 75 // RequestLease requests are special because they're the only type of requests a 76 // non-lease-holder can propose. 77 func (ba *BatchRequest) IsLeaseRequest() bool { 78 if !ba.IsSingleRequest() { 79 return false 80 } 81 _, ok := ba.GetArg(RequestLease) 82 return ok 83 } 84 85 // IsAdmin returns true iff the BatchRequest contains an admin request. 86 func (ba *BatchRequest) IsAdmin() bool { 87 return ba.hasFlag(isAdmin) 88 } 89 90 // IsWrite returns true iff the BatchRequest contains a write. 91 func (ba *BatchRequest) IsWrite() bool { 92 return ba.hasFlag(isWrite) 93 } 94 95 // IsReadOnly returns true if all requests within are read-only. 96 func (ba *BatchRequest) IsReadOnly() bool { 97 return len(ba.Requests) > 0 && !ba.hasFlag(isWrite|isAdmin) 98 } 99 100 // RequiresLeaseHolder returns true if the request can only be served by the 101 // leaseholders of the ranges it addresses. 102 func (ba *BatchRequest) RequiresLeaseHolder() bool { 103 return ba.IsLocking() || ba.Header.ReadConsistency.RequiresReadLease() 104 } 105 106 // IsReverse returns true iff the BatchRequest contains a reverse request. 107 func (ba *BatchRequest) IsReverse() bool { 108 return ba.hasFlag(isReverse) 109 } 110 111 // IsTransactional returns true iff the BatchRequest contains requests that can 112 // be part of a transaction. 113 func (ba *BatchRequest) IsTransactional() bool { 114 return ba.hasFlag(isTxn) 115 } 116 117 // IsAllTransactional returns true iff the BatchRequest contains only requests 118 // that can be part of a transaction. 119 func (ba *BatchRequest) IsAllTransactional() bool { 120 return ba.hasFlagForAll(isTxn) 121 } 122 123 // IsLocking returns true iff the BatchRequest intends to acquire locks. 124 func (ba *BatchRequest) IsLocking() bool { 125 return ba.hasFlag(isLocking) 126 } 127 128 // IsIntentWrite returns true iff the BatchRequest contains an intent write. 129 func (ba *BatchRequest) IsIntentWrite() bool { 130 return ba.hasFlag(isIntentWrite) 131 } 132 133 // IsUnsplittable returns true iff the BatchRequest an un-splittable request. 134 func (ba *BatchRequest) IsUnsplittable() bool { 135 return ba.hasFlag(isUnsplittable) 136 } 137 138 // IsSingleRequest returns true iff the BatchRequest contains a single request. 139 func (ba *BatchRequest) IsSingleRequest() bool { 140 return len(ba.Requests) == 1 141 } 142 143 // IsSingleSkipLeaseCheckRequest returns true iff the batch contains a single 144 // request, and that request has the skipLeaseCheck flag set. 145 func (ba *BatchRequest) IsSingleSkipLeaseCheckRequest() bool { 146 return ba.IsSingleRequest() && ba.hasFlag(skipLeaseCheck) 147 } 148 149 // IsSinglePushTxnRequest returns true iff the batch contains a single 150 // request, and that request is for a PushTxn. 151 func (ba *BatchRequest) IsSinglePushTxnRequest() bool { 152 if ba.IsSingleRequest() { 153 _, ok := ba.Requests[0].GetInner().(*PushTxnRequest) 154 return ok 155 } 156 return false 157 } 158 159 // IsSingleHeartbeatTxnRequest returns true iff the batch contains a single 160 // request, and that request is a HeartbeatTxn. 161 func (ba *BatchRequest) IsSingleHeartbeatTxnRequest() bool { 162 if ba.IsSingleRequest() { 163 _, ok := ba.Requests[0].GetInner().(*HeartbeatTxnRequest) 164 return ok 165 } 166 return false 167 } 168 169 // IsSingleEndTxnRequest returns true iff the batch contains a single request, 170 // and that request is an EndTxnRequest. 171 func (ba *BatchRequest) IsSingleEndTxnRequest() bool { 172 if ba.IsSingleRequest() { 173 _, ok := ba.Requests[0].GetInner().(*EndTxnRequest) 174 return ok 175 } 176 return false 177 } 178 179 // IsSingleAbortTxnRequest returns true iff the batch contains a single request, 180 // and that request is an EndTxnRequest(commit=false). 181 func (ba *BatchRequest) IsSingleAbortTxnRequest() bool { 182 if ba.IsSingleRequest() { 183 if et, ok := ba.Requests[0].GetInner().(*EndTxnRequest); ok { 184 return !et.Commit 185 } 186 } 187 return false 188 } 189 190 // IsSingleSubsumeRequest returns true iff the batch contains a single request, 191 // and that request is an SubsumeRequest. 192 func (ba *BatchRequest) IsSingleSubsumeRequest() bool { 193 if ba.IsSingleRequest() { 194 _, ok := ba.Requests[0].GetInner().(*SubsumeRequest) 195 return ok 196 } 197 return false 198 } 199 200 // IsSingleComputeChecksumRequest returns true iff the batch contains a single 201 // request, and that request is a ComputeChecksumRequest. 202 func (ba *BatchRequest) IsSingleComputeChecksumRequest() bool { 203 if ba.IsSingleRequest() { 204 _, ok := ba.Requests[0].GetInner().(*ComputeChecksumRequest) 205 return ok 206 } 207 return false 208 } 209 210 // IsSingleCheckConsistencyRequest returns true iff the batch contains a single 211 // request, and that request is a CheckConsistencyRequest. 212 func (ba *BatchRequest) IsSingleCheckConsistencyRequest() bool { 213 if ba.IsSingleRequest() { 214 _, ok := ba.Requests[0].GetInner().(*CheckConsistencyRequest) 215 return ok 216 } 217 return false 218 } 219 220 // IsSingleAddSSTableRequest returns true iff the batch contains a single 221 // request, and that request is an AddSSTableRequest that will ingest as an SST, 222 // (i.e. does not have IngestAsWrites set) 223 func (ba *BatchRequest) IsSingleAddSSTableRequest() bool { 224 if ba.IsSingleRequest() { 225 req, ok := ba.Requests[0].GetInner().(*AddSSTableRequest) 226 return ok && !req.IngestAsWrites 227 } 228 return false 229 } 230 231 // IsCompleteTransaction determines whether a batch contains every write in a 232 // transactions. 233 func (ba *BatchRequest) IsCompleteTransaction() bool { 234 et, hasET := ba.GetArg(EndTxn) 235 if !hasET || !et.(*EndTxnRequest).Commit { 236 return false 237 } 238 maxSeq := et.Header().Sequence 239 switch maxSeq { 240 case 0: 241 // If the batch isn't using sequence numbers, 242 // assume that it is not a complete transaction. 243 return false 244 case 1: 245 // The transaction performed no writes. 246 return true 247 } 248 if int(maxSeq) > len(ba.Requests) { 249 // Fast-path. 250 return false 251 } 252 // Check whether any sequence numbers were skipped between 1 and the 253 // EndTxn's sequence number. A Batch is only a complete transaction 254 // if it contains every write that the transaction performed. 255 nextSeq := enginepb.TxnSeq(1) 256 for _, args := range ba.Requests { 257 req := args.GetInner() 258 seq := req.Header().Sequence 259 if seq > nextSeq { 260 return false 261 } 262 if seq == nextSeq { 263 if !IsIntentWrite(req) { 264 return false 265 } 266 nextSeq++ 267 if nextSeq == maxSeq { 268 return true 269 } 270 } 271 } 272 panic("unreachable") 273 } 274 275 // GetPrevLeaseForLeaseRequest returns the previous lease, at the time 276 // of proposal, for a request lease or transfer lease request. If the 277 // batch does not contain a single lease request, this method will panic. 278 func (ba *BatchRequest) GetPrevLeaseForLeaseRequest() Lease { 279 return ba.Requests[0].GetInner().(leaseRequestor).prevLease() 280 } 281 282 // hasFlag returns true iff one of the requests within the batch contains the 283 // specified flag. 284 func (ba *BatchRequest) hasFlag(flag int) bool { 285 for _, union := range ba.Requests { 286 if (union.GetInner().flags() & flag) != 0 { 287 return true 288 } 289 } 290 return false 291 } 292 293 // hasFlagForAll returns true iff all of the requests within the batch contains 294 // the specified flag. 295 func (ba *BatchRequest) hasFlagForAll(flag int) bool { 296 if len(ba.Requests) == 0 { 297 return false 298 } 299 for _, union := range ba.Requests { 300 if (union.GetInner().flags() & flag) == 0 { 301 return false 302 } 303 } 304 return true 305 } 306 307 // GetArg returns a request of the given type if one is contained in the 308 // Batch. The request returned is the first of its kind, with the exception 309 // of EndTxn, where it examines the very last request only. 310 func (ba *BatchRequest) GetArg(method Method) (Request, bool) { 311 // when looking for EndTxn, just look at the last entry. 312 if method == EndTxn { 313 if length := len(ba.Requests); length > 0 { 314 if req := ba.Requests[length-1].GetInner(); req.Method() == EndTxn { 315 return req, true 316 } 317 } 318 return nil, false 319 } 320 321 for _, arg := range ba.Requests { 322 if req := arg.GetInner(); req.Method() == method { 323 return req, true 324 } 325 } 326 return nil, false 327 } 328 329 func (br *BatchResponse) String() string { 330 var str []string 331 str = append(str, fmt.Sprintf("(err: %v)", br.Error)) 332 for count, union := range br.Responses { 333 // Limit the strings to provide just a summary. Without this limit a log 334 // message with a BatchResponse can be very long. 335 if count >= 20 && count < len(br.Responses)-5 { 336 if count == 20 { 337 str = append(str, fmt.Sprintf("... %d skipped ...", len(br.Responses)-25)) 338 } 339 continue 340 } 341 str = append(str, fmt.Sprintf("%T", union.GetInner())) 342 } 343 return strings.Join(str, ", ") 344 } 345 346 // LockSpanIterate calls the passed method with the key ranges of the 347 // transactional locks contained in the batch. Usually the key spans 348 // contained in the requests are used, but when a response contains a 349 // ResumeSpan the ResumeSpan is subtracted from the request span to 350 // provide a more minimal span of keys affected by the request. 351 func (ba *BatchRequest) LockSpanIterate(br *BatchResponse, fn func(Span, lock.Durability)) { 352 for i, arg := range ba.Requests { 353 req := arg.GetInner() 354 if !IsLocking(req) { 355 continue 356 } 357 var resp Response 358 if br != nil { 359 resp = br.Responses[i].GetInner() 360 } 361 if span, ok := ActualSpan(req, resp); ok { 362 fn(span, LockingDurability(req)) 363 } 364 } 365 } 366 367 // RefreshSpanIterate calls the passed function with the key spans of 368 // requests in the batch which need to be refreshed. These requests 369 // must be checked via Refresh/RefreshRange to avoid having to restart 370 // a SERIALIZABLE transaction. Usually the key spans contained in the 371 // requests are used, but when a response contains a ResumeSpan the 372 // ResumeSpan is subtracted from the request span to provide a more 373 // minimal span of keys affected by the request. The supplied function 374 // is called with each span. 375 func (ba *BatchRequest) RefreshSpanIterate(br *BatchResponse, fn func(Span)) { 376 for i, arg := range ba.Requests { 377 req := arg.GetInner() 378 if !NeedsRefresh(req) { 379 continue 380 } 381 var resp Response 382 if br != nil { 383 resp = br.Responses[i].GetInner() 384 } 385 if span, ok := ActualSpan(req, resp); ok { 386 fn(span) 387 } 388 } 389 } 390 391 // ActualSpan returns the actual request span which was operated on, 392 // according to the existence of a resume span in the response. If 393 // nothing was operated on, returns false. 394 func ActualSpan(req Request, resp Response) (Span, bool) { 395 h := req.Header() 396 if resp != nil { 397 resumeSpan := resp.Header().ResumeSpan 398 // If a resume span exists we need to cull the span. 399 if resumeSpan != nil { 400 // Handle the reverse case first. 401 if bytes.Equal(resumeSpan.Key, h.Key) { 402 if bytes.Equal(resumeSpan.EndKey, h.EndKey) { 403 return Span{}, false 404 } 405 return Span{Key: resumeSpan.EndKey, EndKey: h.EndKey}, true 406 } 407 // The forward case. 408 return Span{Key: h.Key, EndKey: resumeSpan.Key}, true 409 } 410 } 411 return h.Span(), true 412 } 413 414 // Combine combines each slot of the given request into the corresponding slot 415 // of the base response. The number of slots must be equal and the respective 416 // slots must be combinable. 417 // On error, the receiver BatchResponse is in an invalid state. In either case, 418 // the supplied BatchResponse must not be used any more. 419 // It is an error to call Combine on responses with errors in them. The 420 // DistSender strips the errors from any responses that it combines. 421 func (br *BatchResponse) Combine(otherBatch *BatchResponse, positions []int) error { 422 if err := br.BatchResponse_Header.combine(otherBatch.BatchResponse_Header); err != nil { 423 return err 424 } 425 for i := range otherBatch.Responses { 426 pos := positions[i] 427 if br.Responses[pos] == (ResponseUnion{}) { 428 br.Responses[pos] = otherBatch.Responses[i] 429 continue 430 } 431 valLeft := br.Responses[pos].GetInner() 432 valRight := otherBatch.Responses[i].GetInner() 433 if err := CombineResponses(valLeft, valRight); err != nil { 434 return err 435 } 436 } 437 return nil 438 } 439 440 // Add adds a request to the batch request. It's a convenience method; 441 // requests may also be added directly into the slice. 442 func (ba *BatchRequest) Add(requests ...Request) { 443 for _, args := range requests { 444 ba.Requests = append(ba.Requests, RequestUnion{}) 445 ba.Requests[len(ba.Requests)-1].MustSetInner(args) 446 } 447 } 448 449 // Add adds a response to the batch response. It's a convenience method; 450 // responses may also be added directly. 451 func (br *BatchResponse) Add(reply Response) { 452 br.Responses = append(br.Responses, ResponseUnion{}) 453 br.Responses[len(br.Responses)-1].MustSetInner(reply) 454 } 455 456 // Methods returns a slice of the contained methods. 457 func (ba *BatchRequest) Methods() []Method { 458 var res []Method 459 for _, arg := range ba.Requests { 460 res = append(res, arg.GetInner().Method()) 461 } 462 return res 463 } 464 465 // Split separates the requests contained in a batch so that each subset of 466 // requests can be executed by a Store (without changing order). In particular, 467 // Admin requests are always singled out and mutating requests separated from 468 // reads. The boolean parameter indicates whether EndTxn should be 469 // special-cased: If false, an EndTxn request will never be split into a new 470 // chunk (otherwise, it is treated according to its flags). This allows sending 471 // a whole transaction in a single Batch when addressing a single range. 472 // 473 // NOTE: One reason for splitting reads from writes is that write-only batches 474 // can sometimes have their read timestamp bumped on the server, which doesn't 475 // work for read requests due to how the timestamp-aware latching works (i.e. a 476 // read that acquired a latch @ ts10 can't simply be bumped to ts 20 because 477 // there might have been overlapping writes in the 10..20 window). 478 func (ba BatchRequest) Split(canSplitET bool) [][]RequestUnion { 479 compatible := func(exFlags, newFlags int) bool { 480 // isAlone requests are never compatible. 481 if (exFlags&isAlone) != 0 || (newFlags&isAlone) != 0 { 482 return false 483 } 484 // If the current or new flags are empty and neither include isAlone, 485 // everything goes. 486 if exFlags == 0 || newFlags == 0 { 487 return true 488 } 489 // Otherwise, the flags below must remain the same with the new 490 // request added. 491 // 492 // Note that we're not checking isRead: The invariants we're 493 // enforcing are that a batch can't mix non-writes with writes. 494 // Checking isRead would cause ConditionalPut and Put to conflict, 495 // which is not what we want. 496 const mask = isWrite | isAdmin | isReverse 497 return (mask & exFlags) == (mask & newFlags) 498 } 499 var parts [][]RequestUnion 500 for len(ba.Requests) > 0 { 501 part := ba.Requests 502 var gFlags, hFlags = -1, -1 503 for i, union := range ba.Requests { 504 args := union.GetInner() 505 flags := args.flags() 506 method := args.Method() 507 if (flags & isPrefix) != 0 { 508 // Requests with the isPrefix flag want to be grouped with the 509 // next non-header request in a batch. Scan forward and find 510 // first non-header request. Naively, this would result in 511 // quadratic behavior for repeat isPrefix requests. We avoid 512 // this by caching first non-header request's flags in hFlags. 513 if hFlags == -1 { 514 for _, nUnion := range ba.Requests[i+1:] { 515 nArgs := nUnion.GetInner() 516 nFlags := nArgs.flags() 517 nMethod := nArgs.Method() 518 if !canSplitET && nMethod == EndTxn { 519 nFlags = 0 // always compatible 520 } 521 if (nFlags & isPrefix) == 0 { 522 hFlags = nFlags 523 break 524 } 525 } 526 } 527 if hFlags != -1 && (hFlags&isAlone) == 0 { 528 flags = hFlags 529 } 530 } else { 531 hFlags = -1 // reset 532 } 533 cmpFlags := flags 534 if !canSplitET && method == EndTxn { 535 cmpFlags = 0 // always compatible 536 } 537 if gFlags == -1 { 538 // If no flags are set so far, everything goes. 539 gFlags = flags 540 } else { 541 if !compatible(gFlags, cmpFlags) { 542 part = ba.Requests[:i] 543 break 544 } 545 gFlags |= flags 546 } 547 } 548 parts = append(parts, part) 549 ba.Requests = ba.Requests[len(part):] 550 } 551 return parts 552 } 553 554 // String gives a brief summary of the contained requests and keys in the batch. 555 // TODO(tschottdorf): the key range is useful information, but requires `keys`. 556 // See #2198. 557 func (ba BatchRequest) String() string { 558 var str []string 559 if ba.Txn != nil { 560 str = append(str, fmt.Sprintf("[txn: %s]", ba.Txn.Short())) 561 } 562 for count, arg := range ba.Requests { 563 // Limit the strings to provide just a summary. Without this limit 564 // a log message with a BatchRequest can be very long. 565 if count >= 20 && count < len(ba.Requests)-5 { 566 if count == 20 { 567 str = append(str, fmt.Sprintf("... %d skipped ...", len(ba.Requests)-25)) 568 } 569 continue 570 } 571 req := arg.GetInner() 572 if et, ok := req.(*EndTxnRequest); ok { 573 h := req.Header() 574 str = append(str, fmt.Sprintf("%s(commit:%t tsflex:%t) [%s] ", 575 req.Method(), et.Commit, et.CanCommitAtHigherTimestamp, h.Key)) 576 } else { 577 h := req.Header() 578 var s string 579 if req.Method() == PushTxn { 580 pushReq := req.(*PushTxnRequest) 581 s = fmt.Sprintf("PushTxn(%s->%s)", pushReq.PusherTxn.Short(), pushReq.PusheeTxn.Short()) 582 } else { 583 s = req.Method().String() 584 } 585 str = append(str, fmt.Sprintf("%s [%s,%s)", s, h.Key, h.EndKey)) 586 } 587 } 588 return strings.Join(str, ", ") 589 } 590 591 // ValidateForEvaluation performs sanity checks on the batch when it's received 592 // by the "server" for evaluation. 593 func (ba BatchRequest) ValidateForEvaluation() error { 594 if ba.RangeID == 0 { 595 return errors.AssertionFailedf("batch request missing range ID") 596 } else if ba.Replica.StoreID == 0 { 597 return errors.AssertionFailedf("batch request missing store ID") 598 } 599 if _, ok := ba.GetArg(EndTxn); ok && ba.Txn == nil { 600 return errors.AssertionFailedf("EndTxn request without transaction") 601 } 602 if ba.Txn != nil { 603 if ba.Txn.WriteTooOld && (ba.Txn.ReadTimestamp.Equal(ba.Txn.WriteTimestamp)) { 604 return errors.AssertionFailedf("WriteTooOld set but no offset in timestamps. txn: %s", ba.Txn) 605 } 606 } 607 return nil 608 }