github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/tscache/cache.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 provides a timestamp cache structure that records the maximum 12 // timestamp that key ranges were read from and written to. 13 package tscache 14 15 import ( 16 "fmt" 17 "time" 18 19 "github.com/cockroachdb/cockroach/pkg/roachpb" 20 "github.com/cockroachdb/cockroach/pkg/util/envutil" 21 "github.com/cockroachdb/cockroach/pkg/util/hlc" 22 "github.com/cockroachdb/cockroach/pkg/util/uuid" 23 ) 24 25 // MinRetentionWindow specifies the minimum duration to hold entries in the 26 // cache before allowing eviction. After this window expires, transactions 27 // writing to this node with timestamps lagging by more than MinRetentionWindow 28 // will necessarily have to advance their commit timestamp. 29 const MinRetentionWindow = 10 * time.Second 30 31 // Cache is a bounded in-memory cache that records the maximum timestamp that 32 // key ranges were read from and written to. The structure serves to protect 33 // against violations of Snapshot Isolation, which requires that the outcome of 34 // reads must be preserved even in the presence of read-write conflicts (i.e. a 35 // write to a key at a lower timestamp than a previous read must not succeed). 36 // Cache corresponds to the "status oracle" discussed in Yabandeh's A Critique 37 // of Snapshot Isolation. 38 // 39 // The cache is updated after the completion of each read operation with the 40 // range of all keys that the request was predicated upon. It is then consulted 41 // for each write operation, allowing them to detect read-write violations that 42 // would allow them to write "under" a read that has already been performed. 43 // 44 // The cache is size-limited, so to prevent read-write conflicts for arbitrarily 45 // old requests, it pessimistically maintains a “low water mark”. This value 46 // always ratchets with monotonic increases and is equivalent to the earliest 47 // timestamp of any key range that is present in the cache. If a write operation 48 // writes to a key not present in the cache, the “low water mark” is consulted 49 // instead to determine read-write conflicts. The low water mark is initialized 50 // to the current system time plus the maximum clock offset. 51 // 52 // All Cache implementations are safe for concurrent use by multiple goroutines. 53 type Cache interface { 54 // Add adds the specified timestamp to the cache covering the range of keys 55 // from start to end. If end is nil, the range covers the start key only. 56 // txnID is nil for no transaction. 57 Add(start, end roachpb.Key, ts hlc.Timestamp, txnID uuid.UUID) 58 // SetLowWater sets the low water mark of the cache for the specified span 59 // to the provided timestamp. 60 SetLowWater(start, end roachpb.Key, ts hlc.Timestamp) 61 62 // GetMax returns the maximum timestamp which overlaps the interval spanning 63 // from start to end. If that maximum timestamp belongs to a single 64 // transaction, that transaction's ID is returned. Otherwise, if that maximum 65 // is shared between multiple transactions, no transaction ID is returned. 66 // Finally, if no part of the specified range is overlapped by timestamp 67 // intervals from any transactions in the cache, the low water timestamp is 68 // returned for the read timestamps. 69 GetMax(start, end roachpb.Key) (hlc.Timestamp, uuid.UUID) 70 71 // Metrics returns the Cache's metrics struct. 72 Metrics() Metrics 73 74 // The following methods are used for testing within this package: 75 // 76 // clear clears the cache and resets the low-water mark. 77 clear(lowWater hlc.Timestamp) 78 // getLowWater return the low water mark. 79 getLowWater() hlc.Timestamp 80 } 81 82 // New returns a new timestamp cache with the supplied hybrid-logical clock. 83 func New(clock *hlc.Clock) Cache { 84 if envutil.EnvOrDefaultBool("COCKROACH_USE_TREE_TSCACHE", false) { 85 return newTreeImpl(clock) 86 } 87 return newSklImpl(clock) 88 } 89 90 // cacheValue combines a timestamp with an optional txnID. It is shared between 91 // multiple Cache implementations. 92 type cacheValue struct { 93 ts hlc.Timestamp 94 txnID uuid.UUID 95 } 96 97 // noTxnID is used when a cacheValue has no corresponding TxnID. 98 var noTxnID uuid.UUID 99 100 func (v cacheValue) String() string { 101 var txnIDStr string 102 switch v.txnID { 103 case noTxnID: 104 txnIDStr = "none" 105 default: 106 txnIDStr = v.txnID.String() 107 } 108 return fmt.Sprintf("{ts: %s, txnID: %s}", v.ts, txnIDStr) 109 } 110 111 // ratchetValue returns the cacheValue that results from ratcheting the provided 112 // old and new cacheValues. It also returns flags reflecting whether the value 113 // was updated. 114 // 115 // This ratcheting policy is shared across all Cache implementations, even if 116 // they do not use this function directly. 117 func ratchetValue(old, new cacheValue) (cacheValue, bool) { 118 if old.ts.Less(new.ts) { 119 // Ratchet to new value. 120 return new, true 121 } else if new.ts.Less(old.ts) { 122 // Nothing to update. 123 return old, false 124 } else if new.txnID != old.txnID { 125 // old.ts == new.ts but the values have different txnIDs. Remove the 126 // transaction ID from the value so that it is no longer owned by any 127 // transaction. 128 new.txnID = noTxnID 129 return new, old.txnID != noTxnID 130 } 131 // old == new. 132 return old, false 133 }