github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/tscache/skl_impl.go (about) 1 // Copyright 2017 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 tscache 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/roachpb" 17 "github.com/cockroachdb/cockroach/pkg/util/hlc" 18 "github.com/cockroachdb/cockroach/pkg/util/log" 19 "github.com/cockroachdb/cockroach/pkg/util/uuid" 20 ) 21 22 // sklImpl implements the Cache interface. It maintains a collection of 23 // skiplists containing keys or key ranges and the timestamps at which 24 // they were most recently read or written. If a timestamp was read or 25 // written by a transaction, the txn ID is stored with the timestamp to 26 // avoid advancing timestamps on successive requests from the same 27 // transaction. 28 type sklImpl struct { 29 cache *intervalSkl 30 clock *hlc.Clock 31 metrics Metrics 32 } 33 34 var _ Cache = &sklImpl{} 35 36 // newSklImpl returns a new treeImpl with the supplied hybrid clock. 37 func newSklImpl(clock *hlc.Clock) *sklImpl { 38 tc := sklImpl{clock: clock, metrics: makeMetrics()} 39 tc.clear(clock.Now()) 40 return &tc 41 } 42 43 // clear clears the cache and resets the low-water mark. 44 func (tc *sklImpl) clear(lowWater hlc.Timestamp) { 45 tc.cache = newIntervalSkl(tc.clock, MinRetentionWindow, tc.metrics.Skl) 46 tc.cache.floorTS = lowWater 47 } 48 49 // Add implements the Cache interface. 50 func (tc *sklImpl) Add(start, end roachpb.Key, ts hlc.Timestamp, txnID uuid.UUID) { 51 start, end = tc.boundKeyLengths(start, end) 52 53 val := cacheValue{ts: ts, txnID: txnID} 54 if len(end) == 0 { 55 tc.cache.Add(nonNil(start), val) 56 } else { 57 tc.cache.AddRange(nonNil(start), end, excludeTo, val) 58 } 59 } 60 61 // SetLowWater implements the Cache interface. 62 func (tc *sklImpl) SetLowWater(start, end roachpb.Key, ts hlc.Timestamp) { 63 tc.Add(start, end, ts, noTxnID) 64 } 65 66 // getLowWater implements the Cache interface. 67 func (tc *sklImpl) getLowWater() hlc.Timestamp { 68 return tc.cache.FloorTS() 69 } 70 71 // GetMax implements the Cache interface. 72 func (tc *sklImpl) GetMax(start, end roachpb.Key) (hlc.Timestamp, uuid.UUID) { 73 var val cacheValue 74 if len(end) == 0 { 75 val = tc.cache.LookupTimestamp(nonNil(start)) 76 } else { 77 val = tc.cache.LookupTimestampRange(nonNil(start), end, excludeTo) 78 } 79 return val.ts, val.txnID 80 } 81 82 // boundKeyLengths makes sure that the key lengths provided are well below the 83 // size of each sklPage, otherwise we'll never be successful in adding it to 84 // an intervalSkl. 85 func (tc *sklImpl) boundKeyLengths(start, end roachpb.Key) (roachpb.Key, roachpb.Key) { 86 // We bound keys to 1/32 of the page size. These could be slightly larger 87 // and still not trigger the "key range too large" panic in intervalSkl, 88 // but anything larger could require multiple page rotations before it's 89 // able to fit in if other ranges are being added concurrently. 90 maxKeySize := int(maximumSklPageSize / 32) 91 92 // If either key is too long, truncate its length, making sure to always 93 // grow the [start,end) range instead of shrinking it. This will reduce the 94 // precision of the entry in the cache, which could allow independent 95 // requests to interfere, but will never permit consistency anomalies. 96 if l := len(start); l > maxKeySize { 97 start = start[:maxKeySize] 98 log.Warningf(context.TODO(), "start key with length %d exceeds maximum key length of %d; "+ 99 "losing precision in timestamp cache", l, maxKeySize) 100 } 101 if l := len(end); l > maxKeySize { 102 end = end[:maxKeySize].PrefixEnd() // PrefixEnd to grow range 103 log.Warningf(context.TODO(), "end key with length %d exceeds maximum key length of %d; "+ 104 "losing precision in timestamp cache", l, maxKeySize) 105 } 106 return start, end 107 } 108 109 // Metrics implements the Cache interface. 110 func (tc *sklImpl) Metrics() Metrics { 111 return tc.metrics 112 } 113 114 // intervalSkl doesn't handle nil keys the same way as empty keys. Cockroach's 115 // KV API layer doesn't make a distinction. 116 var emptyStartKey = []byte("") 117 118 func nonNil(b []byte) []byte { 119 if b == nil { 120 return emptyStartKey 121 } 122 return b 123 }