diff --git a/pkg/kv/dist_sender.go b/pkg/kv/dist_sender.go index eb731d9c506f..d0a7ebfdcf00 100644 --- a/pkg/kv/dist_sender.go +++ b/pkg/kv/dist_sender.go @@ -743,6 +743,11 @@ func (ds *DistSender) divideAndSendBatchToRanges( return resp.reply, resp.pErr } + if ba.IsUnsplittable() { + mismatch := roachpb.NewRangeKeyMismatchError(rs.Key.AsRawKey(), rs.EndKey.AsRawKey(), ri.Desc()) + return nil, roachpb.NewError(mismatch) + } + // Make an empty slice of responses which will be populated with responses // as they come in via Combine(). br = &roachpb.BatchResponse{ diff --git a/pkg/roachpb/api.go b/pkg/roachpb/api.go index 7c52d4b6293b..a211885484ee 100644 --- a/pkg/roachpb/api.go +++ b/pkg/roachpb/api.go @@ -103,14 +103,15 @@ func (rc ReadConsistencyType) SupportsBatch(ba BatchRequest) error { } const ( - isAdmin = 1 << iota // admin cmds don't go through raft, but run on lease holder - isRead // read-only cmds don't go through raft, but may run on lease holder - isWrite // write cmds go through raft and must be proposed on lease holder - isTxn // txn commands may be part of a transaction - isTxnWrite // txn write cmds start heartbeat and are marked for intent resolution - isRange // range commands may span multiple keys - isReverse // reverse commands traverse ranges in descending direction - isAlone // requests which must be alone in a batch + isAdmin = 1 << iota // admin cmds don't go through raft, but run on lease holder + isRead // read-only cmds don't go through raft, but may run on lease holder + isWrite // write cmds go through raft and must be proposed on lease holder + isTxn // txn commands may be part of a transaction + isTxnWrite // txn write cmds start heartbeat and are marked for intent resolution + isRange // range commands may span multiple keys + isReverse // reverse commands traverse ranges in descending direction + isAlone // requests which must be alone in a batch + isUnsplittable // range command that must not be split during sending // Requests for acquiring a lease skip the (proposal-time) check that the // proposing replica has a valid lease. skipLeaseCheck @@ -1068,7 +1069,7 @@ func (*WriteBatchRequest) flags() int { return isWrite | isRange } func (*ExportRequest) flags() int { return isRead | isRange | updatesReadTSCache } func (*ImportRequest) flags() int { return isAdmin | isAlone } func (*AdminScatterRequest) flags() int { return isAdmin | isAlone | isRange } -func (*AddSSTableRequest) flags() int { return isWrite | isAlone | isRange } +func (*AddSSTableRequest) flags() int { return isWrite | isAlone | isRange | isUnsplittable } // RefreshRequest and RefreshRangeRequest both list // updates(Read)TSCache, though they actually update the read or write diff --git a/pkg/roachpb/batch.go b/pkg/roachpb/batch.go index a5021dd660ae..d479ebfa18af 100644 --- a/pkg/roachpb/batch.go +++ b/pkg/roachpb/batch.go @@ -117,6 +117,11 @@ func (ba *BatchRequest) IsTransactionWrite() bool { return ba.hasFlag(isTxnWrite) } +// IsUnsplittable returns true iff the BatchRequest an un-splittable request. +func (ba *BatchRequest) IsUnsplittable() bool { + return ba.hasFlag(isUnsplittable) +} + // IsSingleRequest returns true iff the BatchRequest contains a single request. func (ba *BatchRequest) IsSingleRequest() bool { return len(ba.Requests) == 1