Skip to content
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

Closed
wants to merge 27 commits into from

Conversation

pdillinger
Copy link
Contributor

@pdillinger pdillinger commented Oct 6, 2021

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:

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.

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
@facebook-github-bot
Copy link
Contributor

@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

1 similar comment
@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

}
}
fprintf(stdout, "(Re-)verified %zu unique IDs\n", id_set_.size());
s = fs.ReopenWritableFile(path_, FileOptions(), &data_file_writer_,
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

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

#include "file/filename.h"
#include "rocksdb/file_system.h"
#include "rocksdb/table_properties.h"
#include "rocksdb/unique_id.h"
#ifdef GFLAGS
#pragma once
Copy link
Contributor

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?

Copy link
Contributor Author

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 ;)

Comment on lines 114 to 115
BijectiveHash2x64((*in_out)[1] + 17391078804906429400U,
(*in_out)[0] + 6417269962128484497U, &hi, &lo);
Copy link
Contributor

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"
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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.")

Status GetSstInternalUniqueId(const std::string &db_id,
const std::string &db_session_id,
uint64_t file_number,
std::array<uint64_t, 3> *out);
Copy link
Contributor

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)

Copy link
Contributor Author

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.

// 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
Copy link
Contributor

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) {
Copy link
Contributor

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?

Copy link
Contributor Author

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 👍

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Comment on lines 13 to 14
// for an SST file from TableProperties. This is supported for block-based
// table files created with RocksDB 6.24 and later. NotSupported will be
Copy link
Contributor

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,
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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

Comment on lines +191 to +194
AppendProperty(result, "unique ID",
s.ok() ? UniqueIdToHumanString(id) : "N/A", prop_delim,
kv_delim);

Copy link
Contributor

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?

Copy link
Contributor Author

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).

Copy link
Contributor

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).

Copy link
Contributor Author

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.

Copy link
Contributor

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) {
Copy link
Contributor

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?

Copy link
Contributor

@mrambacher mrambacher left a 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,
Copy link
Contributor

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.

Comment on lines +191 to +194
AppendProperty(result, "unique ID",
s.ok() ? UniqueIdToHumanString(id) : "N/A", prop_delim,
kv_delim);

Copy link
Contributor

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 pdillinger changed the title Support SST unique IDs Experimental support for SST unique IDs Oct 18, 2021
@facebook-github-bot
Copy link
Contributor

@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,
Copy link
Contributor

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,
Copy link
Contributor

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?

Copy link
Contributor

@zhichao-cao zhichao-cao left a 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.

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@pdillinger has updated the pull request. You must reimport the pull request before landing.

@facebook-github-bot
Copy link
Contributor

@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

1 similar comment
@facebook-github-bot
Copy link
Contributor

@pdillinger has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@pdillinger merged this pull request in ad5325a.

yoori pushed a commit to yoori/rocksdb that referenced this pull request Nov 26, 2023
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants