github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/replica_consistency_diff.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 kvserver
    12  
    13  import (
    14  	"bytes"
    15  	"fmt"
    16  	"io"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    19  	"github.com/cockroachdb/cockroach/pkg/storage"
    20  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    21  )
    22  
    23  // ReplicaSnapshotDiff is a part of a []ReplicaSnapshotDiff which represents a diff between
    24  // two replica snapshots. For now it's only a diff between their KV pairs.
    25  type ReplicaSnapshotDiff struct {
    26  	// LeaseHolder is set to true of this kv pair is only present on the lease
    27  	// holder.
    28  	LeaseHolder bool
    29  	Key         roachpb.Key
    30  	Timestamp   hlc.Timestamp
    31  	Value       []byte
    32  }
    33  
    34  // ReplicaSnapshotDiffSlice groups multiple ReplicaSnapshotDiff records and
    35  // exposes a formatting helper.
    36  type ReplicaSnapshotDiffSlice []ReplicaSnapshotDiff
    37  
    38  // WriteTo writes a string representation of itself to the given writer.
    39  func (rsds ReplicaSnapshotDiffSlice) WriteTo(w io.Writer) (int64, error) {
    40  	n, err := w.Write([]byte("--- leaseholder\n+++ follower\n"))
    41  	if err != nil {
    42  		return 0, err
    43  	}
    44  	for _, d := range rsds {
    45  		prefix := "+"
    46  		if d.LeaseHolder {
    47  			// Lease holder (RHS) has something follower (LHS) does not have.
    48  			prefix = "-"
    49  		}
    50  		ts := d.Timestamp
    51  		const format = `%s%d.%09d,%d %s
    52  %s    ts:%s
    53  %s    value:%s
    54  %s    raw mvcc_key/value: %x %x
    55  `
    56  		var prettyTime string
    57  		if d.Timestamp == (hlc.Timestamp{}) {
    58  			prettyTime = "<zero>"
    59  		} else {
    60  			prettyTime = d.Timestamp.GoTime().UTC().String()
    61  		}
    62  		mvccKey := storage.MVCCKey{Key: d.Key, Timestamp: ts}
    63  		num, err := fmt.Fprintf(w, format,
    64  			prefix, ts.WallTime/1e9, ts.WallTime%1e9, ts.Logical, d.Key,
    65  			prefix, prettyTime,
    66  			prefix, SprintKeyValue(storage.MVCCKeyValue{Key: mvccKey, Value: d.Value}, false /* printKey */),
    67  			prefix, storage.EncodeKey(mvccKey), d.Value)
    68  		if err != nil {
    69  			return 0, err
    70  		}
    71  		n += num
    72  	}
    73  	return int64(n), nil
    74  }
    75  
    76  func (rsds ReplicaSnapshotDiffSlice) String() string {
    77  	var buf bytes.Buffer
    78  	_, _ = rsds.WriteTo(&buf)
    79  	return buf.String()
    80  }
    81  
    82  // diffs the two kv dumps between the lease holder and the replica.
    83  func diffRange(l, r *roachpb.RaftSnapshotData) ReplicaSnapshotDiffSlice {
    84  	if l == nil || r == nil {
    85  		return nil
    86  	}
    87  	var diff []ReplicaSnapshotDiff
    88  	i, j := 0, 0
    89  	for {
    90  		var e, v roachpb.RaftSnapshotData_KeyValue
    91  		if i < len(l.KV) {
    92  			e = l.KV[i]
    93  		}
    94  		if j < len(r.KV) {
    95  			v = r.KV[j]
    96  		}
    97  
    98  		addLeaseHolder := func() {
    99  			diff = append(diff, ReplicaSnapshotDiff{LeaseHolder: true, Key: e.Key, Timestamp: e.Timestamp, Value: e.Value})
   100  			i++
   101  		}
   102  		addReplica := func() {
   103  			diff = append(diff, ReplicaSnapshotDiff{LeaseHolder: false, Key: v.Key, Timestamp: v.Timestamp, Value: v.Value})
   104  			j++
   105  		}
   106  
   107  		// Compare keys.
   108  		var comp int
   109  		// Check if it has finished traversing over all the lease holder keys.
   110  		if e.Key == nil {
   111  			if v.Key == nil {
   112  				// Done traversing over all the replica keys. Done!
   113  				break
   114  			} else {
   115  				comp = 1
   116  			}
   117  		} else {
   118  			// Check if it has finished traversing over all the replica keys.
   119  			if v.Key == nil {
   120  				comp = -1
   121  			} else {
   122  				// Both lease holder and replica keys exist. Compare them.
   123  				comp = bytes.Compare(e.Key, v.Key)
   124  			}
   125  		}
   126  		switch comp {
   127  		case -1:
   128  			addLeaseHolder()
   129  
   130  		case 0:
   131  			// Timestamp sorting is weird. Timestamp{} sorts first, the
   132  			// remainder sort in descending order. See storage/engine/doc.go.
   133  			if e.Timestamp != v.Timestamp {
   134  				if e.Timestamp == (hlc.Timestamp{}) {
   135  					addLeaseHolder()
   136  				} else if v.Timestamp == (hlc.Timestamp{}) {
   137  					addReplica()
   138  				} else if v.Timestamp.Less(e.Timestamp) {
   139  					addLeaseHolder()
   140  				} else {
   141  					addReplica()
   142  				}
   143  			} else if !bytes.Equal(e.Value, v.Value) {
   144  				addLeaseHolder()
   145  				addReplica()
   146  			} else {
   147  				// No diff; skip.
   148  				i++
   149  				j++
   150  			}
   151  
   152  		case 1:
   153  			addReplica()
   154  
   155  		}
   156  	}
   157  	return diff
   158  }