github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/storage/mvcc_logical_ops.go (about) 1 // Copyright 2018 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 storage 12 13 import ( 14 "fmt" 15 16 "github.com/cockroachdb/cockroach/pkg/keys" 17 "github.com/cockroachdb/cockroach/pkg/roachpb" 18 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 19 "github.com/cockroachdb/cockroach/pkg/util/bufalloc" 20 "github.com/cockroachdb/cockroach/pkg/util/hlc" 21 ) 22 23 // MVCCLogicalOpType is an enum with values corresponding to each of the 24 // enginepb.MVCCLogicalOp variants. 25 // 26 // LogLogicalOp takes an MVCCLogicalOpType and a corresponding 27 // MVCCLogicalOpDetails instead of an enginepb.MVCCLogicalOp variant for two 28 // reasons. First, it serves as a form of abstraction so that callers of the 29 // method don't need to construct protos themselves. More importantly, it also 30 // avoids allocations in the common case where Writer.LogLogicalOp is a no-op. 31 // This makes LogLogicalOp essentially free for cases where logical op logging 32 // is disabled. 33 type MVCCLogicalOpType int 34 35 const ( 36 // MVCCWriteValueOpType corresponds to the MVCCWriteValueOp variant. 37 MVCCWriteValueOpType MVCCLogicalOpType = iota 38 // MVCCWriteIntentOpType corresponds to the MVCCWriteIntentOp variant. 39 MVCCWriteIntentOpType 40 // MVCCUpdateIntentOpType corresponds to the MVCCUpdateIntentOp variant. 41 MVCCUpdateIntentOpType 42 // MVCCCommitIntentOpType corresponds to the MVCCCommitIntentOp variant. 43 MVCCCommitIntentOpType 44 // MVCCAbortIntentOpType corresponds to the MVCCAbortIntentOp variant. 45 MVCCAbortIntentOpType 46 ) 47 48 // MVCCLogicalOpDetails contains details about the occurrence of an MVCC logical 49 // operation. 50 type MVCCLogicalOpDetails struct { 51 Txn enginepb.TxnMeta 52 Key roachpb.Key 53 Timestamp hlc.Timestamp 54 55 // Safe indicates that the values in this struct will never be invalidated 56 // at a later point. If the details object cannot promise that its values 57 // will never be invalidated, an OpLoggerBatch will make a copy of all 58 // references before adding it to the log. TestMVCCOpLogWriter fails without 59 // this. 60 Safe bool 61 } 62 63 // OpLoggerBatch records a log of logical MVCC operations. 64 type OpLoggerBatch struct { 65 Batch 66 distinct distinctOpLoggerBatch 67 distinctOpen bool 68 69 ops []enginepb.MVCCLogicalOp 70 opsAlloc bufalloc.ByteAllocator 71 } 72 73 // NewOpLoggerBatch creates a new batch that logs logical mvcc operations and 74 // wraps the provided batch. 75 func NewOpLoggerBatch(b Batch) *OpLoggerBatch { 76 ol := &OpLoggerBatch{Batch: b} 77 ol.distinct.parent = ol 78 return ol 79 } 80 81 var _ Batch = &OpLoggerBatch{} 82 83 // LogLogicalOp implements the Writer interface. 84 func (ol *OpLoggerBatch) LogLogicalOp(op MVCCLogicalOpType, details MVCCLogicalOpDetails) { 85 if ol.distinctOpen { 86 panic("distinct batch already open") 87 } 88 ol.logLogicalOp(op, details) 89 ol.Batch.LogLogicalOp(op, details) 90 } 91 92 func (ol *OpLoggerBatch) logLogicalOp(op MVCCLogicalOpType, details MVCCLogicalOpDetails) { 93 if keys.IsLocal(details.Key) { 94 // Ignore mvcc operations on local keys. 95 return 96 } 97 98 switch op { 99 case MVCCWriteValueOpType: 100 if !details.Safe { 101 ol.opsAlloc, details.Key = ol.opsAlloc.Copy(details.Key, 0) 102 } 103 104 ol.recordOp(&enginepb.MVCCWriteValueOp{ 105 Key: details.Key, 106 Timestamp: details.Timestamp, 107 }) 108 case MVCCWriteIntentOpType: 109 if !details.Safe { 110 ol.opsAlloc, details.Txn.Key = ol.opsAlloc.Copy(details.Txn.Key, 0) 111 } 112 113 ol.recordOp(&enginepb.MVCCWriteIntentOp{ 114 TxnID: details.Txn.ID, 115 TxnKey: details.Txn.Key, 116 TxnMinTimestamp: details.Txn.MinTimestamp, 117 Timestamp: details.Timestamp, 118 }) 119 case MVCCUpdateIntentOpType: 120 ol.recordOp(&enginepb.MVCCUpdateIntentOp{ 121 TxnID: details.Txn.ID, 122 Timestamp: details.Timestamp, 123 }) 124 case MVCCCommitIntentOpType: 125 if !details.Safe { 126 ol.opsAlloc, details.Key = ol.opsAlloc.Copy(details.Key, 0) 127 } 128 129 ol.recordOp(&enginepb.MVCCCommitIntentOp{ 130 TxnID: details.Txn.ID, 131 Key: details.Key, 132 Timestamp: details.Timestamp, 133 }) 134 case MVCCAbortIntentOpType: 135 ol.recordOp(&enginepb.MVCCAbortIntentOp{ 136 TxnID: details.Txn.ID, 137 }) 138 default: 139 panic(fmt.Sprintf("unexpected op type %v", op)) 140 } 141 } 142 143 func (ol *OpLoggerBatch) recordOp(op interface{}) { 144 ol.ops = append(ol.ops, enginepb.MVCCLogicalOp{}) 145 ol.ops[len(ol.ops)-1].MustSetValue(op) 146 } 147 148 // LogicalOps returns the list of all logical MVCC operations that have been 149 // recorded by the logger. 150 func (ol *OpLoggerBatch) LogicalOps() []enginepb.MVCCLogicalOp { 151 if ol == nil { 152 return nil 153 } 154 return ol.ops 155 } 156 157 // Distinct implements the Batch interface. 158 func (ol *OpLoggerBatch) Distinct() ReadWriter { 159 if ol.distinctOpen { 160 panic("distinct batch already open") 161 } 162 ol.distinctOpen = true 163 ol.distinct.ReadWriter = ol.Batch.Distinct() 164 return &ol.distinct 165 } 166 167 type distinctOpLoggerBatch struct { 168 ReadWriter 169 parent *OpLoggerBatch 170 } 171 172 // LogLogicalOp implements the Writer interface. 173 func (dlw *distinctOpLoggerBatch) LogLogicalOp(op MVCCLogicalOpType, details MVCCLogicalOpDetails) { 174 dlw.parent.logLogicalOp(op, details) 175 dlw.ReadWriter.LogLogicalOp(op, details) 176 } 177 178 // Close implements the Reader interface. 179 func (dlw *distinctOpLoggerBatch) Close() { 180 if !dlw.parent.distinctOpen { 181 panic("distinct batch not open") 182 } 183 dlw.parent.distinctOpen = false 184 dlw.ReadWriter.Close() 185 }