unique_id_gen.h 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. // This file is for functions that generate unique identifiers by
  6. // (at least in part) by extracting novel entropy or sources of uniqueness
  7. // from the execution environment. (By contrast, random.h is for algorithmic
  8. // pseudorandomness.)
  9. //
  10. // These functions could eventually migrate to public APIs, such as in Env.
  11. #pragma once
  12. #include <array>
  13. #include <atomic>
  14. #include <cstdint>
  15. #include <type_traits>
  16. #include "port/port.h"
  17. #include "rocksdb/rocksdb_namespace.h"
  18. namespace ROCKSDB_NAMESPACE {
  19. // Generates a new 128-bit identifier that is universally unique
  20. // (with high probability) for each call. The result is split into
  21. // two 64-bit pieces. This function has NOT been validated for use in
  22. // cryptography.
  23. //
  24. // This is used in generating DB session IDs and by Env::GenerateUniqueId
  25. // (used for DB IDENTITY) if the platform does not provide a generator of
  26. // RFC 4122 UUIDs or fails somehow. (Set exclude_port_uuid=true if this
  27. // function is used as a fallback for GenerateRfcUuid, because no need
  28. // trying it again.)
  29. void GenerateRawUniqueId(uint64_t* a, uint64_t* b,
  30. bool exclude_port_uuid = false);
  31. #ifndef NDEBUG
  32. // A version of above with options for challenge testing
  33. void TEST_GenerateRawUniqueId(uint64_t* a, uint64_t* b, bool exclude_port_uuid,
  34. bool exclude_env_details,
  35. bool exclude_random_device);
  36. #endif
  37. // Generates globally unique ids with lower probability of any collisions
  38. // vs. each unique id being independently random (GenerateRawUniqueId).
  39. // We call this "semi-structured" because between different
  40. // SemiStructuredUniqueIdGen objects, the IDs are separated by random
  41. // intervals (unstructured), but within a single SemiStructuredUniqueIdGen
  42. // object, the generated IDs are trivially related (structured). See
  43. // https://github.com/pdillinger/unique_id for how this improves probability
  44. // of no collision. In short, if we have n SemiStructuredUniqueIdGen
  45. // objects each generating m IDs, the first collision is expected at
  46. // around n = sqrt(2^128 / m), equivalently n * sqrt(m) = 2^64,
  47. // rather than n * m = 2^64 for fully random IDs.
  48. class SemiStructuredUniqueIdGen {
  49. public:
  50. // Initializes with random starting state (from GenerateRawUniqueId)
  51. SemiStructuredUniqueIdGen() { Reset(); }
  52. // Re-initializes, but not thread safe
  53. void Reset();
  54. // Assuming no fork(), `lower` is guaranteed unique from one call
  55. // to the next (thread safe).
  56. void GenerateNext(uint64_t* upper, uint64_t* lower);
  57. // For generating smaller values. Will cycle through all the possibilities
  58. // before repeating.
  59. template <typename T>
  60. T GenerateNext() {
  61. static_assert(sizeof(T) <= sizeof(uint64_t));
  62. static_assert(std::is_integral_v<T>);
  63. uint64_t ignore, val;
  64. GenerateNext(&ignore, &val);
  65. return static_cast<T>(val);
  66. }
  67. uint64_t GetBaseUpper() const { return base_upper_; }
  68. private:
  69. uint64_t base_upper_;
  70. uint64_t base_lower_;
  71. std::atomic<uint64_t> counter_;
  72. int64_t saved_process_id_;
  73. };
  74. // A unique id generator that should provide reasonable security against
  75. // predicting the output from previous outputs, but is NOT known to be
  76. // cryptographically secure. Unlike std::random_device, this is guaranteed
  77. // not to block once initialized.
  78. class ALIGN_AS(CACHE_LINE_SIZE) UnpredictableUniqueIdGen {
  79. public:
  80. // Initializes with random starting state (from several GenerateRawUniqueId)
  81. UnpredictableUniqueIdGen() { Reset(); }
  82. // Re-initializes, but not thread safe
  83. void Reset();
  84. // Generate next probabilistically unique value. Thread safe. Uses timing
  85. // information to add to the entropy pool.
  86. void GenerateNext(uint64_t* upper, uint64_t* lower);
  87. // Explicitly include given value for entropy pool instead of timing
  88. // information.
  89. void GenerateNextWithEntropy(uint64_t* upper, uint64_t* lower,
  90. uint64_t extra_entropy);
  91. #ifndef NDEBUG
  92. struct TEST_ZeroInitialized {};
  93. explicit UnpredictableUniqueIdGen(TEST_ZeroInitialized);
  94. std::atomic<uint64_t>& TEST_counter() { return counter_; }
  95. #endif
  96. private:
  97. // 256 bit entropy pool
  98. std::array<std::atomic<uint64_t>, 4> pool_;
  99. // Counter to ensure unique hash inputs
  100. std::atomic<uint64_t> counter_;
  101. };
  102. } // namespace ROCKSDB_NAMESPACE