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  }