-
Notifications
You must be signed in to change notification settings - Fork 6.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Experimental support for SST unique IDs #8990
Conversation
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 * TODO: explain internal vs. external ID Intended follow-up (avoid conflicts with facebook#8912): use the internal unique IDs in cache keys. (The file offset can be XORed into the third 64-bit value of the unique ID.) Test Plan: Unit tests added TODO: stress test support
@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
1 similar comment
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
db_stress_tool/db_stress_listener.cc
Outdated
} | ||
} | ||
fprintf(stdout, "(Re-)verified %zu unique IDs\n", id_set_.size()); | ||
s = fs.ReopenWritableFile(path_, FileOptions(), &data_file_writer_, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this always work? You are opening a file for write that is still open for read. Not sure that all OS (e.g. Windows) supports that.
Maybe clear/close the readable file first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, sure I can reset the reader
|
||
namespace ROCKSDB_NAMESPACE { | ||
|
||
#ifdef GFLAGS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason this file depends on GFLAGS or is it just everything in db_stress_tool does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, everything in db_stress
db_stress_tool/db_stress_listener.h
Outdated
#include "file/filename.h" | ||
#include "rocksdb/file_system.h" | ||
#include "rocksdb/table_properties.h" | ||
#include "rocksdb/unique_id.h" | ||
#ifdef GFLAGS | ||
#pragma once |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the pragma once be first in the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IDE has a mind of its own ;)
table/unique_id.cc
Outdated
BijectiveHash2x64((*in_out)[1] + 17391078804906429400U, | ||
(*in_out)[0] + 6417269962128484497U, &hi, &lo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make these constants into constexpr or other constants? They are used at least a few times and it might be nice to somewhere (in a comment) outline the math behind them.
|
||
#include <array> | ||
|
||
#include "rocksdb/unique_id.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oddly enough, I am not sure this include is necessary. What is being used from unique_id here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This header is a logical extension of that one, even if there isn't currently a dependency.
|
||
namespace ROCKSDB_NAMESPACE { | ||
|
||
// Helper for GetUniqueIdFromTableProperties. This function can also be used |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am uncertain if these methods belong here, versus being static/private to the source file. Just checking if you mean for them to be semi-public and accessible outside of the source. At least a few of them do not sound as if they are meant for consumption outside of the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I don't put it in a .h file in this PR, it will be put in .h in the next PR. ("Intended follow-up: Use the internal unique IDs in cache keys.")
table/unique_id_impl.h
Outdated
Status GetSstInternalUniqueId(const std::string &db_id, | ||
const std::string &db_session_id, | ||
uint64_t file_number, | ||
std::array<uint64_t, 3> *out); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It it worth defining this std::array as its own type (even just via using)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. A previous draft of this work was very type-heavy (struct RocksUuid, etc.) and it made a mass of code. I may have over-corrected, and a type-using for this one seems good.
table/unique_id_impl.h
Outdated
// Convert numerical format to byte format for public API | ||
std::string EncodeUniqueIdBytes(const std::array<uint64_t, 3> &in); | ||
|
||
// Reformat a random value down to a compact but friendly format, 20 chars |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment talks more about implementation details rather than the use of this function. This comment probably belongs in the C++ file rather than here. The comment does not seem to match what the function appears as if should be doing (generating a session id from two inputs?).
// result is accurate mod 2^64. | ||
template <int kBase> | ||
inline bool ParseBaseChars(const char** buf, size_t n, uint64_t* v) { | ||
while (n) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the expectation of "v" on input? Does it need to be 0 or should it be cleared here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a comment 👍
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
include/rocksdb/unique_id.h
Outdated
// for an SST file from TableProperties. This is supported for block-based | ||
// table files created with RocksDB 6.24 and later. NotSupported will be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this support a function of properties that only exist in BB tables > 6.24? I see nothing here that appears specific to block-based files.
// first 128 bits. See https://github.com/pdillinger/unique_id | ||
// Using the full 192 bits, we expect to generate roughly 2^96 * sqrt(n) | ||
// files before first collision. | ||
Status GetUniqueIdFromTableProperties(const TableProperties &props, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason for the numeric version of the string rather than always using/returning the human readable one? If there are issues with .c_str and the like, the human readable one seems much safer to use and manage under most scenarios and if the numeric one is required for something, it could be translated internally.
Alternatively, maybe it is better to flip them around and have the human readable the "default" and have a means to convert it to the numeric representation on demand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am always annoyed when an API only gives me access to "raw data" by going through "pretty printed" data. And users tend to be lazy and they're likely to use the human-readable version as key in a map<string, whatever> without thinking about the performance.
I could go back to something like #8362 with a class for unique IDs, providing operators, hash, ToHumanString, etc. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My personal preference is to have a "UniqueId" class and have methods off of it (ToHumanString, RawData, static FromTableProperties, etc) rather than having methods "floating" in namespace. This model better associates the functionality IMO and helps eliminate "bad" data passed into some of these methods.
Having said that, it is probably more of a stylistic question than a correctness question. If you think the class style would have benefits, please mark these methods as "experimental" so they can be more easily changed in a future release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marking experimental and will likely revise API in follow-up PR
AppendProperty(result, "unique ID", | ||
s.ok() ? UniqueIdToHumanString(id) : "N/A", prop_delim, | ||
kv_delim); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to NOT add the unique id at all to the properties file if the GetUnique method failed? Otherwise, if you needed to compare it, would we need to check for "N/A" as a special case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only used for human-readable "dump" operations, as in sst_dump --show_properties. Listing "N/A" provides more information because it can indicate that the sst_dump tool supports dumping unique IDs but that the file does not (have one).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for clarity purposes, this "unique ID" int he properties file will not be used by the existing code but is more of a forensics/debugging property? It will not be used as a cache key or the like? I am wondering if the SW will ever need to parse it (rather than simply display it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what you are referring to as "properties file" but this is for forensics / debugging. We have some test code that parses this output, but it would be odd for others to parse it. Even if they do, the likelihood that they feed it old table files--and without noticing collisions on "N/A"--is low IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I meant in the "TableProperties", not "properties file". Too much context switching. The question was if any code is going to read that "unique id" property from an SST file and attempt to do anything with it, other than print it out. Anything for correlation or for caching? If not, then N/A is fine.
} | ||
} // namespace | ||
|
||
TEST_F(TablePropertyTest, UniqueIdsSchemaAndQuality) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a test that the unique ID can be successfully retrieved from an existing SST file via its properties?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
// first 128 bits. See https://github.com/pdillinger/unique_id | ||
// Using the full 192 bits, we expect to generate roughly 2^96 * sqrt(n) | ||
// files before first collision. | ||
Status GetUniqueIdFromTableProperties(const TableProperties &props, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My personal preference is to have a "UniqueId" class and have methods off of it (ToHumanString, RawData, static FromTableProperties, etc) rather than having methods "floating" in namespace. This model better associates the functionality IMO and helps eliminate "bad" data passed into some of these methods.
Having said that, it is probably more of a stylistic question than a correctness question. If you think the class style would have benefits, please mark these methods as "experimental" so they can be more easily changed in a future release.
AppendProperty(result, "unique ID", | ||
s.ok() ? UniqueIdToHumanString(id) : "N/A", prop_delim, | ||
kv_delim); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for clarity purposes, this "unique ID" int he properties file will not be used by the existing code but is more of a forensics/debugging property? It will not be used as a cache key or the like? I am wondering if the SW will ever need to parse it (rather than simply display it).
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
// unique id, so can be manipulated in more ways but very carefully. | ||
// These must be long term stable to ensure GetUniqueIdFromTableProperties | ||
// is long term stable. | ||
Status GetSstInternalUniqueId(const std::string &db_id, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the relationship between the internal unique ID and the public one we can get belonging to the same SST table?
|
||
// Hash 128 bits to 128 bits, guaranteed not to lose data (equivalent to | ||
// Hash2x64 on 16 bytes little endian) | ||
void BijectiveHash2x64(uint64_t in_high64, uint64_t in_low64, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be better to add a little bit more explanation about this hash algorithm and the potential usage in the .h header?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM in general and make sure the windows build failures are fixed.
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
@pdillinger has updated the pull request. You must reimport the pull request before landing. |
@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
1 similar comment
@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator. |
@pdillinger merged this pull request in ad5325a. |
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 facebook/rocksdb#8912) (The file offset can be XORed into the third 64-bit value of the unique ID.) * Publish the external unique IDs in FileStorageInfo (facebook/rocksdb#8968) Pull Request resolved: facebook/rocksdb#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
Summary:
which computes a universally unique identifier based on table properties
of table files from recent RocksDB versions.
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
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:
the third 64-bit value of the unique ID.)
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.