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  }