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 }