2016-02-09 23:12:00 +00:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-15 23:03:42 +00:00
|
|
|
// 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).
|
2013-10-16 21:59:46 +00:00
|
|
|
//
|
2011-03-18 22:37:00 +00:00
|
|
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
|
|
|
2020-04-20 20:21:34 +00:00
|
|
|
#include "util/hash.h"
|
2021-08-21 01:40:53 +00:00
|
|
|
|
Experimental support for SST unique IDs (#8990)
Summary:
* New public header unique_id.h and function GetUniqueIdFromTableProperties
which computes a universally unique identifier based on table properties
of table files from recent RocksDB versions.
* Generation of DB session IDs is refactored so that they are
guaranteed unique in the lifetime of a process running RocksDB.
(SemiStructuredUniqueIdGen, new test included.) Along with file numbers,
this enables SST unique IDs to be guaranteed unique among SSTs generated
in a single process, and "better than random" between processes.
See https://github.com/pdillinger/unique_id
* In addition to public API producing 'external' unique IDs, there is a function
for producing 'internal' unique IDs, with functions for converting between the
two. In short, the external ID is "safe" for things people might do with it, and
the internal ID enables more "power user" features for the future. Specifically,
the external ID goes through a hashing layer so that any subset of bits in the
external ID can be used as a hash of the full ID, while also preserving
uniqueness guarantees in the first 128 bits (bijective both on first 128 bits
and on full 192 bits).
Intended follow-up:
* Use the internal unique IDs in cache keys. (Avoid conflicts with https://github.com/facebook/rocksdb/issues/8912) (The file offset can be XORed into
the third 64-bit value of the unique ID.)
* Publish the external unique IDs in FileStorageInfo (https://github.com/facebook/rocksdb/issues/8968)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/8990
Test Plan:
Unit tests added, and checking of unique ids in stress test.
NOTE in stress test we do not generate nearly enough files to thoroughly
stress uniqueness, but the test trims off pieces of the ID to check for
uniqueness so that we can infer (with some assumptions) stronger
properties in the aggregate.
Reviewed By: zhichao-cao, mrambacher
Differential Revision: D31582865
Pulled By: pdillinger
fbshipit-source-id: 1f620c4c86af9abe2a8d177b9ccf2ad2b9f48243
2021-10-19 06:28:28 +00:00
|
|
|
#include <string>
|
2021-08-21 01:40:53 +00:00
|
|
|
|
2020-04-20 20:21:34 +00:00
|
|
|
#include "port/lang.h"
|
2011-03-18 22:37:00 +00:00
|
|
|
#include "util/coding.h"
|
2021-08-21 01:40:53 +00:00
|
|
|
#include "util/hash128.h"
|
|
|
|
#include "util/math128.h"
|
Add new persistent 64-bit hash (#5984)
Summary:
For upcoming new SST filter implementations, we will use a new
64-bit hash function (XXH3 preview, slightly modified). This change
updates hash.{h,cc} for that change, adds unit tests, and out-of-lines
the implementations to keep hash.h as clean/small as possible.
In developing the unit tests, I discovered that the XXH3 preview always
returns zero for the empty string. Zero is problematic for some
algorithms (including an upcoming SST filter implementation) if it
occurs more often than at the "natural" rate, so it should not be
returned from trivial values using trivial seeds. I modified our fork
of XXH3 to return a modest hash of the seed for the empty string.
With hash function details out-of-lines in hash.h, it makes sense to
enable XXH_INLINE_ALL, so that direct calls to XXH64/XXH32/XXH3p
are inlined. To fix array-bounds warnings on some inline calls, I
injected some casts to uintptr_t in xxhash.cc. (Issue reported to Yann.)
Revised: Reverted using XXH_INLINE_ALL for now. Some Facebook
checks are unhappy about #include on xxhash.cc file. I would
fix that by rename to xxhash_cc.h, but to best preserve history I want
to do that in a separate commit (PR) from the uintptr casts.
Also updated filter_bench for this change, improving the performance
predictability of dry run hashing and adding support for 64-bit hash
(for upcoming new SST filter implementations, minor dead code in the
tool for now).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5984
Differential Revision: D18246567
Pulled By: pdillinger
fbshipit-source-id: 6162fbf6381d63c8cc611dd7ec70e1ddc883fbb8
2019-10-31 23:34:51 +00:00
|
|
|
#include "util/xxhash.h"
|
2021-08-21 01:40:53 +00:00
|
|
|
#include "util/xxph3.h"
|
2011-03-18 22:37:00 +00:00
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
namespace ROCKSDB_NAMESPACE {
|
2011-03-18 22:37:00 +00:00
|
|
|
|
Integrity protection for live updates to WriteBatch (#7748)
Summary:
This PR adds the foundation classes for key-value integrity protection and the first use case: protecting live updates from the source buffers added to `WriteBatch` through the destination buffer in `MemTable`. The width of the protection info is not yet configurable -- only eight bytes per key is supported. This PR allows users to enable protection by constructing `WriteBatch` with `protection_bytes_per_key == 8`. It does not yet expose a way for users to get integrity protection via other write APIs (e.g., `Put()`, `Merge()`, `Delete()`, etc.).
The foundation classes (`ProtectionInfo.*`) embed the coverage info in their type, and provide `Protect.*()` and `Strip.*()` functions to navigate between types with different coverage. For making bytes per key configurable (for powers of two up to eight) in the future, these classes are templated on the unsigned integer type used to store the protection info. That integer contains the XOR'd result of hashes with independent seeds for all covered fields. For integer fields, the hash is computed on the raw unadjusted bytes, so the result is endian-dependent. The most significant bytes are truncated when the hash value (8 bytes) is wider than the protection integer.
When `WriteBatch` is constructed with `protection_bytes_per_key == 8`, we hold a `ProtectionInfoKVOTC` (i.e., one that covers key, value, optype aka `ValueType`, timestamp, and CF ID) for each entry added to the batch. The protection info is generated from the original buffers passed by the user, as well as the original metadata generated internally. When writing to memtable, each entry is transformed to a `ProtectionInfoKVOTS` (i.e., dropping coverage of CF ID and adding coverage of sequence number), since at that point we know the sequence number, and have already selected a memtable corresponding to a particular CF. This protection info is verified once the entry is encoded in the `MemTable` buffer.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/7748
Test Plan:
- an integration test to verify a wide variety of single-byte changes to the encoded `MemTable` buffer are caught
- add to stress/crash test to verify it works in variety of configs/operations without intentional corruption
- [deferred] unit tests for `ProtectionInfo.*` classes for edge cases like KV swap, `SliceParts` and `Slice` APIs are interchangeable, etc.
Reviewed By: pdillinger
Differential Revision: D25754492
Pulled By: ajkr
fbshipit-source-id: e481bac6c03c2ab268be41359730f1ceb9964866
2021-01-29 20:17:17 +00:00
|
|
|
uint64_t (*kGetSliceNPHash64UnseededFnPtr)(const Slice&) = &GetSliceHash64;
|
|
|
|
|
2011-03-18 22:37:00 +00:00
|
|
|
uint32_t Hash(const char* data, size_t n, uint32_t seed) {
|
Add new persistent 64-bit hash (#5984)
Summary:
For upcoming new SST filter implementations, we will use a new
64-bit hash function (XXH3 preview, slightly modified). This change
updates hash.{h,cc} for that change, adds unit tests, and out-of-lines
the implementations to keep hash.h as clean/small as possible.
In developing the unit tests, I discovered that the XXH3 preview always
returns zero for the empty string. Zero is problematic for some
algorithms (including an upcoming SST filter implementation) if it
occurs more often than at the "natural" rate, so it should not be
returned from trivial values using trivial seeds. I modified our fork
of XXH3 to return a modest hash of the seed for the empty string.
With hash function details out-of-lines in hash.h, it makes sense to
enable XXH_INLINE_ALL, so that direct calls to XXH64/XXH32/XXH3p
are inlined. To fix array-bounds warnings on some inline calls, I
injected some casts to uintptr_t in xxhash.cc. (Issue reported to Yann.)
Revised: Reverted using XXH_INLINE_ALL for now. Some Facebook
checks are unhappy about #include on xxhash.cc file. I would
fix that by rename to xxhash_cc.h, but to best preserve history I want
to do that in a separate commit (PR) from the uintptr casts.
Also updated filter_bench for this change, improving the performance
predictability of dry run hashing and adding support for 64-bit hash
(for upcoming new SST filter implementations, minor dead code in the
tool for now).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5984
Differential Revision: D18246567
Pulled By: pdillinger
fbshipit-source-id: 6162fbf6381d63c8cc611dd7ec70e1ddc883fbb8
2019-10-31 23:34:51 +00:00
|
|
|
// MurmurHash1 - fast but mediocre quality
|
|
|
|
// https://github.com/aappleby/smhasher/wiki/MurmurHash1
|
|
|
|
//
|
2011-03-18 22:37:00 +00:00
|
|
|
const uint32_t m = 0xc6a4a793;
|
|
|
|
const uint32_t r = 24;
|
|
|
|
const char* limit = data + n;
|
2014-11-11 21:47:22 +00:00
|
|
|
uint32_t h = static_cast<uint32_t>(seed ^ (n * m));
|
2011-03-18 22:37:00 +00:00
|
|
|
|
|
|
|
// Pick up four bytes at a time
|
|
|
|
while (data + 4 <= limit) {
|
|
|
|
uint32_t w = DecodeFixed32(data);
|
|
|
|
data += 4;
|
|
|
|
h += w;
|
|
|
|
h *= m;
|
|
|
|
h ^= (h >> 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pick up remaining bytes
|
|
|
|
switch (limit - data) {
|
2017-07-10 19:22:26 +00:00
|
|
|
// Note: The original hash implementation used data[i] << shift, which
|
|
|
|
// promotes the char to int and then performs the shift. If the char is
|
|
|
|
// negative, the shift is undefined behavior in C++. The hash algorithm is
|
|
|
|
// part of the format definition, so we cannot change it; to obtain the same
|
|
|
|
// behavior in a legal way we just cast to uint32_t, which will do
|
|
|
|
// sign-extension. To guarantee compatibility with architectures where chars
|
|
|
|
// are unsigned we first cast the char to int8_t.
|
2011-03-18 22:37:00 +00:00
|
|
|
case 3:
|
2017-07-10 19:22:26 +00:00
|
|
|
h += static_cast<uint32_t>(static_cast<int8_t>(data[2])) << 16;
|
2018-07-13 17:47:49 +00:00
|
|
|
FALLTHROUGH_INTENDED;
|
2011-03-18 22:37:00 +00:00
|
|
|
case 2:
|
2017-07-10 19:22:26 +00:00
|
|
|
h += static_cast<uint32_t>(static_cast<int8_t>(data[1])) << 8;
|
2018-07-13 17:47:49 +00:00
|
|
|
FALLTHROUGH_INTENDED;
|
2011-03-18 22:37:00 +00:00
|
|
|
case 1:
|
2017-07-10 19:22:26 +00:00
|
|
|
h += static_cast<uint32_t>(static_cast<int8_t>(data[0]));
|
2011-03-18 22:37:00 +00:00
|
|
|
h *= m;
|
|
|
|
h ^= (h >> r);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
Add new persistent 64-bit hash (#5984)
Summary:
For upcoming new SST filter implementations, we will use a new
64-bit hash function (XXH3 preview, slightly modified). This change
updates hash.{h,cc} for that change, adds unit tests, and out-of-lines
the implementations to keep hash.h as clean/small as possible.
In developing the unit tests, I discovered that the XXH3 preview always
returns zero for the empty string. Zero is problematic for some
algorithms (including an upcoming SST filter implementation) if it
occurs more often than at the "natural" rate, so it should not be
returned from trivial values using trivial seeds. I modified our fork
of XXH3 to return a modest hash of the seed for the empty string.
With hash function details out-of-lines in hash.h, it makes sense to
enable XXH_INLINE_ALL, so that direct calls to XXH64/XXH32/XXH3p
are inlined. To fix array-bounds warnings on some inline calls, I
injected some casts to uintptr_t in xxhash.cc. (Issue reported to Yann.)
Revised: Reverted using XXH_INLINE_ALL for now. Some Facebook
checks are unhappy about #include on xxhash.cc file. I would
fix that by rename to xxhash_cc.h, but to best preserve history I want
to do that in a separate commit (PR) from the uintptr casts.
Also updated filter_bench for this change, improving the performance
predictability of dry run hashing and adding support for 64-bit hash
(for upcoming new SST filter implementations, minor dead code in the
tool for now).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5984
Differential Revision: D18246567
Pulled By: pdillinger
fbshipit-source-id: 6162fbf6381d63c8cc611dd7ec70e1ddc883fbb8
2019-10-31 23:34:51 +00:00
|
|
|
// We are standardizing on a preview release of XXH3, because that's
|
|
|
|
// the best available at time of standardizing.
|
|
|
|
//
|
|
|
|
// In testing (mostly Intel Skylake), this hash function is much more
|
|
|
|
// thorough than Hash32 and is almost universally faster. Hash() only
|
|
|
|
// seems faster when passing runtime-sized keys of the same small size
|
|
|
|
// (less than about 24 bytes) thousands of times in a row; this seems
|
|
|
|
// to allow the branch predictor to work some magic. XXH3's speed is
|
|
|
|
// much less dependent on branch prediction.
|
|
|
|
//
|
|
|
|
// Hashing with a prefix extractor is potentially a common case of
|
|
|
|
// hashing objects of small, predictable size. We could consider
|
|
|
|
// bundling hash functions specialized for particular lengths with
|
|
|
|
// the prefix extractors.
|
|
|
|
uint64_t Hash64(const char* data, size_t n, uint64_t seed) {
|
2021-08-21 01:40:53 +00:00
|
|
|
return XXPH3_64bits_withSeed(data, n, seed);
|
Add new persistent 64-bit hash (#5984)
Summary:
For upcoming new SST filter implementations, we will use a new
64-bit hash function (XXH3 preview, slightly modified). This change
updates hash.{h,cc} for that change, adds unit tests, and out-of-lines
the implementations to keep hash.h as clean/small as possible.
In developing the unit tests, I discovered that the XXH3 preview always
returns zero for the empty string. Zero is problematic for some
algorithms (including an upcoming SST filter implementation) if it
occurs more often than at the "natural" rate, so it should not be
returned from trivial values using trivial seeds. I modified our fork
of XXH3 to return a modest hash of the seed for the empty string.
With hash function details out-of-lines in hash.h, it makes sense to
enable XXH_INLINE_ALL, so that direct calls to XXH64/XXH32/XXH3p
are inlined. To fix array-bounds warnings on some inline calls, I
injected some casts to uintptr_t in xxhash.cc. (Issue reported to Yann.)
Revised: Reverted using XXH_INLINE_ALL for now. Some Facebook
checks are unhappy about #include on xxhash.cc file. I would
fix that by rename to xxhash_cc.h, but to best preserve history I want
to do that in a separate commit (PR) from the uintptr casts.
Also updated filter_bench for this change, improving the performance
predictability of dry run hashing and adding support for 64-bit hash
(for upcoming new SST filter implementations, minor dead code in the
tool for now).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5984
Differential Revision: D18246567
Pulled By: pdillinger
fbshipit-source-id: 6162fbf6381d63c8cc611dd7ec70e1ddc883fbb8
2019-10-31 23:34:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Hash64(const char* data, size_t n) {
|
|
|
|
// Same as seed = 0
|
2021-08-21 01:40:53 +00:00
|
|
|
return XXPH3_64bits(data, n);
|
Add new persistent 64-bit hash (#5984)
Summary:
For upcoming new SST filter implementations, we will use a new
64-bit hash function (XXH3 preview, slightly modified). This change
updates hash.{h,cc} for that change, adds unit tests, and out-of-lines
the implementations to keep hash.h as clean/small as possible.
In developing the unit tests, I discovered that the XXH3 preview always
returns zero for the empty string. Zero is problematic for some
algorithms (including an upcoming SST filter implementation) if it
occurs more often than at the "natural" rate, so it should not be
returned from trivial values using trivial seeds. I modified our fork
of XXH3 to return a modest hash of the seed for the empty string.
With hash function details out-of-lines in hash.h, it makes sense to
enable XXH_INLINE_ALL, so that direct calls to XXH64/XXH32/XXH3p
are inlined. To fix array-bounds warnings on some inline calls, I
injected some casts to uintptr_t in xxhash.cc. (Issue reported to Yann.)
Revised: Reverted using XXH_INLINE_ALL for now. Some Facebook
checks are unhappy about #include on xxhash.cc file. I would
fix that by rename to xxhash_cc.h, but to best preserve history I want
to do that in a separate commit (PR) from the uintptr casts.
Also updated filter_bench for this change, improving the performance
predictability of dry run hashing and adding support for 64-bit hash
(for upcoming new SST filter implementations, minor dead code in the
tool for now).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5984
Differential Revision: D18246567
Pulled By: pdillinger
fbshipit-source-id: 6162fbf6381d63c8cc611dd7ec70e1ddc883fbb8
2019-10-31 23:34:51 +00:00
|
|
|
}
|
|
|
|
|
Integrity protection for live updates to WriteBatch (#7748)
Summary:
This PR adds the foundation classes for key-value integrity protection and the first use case: protecting live updates from the source buffers added to `WriteBatch` through the destination buffer in `MemTable`. The width of the protection info is not yet configurable -- only eight bytes per key is supported. This PR allows users to enable protection by constructing `WriteBatch` with `protection_bytes_per_key == 8`. It does not yet expose a way for users to get integrity protection via other write APIs (e.g., `Put()`, `Merge()`, `Delete()`, etc.).
The foundation classes (`ProtectionInfo.*`) embed the coverage info in their type, and provide `Protect.*()` and `Strip.*()` functions to navigate between types with different coverage. For making bytes per key configurable (for powers of two up to eight) in the future, these classes are templated on the unsigned integer type used to store the protection info. That integer contains the XOR'd result of hashes with independent seeds for all covered fields. For integer fields, the hash is computed on the raw unadjusted bytes, so the result is endian-dependent. The most significant bytes are truncated when the hash value (8 bytes) is wider than the protection integer.
When `WriteBatch` is constructed with `protection_bytes_per_key == 8`, we hold a `ProtectionInfoKVOTC` (i.e., one that covers key, value, optype aka `ValueType`, timestamp, and CF ID) for each entry added to the batch. The protection info is generated from the original buffers passed by the user, as well as the original metadata generated internally. When writing to memtable, each entry is transformed to a `ProtectionInfoKVOTS` (i.e., dropping coverage of CF ID and adding coverage of sequence number), since at that point we know the sequence number, and have already selected a memtable corresponding to a particular CF. This protection info is verified once the entry is encoded in the `MemTable` buffer.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/7748
Test Plan:
- an integration test to verify a wide variety of single-byte changes to the encoded `MemTable` buffer are caught
- add to stress/crash test to verify it works in variety of configs/operations without intentional corruption
- [deferred] unit tests for `ProtectionInfo.*` classes for edge cases like KV swap, `SliceParts` and `Slice` APIs are interchangeable, etc.
Reviewed By: pdillinger
Differential Revision: D25754492
Pulled By: ajkr
fbshipit-source-id: e481bac6c03c2ab268be41359730f1ceb9964866
2021-01-29 20:17:17 +00:00
|
|
|
uint64_t GetSlicePartsNPHash64(const SliceParts& data, uint64_t seed) {
|
|
|
|
// TODO(ajkr): use XXH3 streaming APIs to avoid the copy/allocation.
|
|
|
|
size_t concat_len = 0;
|
|
|
|
for (int i = 0; i < data.num_parts; ++i) {
|
|
|
|
concat_len += data.parts[i].size();
|
|
|
|
}
|
|
|
|
std::string concat_data;
|
|
|
|
concat_data.reserve(concat_len);
|
|
|
|
for (int i = 0; i < data.num_parts; ++i) {
|
|
|
|
concat_data.append(data.parts[i].data(), data.parts[i].size());
|
|
|
|
}
|
|
|
|
assert(concat_data.size() == concat_len);
|
|
|
|
return NPHash64(concat_data.data(), concat_len, seed);
|
|
|
|
}
|
|
|
|
|
2021-08-21 01:40:53 +00:00
|
|
|
Unsigned128 Hash128(const char* data, size_t n, uint64_t seed) {
|
|
|
|
auto h = XXH3_128bits_withSeed(data, n, seed);
|
|
|
|
return (Unsigned128{h.high64} << 64) | (h.low64);
|
|
|
|
}
|
|
|
|
|
|
|
|
Unsigned128 Hash128(const char* data, size_t n) {
|
|
|
|
// Same as seed = 0
|
|
|
|
auto h = XXH3_128bits(data, n);
|
|
|
|
return (Unsigned128{h.high64} << 64) | (h.low64);
|
|
|
|
}
|
|
|
|
|
Built-in support for generating unique IDs, bug fix (#8708)
Summary:
Env::GenerateUniqueId() works fine on Windows and on POSIX
where /proc/sys/kernel/random/uuid exists. Our other implementation is
flawed and easily produces collision in a new multi-threaded test.
As we rely more heavily on DB session ID uniqueness, this becomes a
serious issue.
This change combines several individually suitable entropy sources
for reliable generation of random unique IDs, with goal of uniqueness
and portability, not cryptographic strength nor maximum speed.
Specifically:
* Moves code for getting UUIDs from the OS to port::GenerateRfcUuid
rather than in Env implementation details. Callers are now told whether
the operation fails or succeeds.
* Adds an internal API GenerateRawUniqueId for generating high-quality
128-bit unique identifiers, by combining entropy from three "tracks":
* Lots of info from default Env like time, process id, and hostname.
* std::random_device
* port::GenerateRfcUuid (when working)
* Built-in implementations of Env::GenerateUniqueId() will now always
produce an RFC 4122 UUID string, either from platform-specific API or
by converting the output of GenerateRawUniqueId.
DB session IDs now use GenerateRawUniqueId while DB IDs (not as
critical) try to use port::GenerateRfcUuid but fall back on
GenerateRawUniqueId with conversion to an RFC 4122 UUID.
GenerateRawUniqueId is declared and defined under env/ rather than util/
or even port/ because of the Env dependency.
Likely follow-up: enhance GenerateRawUniqueId to be faster after the
first call and to guarantee uniqueness within the lifetime of a single
process (imparting the same property onto DB session IDs).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/8708
Test Plan:
A new mini-stress test in env_test checks the various public
and internal APIs for uniqueness, including each track of
GenerateRawUniqueId individually. We can't hope to verify anywhere close
to 128 bits of entropy, but it can at least detect flaws as bad as the
old code. Serial execution of the new tests takes about 350 ms on
my machine.
Reviewed By: zhichao-cao, mrambacher
Differential Revision: D30563780
Pulled By: pdillinger
fbshipit-source-id: de4c9ff4b2f581cf784fcedb5f39f16e5185c364
2021-08-30 22:19:39 +00:00
|
|
|
void Hash2x64(const char* data, size_t n, uint64_t* high64, uint64_t* low64) {
|
|
|
|
// Same as seed = 0
|
|
|
|
auto h = XXH3_128bits(data, n);
|
|
|
|
*high64 = h.high64;
|
|
|
|
*low64 = h.low64;
|
|
|
|
}
|
|
|
|
|
Experimental support for SST unique IDs (#8990)
Summary:
* New public header unique_id.h and function GetUniqueIdFromTableProperties
which computes a universally unique identifier based on table properties
of table files from recent RocksDB versions.
* Generation of DB session IDs is refactored so that they are
guaranteed unique in the lifetime of a process running RocksDB.
(SemiStructuredUniqueIdGen, new test included.) Along with file numbers,
this enables SST unique IDs to be guaranteed unique among SSTs generated
in a single process, and "better than random" between processes.
See https://github.com/pdillinger/unique_id
* In addition to public API producing 'external' unique IDs, there is a function
for producing 'internal' unique IDs, with functions for converting between the
two. In short, the external ID is "safe" for things people might do with it, and
the internal ID enables more "power user" features for the future. Specifically,
the external ID goes through a hashing layer so that any subset of bits in the
external ID can be used as a hash of the full ID, while also preserving
uniqueness guarantees in the first 128 bits (bijective both on first 128 bits
and on full 192 bits).
Intended follow-up:
* Use the internal unique IDs in cache keys. (Avoid conflicts with https://github.com/facebook/rocksdb/issues/8912) (The file offset can be XORed into
the third 64-bit value of the unique ID.)
* Publish the external unique IDs in FileStorageInfo (https://github.com/facebook/rocksdb/issues/8968)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/8990
Test Plan:
Unit tests added, and checking of unique ids in stress test.
NOTE in stress test we do not generate nearly enough files to thoroughly
stress uniqueness, but the test trims off pieces of the ID to check for
uniqueness so that we can infer (with some assumptions) stronger
properties in the aggregate.
Reviewed By: zhichao-cao, mrambacher
Differential Revision: D31582865
Pulled By: pdillinger
fbshipit-source-id: 1f620c4c86af9abe2a8d177b9ccf2ad2b9f48243
2021-10-19 06:28:28 +00:00
|
|
|
void Hash2x64(const char* data, size_t n, uint64_t seed, uint64_t* high64,
|
|
|
|
uint64_t* low64) {
|
|
|
|
auto h = XXH3_128bits_withSeed(data, n, seed);
|
|
|
|
*high64 = h.high64;
|
|
|
|
*low64 = h.low64;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
inline uint64_t XXH3_avalanche(uint64_t h64) {
|
|
|
|
h64 ^= h64 >> 37;
|
|
|
|
h64 *= 0x165667919E3779F9U;
|
|
|
|
h64 ^= h64 >> 32;
|
|
|
|
return h64;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline uint64_t XXH3_unavalanche(uint64_t h64) {
|
|
|
|
h64 ^= h64 >> 32;
|
|
|
|
h64 *= 0x8da8ee41d6df849U; // inverse of 0x165667919E3779F9U
|
|
|
|
h64 ^= h64 >> 37;
|
|
|
|
return h64;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
void BijectiveHash2x64(uint64_t in_high64, uint64_t in_low64, uint64_t seed,
|
|
|
|
uint64_t* out_high64, uint64_t* out_low64) {
|
|
|
|
// Adapted from XXH3_len_9to16_128b
|
|
|
|
const uint64_t bitflipl = /*secret part*/ 0x59973f0033362349U - seed;
|
|
|
|
const uint64_t bitfliph = /*secret part*/ 0xc202797692d63d58U + seed;
|
|
|
|
Unsigned128 tmp128 =
|
|
|
|
Multiply64to128(in_low64 ^ in_high64 ^ bitflipl, 0x9E3779B185EBCA87U);
|
|
|
|
uint64_t lo = Lower64of128(tmp128);
|
|
|
|
uint64_t hi = Upper64of128(tmp128);
|
|
|
|
lo += 0x3c0000000000000U; // (len - 1) << 54
|
|
|
|
in_high64 ^= bitfliph;
|
|
|
|
hi += in_high64 + (Lower32of64(in_high64) * uint64_t{0x85EBCA76});
|
|
|
|
lo ^= EndianSwapValue(hi);
|
|
|
|
tmp128 = Multiply64to128(lo, 0xC2B2AE3D27D4EB4FU);
|
|
|
|
lo = Lower64of128(tmp128);
|
|
|
|
hi = Upper64of128(tmp128) + (hi * 0xC2B2AE3D27D4EB4FU);
|
|
|
|
*out_low64 = XXH3_avalanche(lo);
|
|
|
|
*out_high64 = XXH3_avalanche(hi);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BijectiveUnhash2x64(uint64_t in_high64, uint64_t in_low64, uint64_t seed,
|
|
|
|
uint64_t* out_high64, uint64_t* out_low64) {
|
|
|
|
// Inverted above (also consulting XXH3_len_9to16_128b)
|
|
|
|
const uint64_t bitflipl = /*secret part*/ 0x59973f0033362349U - seed;
|
|
|
|
const uint64_t bitfliph = /*secret part*/ 0xc202797692d63d58U + seed;
|
|
|
|
uint64_t lo = XXH3_unavalanche(in_low64);
|
|
|
|
uint64_t hi = XXH3_unavalanche(in_high64);
|
|
|
|
lo *= 0xba79078168d4baf; // inverse of 0xC2B2AE3D27D4EB4FU
|
|
|
|
hi -= Upper64of128(Multiply64to128(lo, 0xC2B2AE3D27D4EB4FU));
|
|
|
|
hi *= 0xba79078168d4baf; // inverse of 0xC2B2AE3D27D4EB4FU
|
|
|
|
lo ^= EndianSwapValue(hi);
|
|
|
|
lo -= 0x3c0000000000000U;
|
|
|
|
lo *= 0x887493432badb37U; // inverse of 0x9E3779B185EBCA87U
|
|
|
|
hi -= Upper64of128(Multiply64to128(lo, 0x9E3779B185EBCA87U));
|
|
|
|
uint32_t tmp32 = Lower32of64(hi) * 0xb6c92f47; // inverse of 0x85EBCA77
|
|
|
|
hi -= tmp32;
|
|
|
|
hi = (hi & 0xFFFFFFFF00000000U) -
|
|
|
|
((tmp32 * uint64_t{0x85EBCA76}) & 0xFFFFFFFF00000000U) + tmp32;
|
|
|
|
hi ^= bitfliph;
|
|
|
|
lo ^= hi ^ bitflipl;
|
|
|
|
*out_high64 = hi;
|
|
|
|
*out_low64 = lo;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BijectiveHash2x64(uint64_t in_high64, uint64_t in_low64,
|
|
|
|
uint64_t* out_high64, uint64_t* out_low64) {
|
|
|
|
BijectiveHash2x64(in_high64, in_low64, /*seed*/ 0, out_high64, out_low64);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BijectiveUnhash2x64(uint64_t in_high64, uint64_t in_low64,
|
|
|
|
uint64_t* out_high64, uint64_t* out_low64) {
|
|
|
|
BijectiveUnhash2x64(in_high64, in_low64, /*seed*/ 0, out_high64, out_low64);
|
|
|
|
}
|
2020-02-20 20:07:53 +00:00
|
|
|
} // namespace ROCKSDB_NAMESPACE
|