-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
28362: engine: create new OpLoggerBatch to log logical MVCC ops r=nvanbenschoten a=nvanbenschoten This change creates a new Batch implementation that logs logical MVCC operations. This allows a Replica to selectively decide when a request's logical MVCC operations should be attached to a raft command. This is important for rangefeed because it operates off of logical operations, not physical ones. With this change, a Replica will never decide to turn on logical op logging, so it doesn't change any behavior yet. A future change will include logic to conditionally log and replica logical MVCC ops. This turns out to be fairly involved, so I figured it would be preferable to split this into its own PR. A description of the tentative plan for that is included in `replica.go`. Suggestions are very welcome! Release note: None Co-authored-by: Nathan VanBenschoten <[email protected]>
- Loading branch information
Showing
14 changed files
with
785 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
// Copyright 2018 The Cockroach Authors. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
// implied. See the License for the specific language governing | ||
// permissions and limitations under the License. | ||
|
||
package engine | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/roachpb" | ||
"github.com/cockroachdb/cockroach/pkg/storage/engine/enginepb" | ||
"github.com/cockroachdb/cockroach/pkg/util/bufalloc" | ||
) | ||
|
||
// MVCCLogicalOpType is an enum with values corresponding to each of the | ||
// enginepb.MVCCLogicalOp variants. | ||
// | ||
// LogLogicalOp takes an MVCCLogicalOpType and a corresponding | ||
// MVCCLogicalOpDetails instead of an enginepb.MVCCLogicalOp variant for two | ||
// reasons. First, it serves as a form of abstraction so that callers of the | ||
// method don't need to construct protos themselves. More importantly, it also | ||
// avoids allocations in the common case where Writer.LogLogicalOp is a no-op. | ||
// This makes LogLogicalOp essentially free for cases where logical op logging | ||
// is disabled. | ||
type MVCCLogicalOpType int | ||
|
||
const ( | ||
// MVCCWriteValueOpType corresponds to the MVCCWriteValueOp variant. | ||
MVCCWriteValueOpType MVCCLogicalOpType = iota | ||
// MVCCWriteIntentOpType corresponds to the MVCCWriteIntentOp variant. | ||
MVCCWriteIntentOpType | ||
// MVCCUpdateIntentOpType corresponds to the MVCCUpdateIntentOp variant. | ||
MVCCUpdateIntentOpType | ||
// MVCCCommitIntentOpType corresponds to the MVCCCommitIntentOp variant. | ||
MVCCCommitIntentOpType | ||
// MVCCAbortIntentOpType corresponds to the MVCCAbortIntentOp variant. | ||
MVCCAbortIntentOpType | ||
) | ||
|
||
// MVCCLogicalOpDetails contains details about the occurrence of an MVCC logical | ||
// operation. | ||
type MVCCLogicalOpDetails struct { | ||
Txn enginepb.TxnMeta | ||
Key roachpb.Key | ||
Value roachpb.Value | ||
|
||
// Safe indicates that the values in this struct will never be invalidated | ||
// at a later point. If the details object cannot promise that its values | ||
// will never be invalidated, an OpLoggerBatch will make a copy of all | ||
// references before adding it to the log. TestMVCCOpLogWriter fails without | ||
// this. | ||
Safe bool | ||
} | ||
|
||
// OpLoggerBatch records a log of logical MVCC operations. | ||
type OpLoggerBatch struct { | ||
Batch | ||
distinct distinctOpLoggerBatch | ||
distinctOpen bool | ||
|
||
ops []enginepb.MVCCLogicalOp | ||
opsAlloc bufalloc.ByteAllocator | ||
} | ||
|
||
// NewOpLoggerBatch creates a new batch that logs logical mvcc operations and | ||
// wraps the provided batch. | ||
func NewOpLoggerBatch(b Batch) *OpLoggerBatch { | ||
ol := &OpLoggerBatch{Batch: b} | ||
ol.distinct.parent = ol | ||
return ol | ||
} | ||
|
||
var _ Batch = &OpLoggerBatch{} | ||
|
||
// LogLogicalOp implements the Writer interface. | ||
func (ol *OpLoggerBatch) LogLogicalOp(op MVCCLogicalOpType, details MVCCLogicalOpDetails) { | ||
if ol.distinctOpen { | ||
panic("distinct batch already open") | ||
} | ||
ol.logLogicalOp(op, details) | ||
ol.Batch.LogLogicalOp(op, details) | ||
} | ||
|
||
func (ol *OpLoggerBatch) logLogicalOp(op MVCCLogicalOpType, details MVCCLogicalOpDetails) { | ||
switch op { | ||
case MVCCWriteValueOpType: | ||
if !details.Safe { | ||
ol.opsAlloc, details.Key = ol.opsAlloc.Copy(details.Key, 0) | ||
ol.opsAlloc, details.Value.RawBytes = ol.opsAlloc.Copy(details.Value.RawBytes, 0) | ||
} | ||
|
||
ol.recordOp(&enginepb.MVCCWriteValueOp{ | ||
Key: details.Key, | ||
Timestamp: details.Value.Timestamp, | ||
Value: details.Value.RawBytes, | ||
}) | ||
case MVCCWriteIntentOpType: | ||
if !details.Safe { | ||
ol.opsAlloc, details.Txn.Key = ol.opsAlloc.Copy(details.Txn.Key, 0) | ||
} | ||
|
||
ol.recordOp(&enginepb.MVCCWriteIntentOp{ | ||
TxnID: details.Txn.ID, | ||
TxnKey: details.Txn.Key, | ||
Timestamp: details.Value.Timestamp, | ||
}) | ||
case MVCCUpdateIntentOpType: | ||
ol.recordOp(&enginepb.MVCCUpdateIntentOp{ | ||
TxnID: details.Txn.ID, | ||
Timestamp: details.Value.Timestamp, | ||
}) | ||
case MVCCCommitIntentOpType: | ||
if !details.Safe { | ||
ol.opsAlloc, details.Key = ol.opsAlloc.Copy(details.Key, 0) | ||
} | ||
|
||
ol.recordOp(&enginepb.MVCCCommitIntentOp{ | ||
TxnID: details.Txn.ID, | ||
Key: details.Key, | ||
Timestamp: details.Value.Timestamp, | ||
}) | ||
case MVCCAbortIntentOpType: | ||
ol.recordOp(&enginepb.MVCCAbortIntentOp{ | ||
TxnID: details.Txn.ID, | ||
}) | ||
default: | ||
panic(fmt.Sprintf("unexpected op type %v", op)) | ||
} | ||
} | ||
|
||
func (ol *OpLoggerBatch) recordOp(op interface{}) { | ||
ol.ops = append(ol.ops, enginepb.MVCCLogicalOp{}) | ||
ol.ops[len(ol.ops)-1].MustSetValue(op) | ||
} | ||
|
||
// LogicalOps returns the list of all logical MVCC operations that have been | ||
// recorded by the logger. | ||
func (ol *OpLoggerBatch) LogicalOps() []enginepb.MVCCLogicalOp { | ||
if ol == nil { | ||
return nil | ||
} | ||
return ol.ops | ||
} | ||
|
||
// Distinct implements the Batch interface. | ||
func (ol *OpLoggerBatch) Distinct() ReadWriter { | ||
if ol.distinctOpen { | ||
panic("distinct batch already open") | ||
} | ||
ol.distinctOpen = true | ||
ol.distinct.ReadWriter = ol.Batch.Distinct() | ||
return &ol.distinct | ||
} | ||
|
||
type distinctOpLoggerBatch struct { | ||
ReadWriter | ||
parent *OpLoggerBatch | ||
} | ||
|
||
// LogLogicalOp implements the Writer interface. | ||
func (dlw *distinctOpLoggerBatch) LogLogicalOp(op MVCCLogicalOpType, details MVCCLogicalOpDetails) { | ||
dlw.parent.logLogicalOp(op, details) | ||
dlw.ReadWriter.LogLogicalOp(op, details) | ||
} | ||
|
||
// Close implements the Reader interface. | ||
func (dlw *distinctOpLoggerBatch) Close() { | ||
if !dlw.parent.distinctOpen { | ||
panic("distinct batch not open") | ||
} | ||
dlw.parent.distinctOpen = false | ||
dlw.ReadWriter.Close() | ||
} |
Oops, something went wrong.