summaryrefslogtreecommitdiffstats
path: root/src/rocksdb/cache/cache_key.h
blob: 0b93c6bd9472fb2d3ea43c8169462accbab494f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//  Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).

#pragma once

#include <cstdint>

#include "rocksdb/rocksdb_namespace.h"
#include "rocksdb/slice.h"
#include "table/unique_id_impl.h"

namespace ROCKSDB_NAMESPACE {

class Cache;

// A standard holder for fixed-size block cache keys (and for related caches).
// They are created through one of these, each using its own range of values:
// * CacheKey::CreateUniqueForCacheLifetime
// * CacheKey::CreateUniqueForProcessLifetime
// * Default ctor ("empty" cache key)
// * OffsetableCacheKey->WithOffset
//
// The first two use atomic counters to guarantee uniqueness over the given
// lifetime and the last uses a form of universally unique identifier for
// uniqueness with very high probabilty (and guaranteed for files generated
// during a single process lifetime).
//
// CacheKeys are currently used by calling AsSlice() to pass as a key to
// Cache. For performance, the keys are endianness-dependent (though otherwise
// portable). (Persistable cache entries are not intended to cross platforms.)
class CacheKey {
 public:
  // For convenience, constructs an "empty" cache key that is never returned
  // by other means.
  inline CacheKey() : file_num_etc64_(), offset_etc64_() {}

  inline bool IsEmpty() const {
    return (file_num_etc64_ == 0) & (offset_etc64_ == 0);
  }

  // Use this cache key as a Slice (byte order is endianness-dependent)
  inline Slice AsSlice() const {
    static_assert(sizeof(*this) == 16, "Standardized on 16-byte cache key");
    assert(!IsEmpty());
    return Slice(reinterpret_cast<const char *>(this), sizeof(*this));
  }

  // Create a CacheKey that is unique among others associated with this Cache
  // instance. Depends on Cache::NewId. This is useful for block cache
  // "reservations".
  static CacheKey CreateUniqueForCacheLifetime(Cache *cache);

  // Create a CacheKey that is unique among others for the lifetime of this
  // process. This is useful for saving in a static data member so that
  // different DB instances can agree on a cache key for shared entities,
  // such as for CacheEntryStatsCollector.
  static CacheKey CreateUniqueForProcessLifetime();

 protected:
  friend class OffsetableCacheKey;
  CacheKey(uint64_t file_num_etc64, uint64_t offset_etc64)
      : file_num_etc64_(file_num_etc64), offset_etc64_(offset_etc64) {}
  uint64_t file_num_etc64_;
  uint64_t offset_etc64_;
};

constexpr uint8_t kCacheKeySize = static_cast<uint8_t>(sizeof(CacheKey));

// A file-specific generator of cache keys, sometimes referred to as the
// "base" cache key for a file because all the cache keys for various offsets
// within the file are computed using simple arithmetic. The basis for the
// general approach is dicussed here: https://github.com/pdillinger/unique_id
// Heavily related to GetUniqueIdFromTableProperties.
//
// If the db_id, db_session_id, and file_number come from the file's table
// properties, then the keys will be stable across DB::Open/Close, backup/
// restore, import/export, etc.
//
// This class "is a" CacheKey only privately so that it is not misused as
// a ready-to-use CacheKey.
class OffsetableCacheKey : private CacheKey {
 public:
  // For convenience, constructs an "empty" cache key that should not be used.
  inline OffsetableCacheKey() : CacheKey() {}

  // Constructs an OffsetableCacheKey with the given information about a file.
  // This constructor never generates an "empty" base key.
  OffsetableCacheKey(const std::string &db_id, const std::string &db_session_id,
                     uint64_t file_number);

  // Creates an OffsetableCacheKey from an SST unique ID, so that cache keys
  // can be derived from DB manifest data before reading the file from
  // storage--so that every part of the file can potentially go in a persistent
  // cache.
  //
  // Calling GetSstInternalUniqueId() on a db_id, db_session_id, and
  // file_number and passing the result to this function produces the same
  // base cache key as feeding those inputs directly to the constructor.
  //
  // This is a bijective transformation assuming either id is empty or
  // lower 64 bits is non-zero:
  // * Empty (all zeros) input -> empty (all zeros) output
  // * Lower 64 input is non-zero -> lower 64 output (file_num_etc64_) is
  //   non-zero
  static OffsetableCacheKey FromInternalUniqueId(UniqueIdPtr id);

  // This is the inverse transformation to the above, assuming either empty
  // or lower 64 bits (file_num_etc64_) is non-zero. Perhaps only useful for
  // testing.
  UniqueId64x2 ToInternalUniqueId();

  inline bool IsEmpty() const {
    bool result = file_num_etc64_ == 0;
    assert(!(offset_etc64_ > 0 && result));
    return result;
  }

  // Construct a CacheKey for an offset within a file. An offset is not
  // necessarily a byte offset if a smaller unique identifier of keyable
  // offsets is used.
  //
  // This class was designed to make this hot code extremely fast.
  inline CacheKey WithOffset(uint64_t offset) const {
    assert(!IsEmpty());
    return CacheKey(file_num_etc64_, offset_etc64_ ^ offset);
  }

  // The "common prefix" is a shared prefix for all the returned CacheKeys.
  // It is specific to the file but the same for all offsets within the file.
  static constexpr size_t kCommonPrefixSize = 8;
  inline Slice CommonPrefixSlice() const {
    static_assert(sizeof(file_num_etc64_) == kCommonPrefixSize,
                  "8 byte common prefix expected");
    assert(!IsEmpty());
    assert(&this->file_num_etc64_ == static_cast<const void *>(this));

    return Slice(reinterpret_cast<const char *>(this), kCommonPrefixSize);
  }
};

}  // namespace ROCKSDB_NAMESPACE