github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/cmd/pebble/mvcc.go (about) 1 // Copyright 2018 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package main 6 7 import ( 8 "bytes" 9 10 "github.com/petermattis/pebble" 11 "github.com/petermattis/pebble/internal/bytealloc" 12 ) 13 14 // MVCC encoding and decoding routines adapted from CockroachDB sources. Used 15 // to perform apples-to-apples benchmarking for CockroachDB's usage of RocksDB. 16 17 var mvccComparer = &pebble.Comparer{ 18 Compare: mvccCompare, 19 20 AbbreviatedKey: func(k []byte) uint64 { 21 key, _, ok := mvccSplitKey(k) 22 if !ok { 23 return 0 24 } 25 return pebble.DefaultComparer.AbbreviatedKey(key) 26 }, 27 28 Separator: func(dst, a, b []byte) []byte { 29 return append(dst, a...) 30 }, 31 32 Successor: func(dst, a []byte) []byte { 33 return append(dst, a...) 34 }, 35 36 Name: "cockroach_comparator", 37 } 38 39 func mvccSplitKey(mvccKey []byte) (key []byte, ts []byte, ok bool) { 40 if len(mvccKey) == 0 { 41 return nil, nil, false 42 } 43 n := len(mvccKey) - 1 44 tsLen := int(mvccKey[n]) 45 if n < tsLen { 46 return nil, nil, false 47 } 48 key = mvccKey[:n-tsLen] 49 if tsLen > 0 { 50 ts = mvccKey[n-tsLen+1 : len(mvccKey)-1] 51 } 52 return key, ts, true 53 } 54 55 func mvccCompare(a, b []byte) int { 56 aKey, aTS, aOK := mvccSplitKey(a) 57 bKey, bTS, bOK := mvccSplitKey(b) 58 if !aOK || !bOK { 59 // This should never happen unless there is some sort of corruption of 60 // the keys. 61 return bytes.Compare(a, b) 62 } 63 if c := bytes.Compare(aKey, bKey); c != 0 { 64 return c 65 } 66 if len(aTS) == 0 { 67 if len(bTS) == 0 { 68 return 0 69 } 70 return -1 71 } else if len(bTS) == 0 { 72 return +1 73 } 74 return bytes.Compare(bTS, aTS) 75 } 76 77 // <key>\x00[<wall_time>[<logical>]]<#timestamp-bytes> 78 func mvccEncode(dst, key []byte, walltime uint64, logical uint32) []byte { 79 dst = append(dst, key...) 80 dst = append(dst, 0) 81 if walltime != 0 || logical != 0 { 82 extra := byte(1 + 8) 83 dst = encodeUint64Ascending(dst, walltime) 84 if logical != 0 { 85 dst = encodeUint32Ascending(dst, logical) 86 extra += 4 87 } 88 dst = append(dst, extra) 89 } 90 return dst 91 } 92 93 func mvccForwardScan(d DB, start, end, ts []byte) (int, int64) { 94 it := d.NewIter(&pebble.IterOptions{ 95 LowerBound: mvccEncode(nil, start, 0, 0), 96 UpperBound: mvccEncode(nil, end, 0, 0), 97 }) 98 defer it.Close() 99 100 var data bytealloc.A 101 var count int 102 var nbytes int64 103 104 for valid := it.First(); valid; valid = it.Next() { 105 key, keyTS, _ := mvccSplitKey(it.Key()) 106 if bytes.Compare(keyTS, ts) <= 0 { 107 data, _ = data.Copy(key) 108 data, _ = data.Copy(it.Value()) 109 } 110 count++ 111 nbytes += int64(len(it.Key()) + len(it.Value())) 112 } 113 return count, nbytes 114 } 115 116 func mvccReverseScan(d DB, start, end, ts []byte) (int, int64) { 117 it := d.NewIter(&pebble.IterOptions{ 118 LowerBound: mvccEncode(nil, start, 0, 0), 119 UpperBound: mvccEncode(nil, end, 0, 0), 120 }) 121 defer it.Close() 122 123 var data bytealloc.A 124 var count int 125 var nbytes int64 126 127 for valid := it.Last(); valid; valid = it.Prev() { 128 key, keyTS, _ := mvccSplitKey(it.Key()) 129 if bytes.Compare(keyTS, ts) <= 0 { 130 data, _ = data.Copy(key) 131 data, _ = data.Copy(it.Value()) 132 } 133 count++ 134 nbytes += int64(len(it.Key()) + len(it.Value())) 135 } 136 return count, nbytes 137 }