mirror of https://github.com/facebook/rocksdb.git
Add timestamp support in dump_wal/dump/idump (#12690)
Summary: As titled. For dumping wal files, since a mapping from column family id to the user comparator object is needed to print the timestamp in human readable format, option `[--db=<db_path>]` is added to `dump_wal` command to allow the user to choose to optionally open the DB as read only instance and dump the wal file with better timestamp formatting. Pull Request resolved: https://github.com/facebook/rocksdb/pull/12690 Test Plan: Manually tested dump_wal: [dump a wal file specified with --walfile] ``` >> ./ldb --walfile=$TEST_DB/000004.log dump_wal --print_value >>1,1,28,13,PUT(0) : 0x666F6F0100000000000000 : 0x7631 (Column family id: [0] contained in WAL are not opened in DB. Applied default hex formatting for user key. Specify --db=<db_path> to open DB for better user key formatting if it contains timestamp.) ``` [dump with --db specified for better timestamp formatting] ``` >> ./ldb --walfile=$TEST_DB/000004.log dump_wal --db=$TEST_DB --print_value >> 1,1,28,13,PUT(0) : 0x666F6F|timestamp:1 : 0x7631 ``` dump: [dump a file specified with --path] ``` >>./ldb --path=/tmp/rocksdbtest-501/column_family_test_75359_17910784957761284041/000004.log dump Sequence,Count,ByteSize,Physical Offset,Key(s) : value 1,1,28,13,PUT(0) : 0x666F6F0100000000000000 : 0x7631 (Column family id: [0] contained in WAL are not opened in DB. Applied default hex formatting for user key. Specify --db=<db_path> to open DB for better user key formatting if it contains timestamp.) ``` [dump db specified with --db] ``` >> ./ldb --db=/tmp/rocksdbtest-501/column_family_test_75359_17910784957761284041 dump >> foo|timestamp:1 ==> v1 Keys in range: 1 ``` idump ``` ./ldb --db=$TEST_DB idump 'foo|timestamp:1' seq:1, type:1 => v1 Internal keys in range: 1 ``` Reviewed By: ltamasi Differential Revision: D57755382 Pulled By: jowlyzhang fbshipit-source-id: a0a2ef80c92801cbf7bfccc64769c1191824362e
This commit is contained in:
parent
f044b6a6ad
commit
9a72cf1a61
|
@ -160,6 +160,7 @@ class LDBCommand {
|
||||||
DB* db_;
|
DB* db_;
|
||||||
DBWithTTL* db_ttl_;
|
DBWithTTL* db_ttl_;
|
||||||
std::map<std::string, ColumnFamilyHandle*> cf_handles_;
|
std::map<std::string, ColumnFamilyHandle*> cf_handles_;
|
||||||
|
std::map<uint32_t, const Comparator*> ucmps_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* true implies that this command can work if the db is opened in read-only
|
* true implies that this command can work if the db is opened in read-only
|
||||||
|
@ -224,17 +225,19 @@ class LDBCommand {
|
||||||
ColumnFamilyHandle* GetCfHandle();
|
ColumnFamilyHandle* GetCfHandle();
|
||||||
|
|
||||||
static std::string PrintKeyValue(const std::string& key,
|
static std::string PrintKeyValue(const std::string& key,
|
||||||
|
const std::string& timestamp,
|
||||||
const std::string& value, bool is_key_hex,
|
const std::string& value, bool is_key_hex,
|
||||||
bool is_value_hex);
|
bool is_value_hex, const Comparator* ucmp);
|
||||||
|
|
||||||
static std::string PrintKeyValue(const std::string& key,
|
static std::string PrintKeyValue(const std::string& key,
|
||||||
const std::string& value, bool is_hex);
|
const std::string& timestamp,
|
||||||
|
const std::string& value, bool is_hex,
|
||||||
|
const Comparator* ucmp);
|
||||||
|
|
||||||
static std::string PrintKeyValueOrWideColumns(const Slice& key,
|
static std::string PrintKeyValueOrWideColumns(
|
||||||
const Slice& value,
|
const Slice& key, const Slice& timestamp, const Slice& value,
|
||||||
const WideColumns& wide_columns,
|
const WideColumns& wide_columns, bool is_key_hex, bool is_value_hex,
|
||||||
bool is_key_hex,
|
const Comparator* ucmp);
|
||||||
bool is_value_hex);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if the specified flag is present in the specified flags vector
|
* Return true if the specified flag is present in the specified flags vector
|
||||||
|
|
233
tools/ldb_cmd.cc
233
tools/ldb_cmd.cc
|
@ -45,6 +45,7 @@
|
||||||
#include "util/file_checksum_helper.h"
|
#include "util/file_checksum_helper.h"
|
||||||
#include "util/stderr_logger.h"
|
#include "util/stderr_logger.h"
|
||||||
#include "util/string_util.h"
|
#include "util/string_util.h"
|
||||||
|
#include "util/write_batch_util.h"
|
||||||
#include "utilities/blob_db/blob_dump_tool.h"
|
#include "utilities/blob_db/blob_dump_tool.h"
|
||||||
#include "utilities/merge_operators.h"
|
#include "utilities/merge_operators.h"
|
||||||
#include "utilities/ttl/db_ttl_impl.h"
|
#include "utilities/ttl/db_ttl_impl.h"
|
||||||
|
@ -115,6 +116,7 @@ namespace {
|
||||||
|
|
||||||
void DumpWalFile(Options options, std::string wal_file, bool print_header,
|
void DumpWalFile(Options options, std::string wal_file, bool print_header,
|
||||||
bool print_values, bool is_write_committed,
|
bool print_values, bool is_write_committed,
|
||||||
|
const std::map<uint32_t, const Comparator*>& ucmps,
|
||||||
LDBCommandExecuteResult* exec_state);
|
LDBCommandExecuteResult* exec_state);
|
||||||
|
|
||||||
void DumpSstFile(Options options, std::string filename, bool output_hex,
|
void DumpSstFile(Options options, std::string filename, bool output_hex,
|
||||||
|
@ -503,6 +505,7 @@ void LDBCommand::OpenDB() {
|
||||||
bool found_cf_name = false;
|
bool found_cf_name = false;
|
||||||
for (size_t i = 0; i < handles_opened.size(); i++) {
|
for (size_t i = 0; i < handles_opened.size(); i++) {
|
||||||
cf_handles_[column_families_[i].name] = handles_opened[i];
|
cf_handles_[column_families_[i].name] = handles_opened[i];
|
||||||
|
ucmps_[handles_opened[i]->GetID()] = handles_opened[i]->GetComparator();
|
||||||
if (column_family_name_ == column_families_[i].name) {
|
if (column_family_name_ == column_families_[i].name) {
|
||||||
found_cf_name = true;
|
found_cf_name = true;
|
||||||
}
|
}
|
||||||
|
@ -512,6 +515,8 @@ void LDBCommand::OpenDB() {
|
||||||
"Non-existing column family " + column_family_name_);
|
"Non-existing column family " + column_family_name_);
|
||||||
CloseDB();
|
CloseDB();
|
||||||
}
|
}
|
||||||
|
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
|
||||||
|
ucmps_[default_cf->GetID()] = default_cf->GetComparator();
|
||||||
} else {
|
} else {
|
||||||
// We successfully opened DB in single column family mode.
|
// We successfully opened DB in single column family mode.
|
||||||
assert(column_families_.empty());
|
assert(column_families_.empty());
|
||||||
|
@ -520,6 +525,8 @@ void LDBCommand::OpenDB() {
|
||||||
"Non-existing column family " + column_family_name_);
|
"Non-existing column family " + column_family_name_);
|
||||||
CloseDB();
|
CloseDB();
|
||||||
}
|
}
|
||||||
|
ColumnFamilyHandle* default_cf = db_->DefaultColumnFamily();
|
||||||
|
ucmps_[default_cf->GetID()] = default_cf->GetComparator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1146,27 +1153,36 @@ std::string LDBCommand::StringToHex(const std::string& str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string LDBCommand::PrintKeyValue(const std::string& key,
|
std::string LDBCommand::PrintKeyValue(const std::string& key,
|
||||||
|
const std::string& timestamp,
|
||||||
const std::string& value, bool is_key_hex,
|
const std::string& value, bool is_key_hex,
|
||||||
bool is_value_hex) {
|
bool is_value_hex,
|
||||||
|
const Comparator* ucmp) {
|
||||||
std::string result;
|
std::string result;
|
||||||
result.append(is_key_hex ? StringToHex(key) : key);
|
result.append(is_key_hex ? StringToHex(key) : key);
|
||||||
|
if (!timestamp.empty()) {
|
||||||
|
result.append("|timestamp:");
|
||||||
|
result.append(ucmp->TimestampToString(timestamp));
|
||||||
|
}
|
||||||
result.append(DELIM);
|
result.append(DELIM);
|
||||||
result.append(is_value_hex ? StringToHex(value) : value);
|
result.append(is_value_hex ? StringToHex(value) : value);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string LDBCommand::PrintKeyValue(const std::string& key,
|
std::string LDBCommand::PrintKeyValue(const std::string& key,
|
||||||
const std::string& value, bool is_hex) {
|
const std::string& timestamp,
|
||||||
return PrintKeyValue(key, value, is_hex, is_hex);
|
const std::string& value, bool is_hex,
|
||||||
|
const Comparator* ucmp) {
|
||||||
|
return PrintKeyValue(key, timestamp, value, is_hex, is_hex, ucmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string LDBCommand::PrintKeyValueOrWideColumns(
|
std::string LDBCommand::PrintKeyValueOrWideColumns(
|
||||||
const Slice& key, const Slice& value, const WideColumns& wide_columns,
|
const Slice& key, const Slice& timestamp, const Slice& value,
|
||||||
bool is_key_hex, bool is_value_hex) {
|
const WideColumns& wide_columns, bool is_key_hex, bool is_value_hex,
|
||||||
|
const Comparator* ucmp) {
|
||||||
if (wide_columns.empty() ||
|
if (wide_columns.empty() ||
|
||||||
WideColumnsHelper::HasDefaultColumnOnly(wide_columns)) {
|
WideColumnsHelper::HasDefaultColumnOnly(wide_columns)) {
|
||||||
return PrintKeyValue(key.ToString(), value.ToString(), is_key_hex,
|
return PrintKeyValue(key.ToString(), timestamp.ToString(), value.ToString(),
|
||||||
is_value_hex);
|
is_key_hex, is_value_hex, ucmp);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
// Sample plaintext output (first column is kDefaultWideColumnName)
|
// Sample plaintext output (first column is kDefaultWideColumnName)
|
||||||
|
@ -1177,9 +1193,10 @@ std::string LDBCommand::PrintKeyValueOrWideColumns(
|
||||||
*/
|
*/
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
WideColumnsHelper::DumpWideColumns(wide_columns, oss, is_value_hex);
|
WideColumnsHelper::DumpWideColumns(wide_columns, oss, is_value_hex);
|
||||||
return PrintKeyValue(key.ToString(), oss.str().c_str(), is_key_hex,
|
return PrintKeyValue(key.ToString(), timestamp.ToString(), oss.str().c_str(),
|
||||||
false); // is_value_hex_ is already honored in oss.
|
is_key_hex, false,
|
||||||
// avoid double-hexing it.
|
ucmp); // is_value_hex_ is already honored in oss.
|
||||||
|
// avoid double-hexing it.
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string LDBCommand::HelpRangeCmdArgs() {
|
std::string LDBCommand::HelpRangeCmdArgs() {
|
||||||
|
@ -1929,10 +1946,12 @@ void InternalDumpCommand::DoCommand() {
|
||||||
assert(GetExecuteState().IsFailed());
|
assert(GetExecuteState().IsFailed());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
ColumnFamilyHandle* cfh = GetCfHandle();
|
||||||
|
const Comparator* ucmp = cfh->GetComparator();
|
||||||
|
size_t ts_sz = ucmp->timestamp_size();
|
||||||
if (print_stats_) {
|
if (print_stats_) {
|
||||||
std::string stats;
|
std::string stats;
|
||||||
if (db_->GetProperty(GetCfHandle(), "rocksdb.stats", &stats)) {
|
if (db_->GetProperty(cfh, "rocksdb.stats", &stats)) {
|
||||||
fprintf(stdout, "%s\n", stats.c_str());
|
fprintf(stdout, "%s\n", stats.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1954,7 +1973,11 @@ void InternalDumpCommand::DoCommand() {
|
||||||
for (auto& key_version : key_versions) {
|
for (auto& key_version : key_versions) {
|
||||||
ValueType value_type = static_cast<ValueType>(key_version.type);
|
ValueType value_type = static_cast<ValueType>(key_version.type);
|
||||||
InternalKey ikey(key_version.user_key, key_version.sequence, value_type);
|
InternalKey ikey(key_version.user_key, key_version.sequence, value_type);
|
||||||
if (has_to_ && ikey.user_key() == to_) {
|
Slice user_key_without_ts = ikey.user_key();
|
||||||
|
if (ts_sz > 0) {
|
||||||
|
user_key_without_ts.remove_suffix(ts_sz);
|
||||||
|
}
|
||||||
|
if (has_to_ && ucmp->Compare(user_key_without_ts, to_) == 0) {
|
||||||
// GetAllKeyVersions() includes keys with user key `to_`, but idump has
|
// GetAllKeyVersions() includes keys with user key `to_`, but idump has
|
||||||
// traditionally excluded such keys.
|
// traditionally excluded such keys.
|
||||||
break;
|
break;
|
||||||
|
@ -1990,7 +2013,7 @@ void InternalDumpCommand::DoCommand() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!count_only_ && !count_delim_) {
|
if (!count_only_ && !count_delim_) {
|
||||||
std::string key = ikey.DebugString(is_key_hex_);
|
std::string key = ikey.DebugString(is_key_hex_, ucmp);
|
||||||
Slice value(key_version.value);
|
Slice value(key_version.value);
|
||||||
if (!decode_blob_index_ || value_type != kTypeBlobIndex) {
|
if (!decode_blob_index_ || value_type != kTypeBlobIndex) {
|
||||||
if (value_type == kTypeWideColumnEntity) {
|
if (value_type == kTypeWideColumnEntity) {
|
||||||
|
@ -2166,7 +2189,7 @@ void DBDumperCommand::DoCommand() {
|
||||||
// TODO(myabandeh): allow configuring is_write_commited
|
// TODO(myabandeh): allow configuring is_write_commited
|
||||||
DumpWalFile(options_, path_, /* print_header_ */ true,
|
DumpWalFile(options_, path_, /* print_header_ */ true,
|
||||||
/* print_values_ */ true, true /* is_write_commited */,
|
/* print_values_ */ true, true /* is_write_commited */,
|
||||||
&exec_state_);
|
ucmps_, &exec_state_);
|
||||||
break;
|
break;
|
||||||
case kTableFile:
|
case kTableFile:
|
||||||
DumpSstFile(options_, path_, is_key_hex_, /* show_properties */ true,
|
DumpSstFile(options_, path_, is_key_hex_, /* show_properties */ true,
|
||||||
|
@ -2206,8 +2229,16 @@ void DBDumperCommand::DoDumpCommand() {
|
||||||
|
|
||||||
// Setup key iterator
|
// Setup key iterator
|
||||||
ReadOptions scan_read_opts;
|
ReadOptions scan_read_opts;
|
||||||
|
Slice read_timestamp;
|
||||||
|
ColumnFamilyHandle* cfh = GetCfHandle();
|
||||||
|
const Comparator* ucmp = cfh->GetComparator();
|
||||||
|
size_t ts_sz = ucmp->timestamp_size();
|
||||||
|
if (ucmp->timestamp_size() > 0) {
|
||||||
|
read_timestamp = ucmp->GetMaxTimestamp();
|
||||||
|
scan_read_opts.timestamp = &read_timestamp;
|
||||||
|
}
|
||||||
scan_read_opts.total_order_seek = true;
|
scan_read_opts.total_order_seek = true;
|
||||||
Iterator* iter = db_->NewIterator(scan_read_opts, GetCfHandle());
|
Iterator* iter = db_->NewIterator(scan_read_opts, cfh);
|
||||||
Status st = iter->status();
|
Status st = iter->status();
|
||||||
if (!st.ok()) {
|
if (!st.ok()) {
|
||||||
exec_state_ =
|
exec_state_ =
|
||||||
|
@ -2262,7 +2293,7 @@ void DBDumperCommand::DoDumpCommand() {
|
||||||
for (; iter->Valid(); iter->Next()) {
|
for (; iter->Valid(); iter->Next()) {
|
||||||
int rawtime = 0;
|
int rawtime = 0;
|
||||||
// If end marker was specified, we stop before it
|
// If end marker was specified, we stop before it
|
||||||
if (!null_to_ && (iter->key().ToString() >= to_)) {
|
if (!null_to_ && ucmp->Compare(iter->key(), to_) >= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Terminate if maximum number of keys have been dumped
|
// Terminate if maximum number of keys have been dumped
|
||||||
|
@ -2316,11 +2347,14 @@ void DBDumperCommand::DoDumpCommand() {
|
||||||
// (TODO) TTL Iterator does not support wide columns yet.
|
// (TODO) TTL Iterator does not support wide columns yet.
|
||||||
std::string str =
|
std::string str =
|
||||||
is_db_ttl_
|
is_db_ttl_
|
||||||
? PrintKeyValue(iter->key().ToString(), iter->value().ToString(),
|
? PrintKeyValue(iter->key().ToString(),
|
||||||
is_key_hex_, is_value_hex_)
|
ts_sz == 0 ? "" : iter->timestamp().ToString(),
|
||||||
: PrintKeyValueOrWideColumns(iter->key(), iter->value(),
|
iter->value().ToString(), is_key_hex_,
|
||||||
iter->columns(), is_key_hex_,
|
is_value_hex_, ucmp)
|
||||||
is_value_hex_);
|
: PrintKeyValueOrWideColumns(
|
||||||
|
iter->key(), ts_sz == 0 ? "" : iter->timestamp().ToString(),
|
||||||
|
iter->value(), iter->columns(), is_key_hex_, is_value_hex_,
|
||||||
|
ucmp);
|
||||||
fprintf(stdout, "%s\n", str.c_str());
|
fprintf(stdout, "%s\n", str.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2641,14 +2675,16 @@ struct StdErrReporter : public log::Reader::Reporter {
|
||||||
class InMemoryHandler : public WriteBatch::Handler {
|
class InMemoryHandler : public WriteBatch::Handler {
|
||||||
public:
|
public:
|
||||||
InMemoryHandler(std::stringstream& row, bool print_values,
|
InMemoryHandler(std::stringstream& row, bool print_values,
|
||||||
bool write_after_commit = false)
|
bool write_after_commit,
|
||||||
|
const std::map<uint32_t, const Comparator*>& ucmps)
|
||||||
: Handler(),
|
: Handler(),
|
||||||
row_(row),
|
row_(row),
|
||||||
print_values_(print_values),
|
print_values_(print_values),
|
||||||
write_after_commit_(write_after_commit) {}
|
write_after_commit_(write_after_commit),
|
||||||
|
ucmps_(ucmps) {}
|
||||||
|
|
||||||
void commonPutMerge(const Slice& key, const Slice& value) {
|
void commonPutMerge(uint32_t cf, const Slice& key, const Slice& value) {
|
||||||
std::string k = LDBCommand::StringToHex(key.ToString());
|
std::string k = PrintKey(cf, key);
|
||||||
if (print_values_) {
|
if (print_values_) {
|
||||||
std::string v = LDBCommand::StringToHex(value.ToString());
|
std::string v = LDBCommand::StringToHex(value.ToString());
|
||||||
row_ << k << " : ";
|
row_ << k << " : ";
|
||||||
|
@ -2660,15 +2696,13 @@ class InMemoryHandler : public WriteBatch::Handler {
|
||||||
|
|
||||||
Status PutCF(uint32_t cf, const Slice& key, const Slice& value) override {
|
Status PutCF(uint32_t cf, const Slice& key, const Slice& value) override {
|
||||||
row_ << "PUT(" << cf << ") : ";
|
row_ << "PUT(" << cf << ") : ";
|
||||||
commonPutMerge(key, value);
|
commonPutMerge(cf, key, value);
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status PutEntityCF(uint32_t cf, const Slice& key,
|
Status PutEntityCF(uint32_t cf, const Slice& key,
|
||||||
const Slice& value) override {
|
const Slice& value) override {
|
||||||
row_ << "PUT_ENTITY(" << cf
|
row_ << "PUT_ENTITY(" << cf << ") : " << PrintKey(cf, key);
|
||||||
<< ") : " << LDBCommand::StringToHex(key.ToString());
|
|
||||||
|
|
||||||
if (print_values_) {
|
if (print_values_) {
|
||||||
row_ << " : ";
|
row_ << " : ";
|
||||||
const Status s =
|
const Status s =
|
||||||
|
@ -2684,7 +2718,7 @@ class InMemoryHandler : public WriteBatch::Handler {
|
||||||
|
|
||||||
Status MergeCF(uint32_t cf, const Slice& key, const Slice& value) override {
|
Status MergeCF(uint32_t cf, const Slice& key, const Slice& value) override {
|
||||||
row_ << "MERGE(" << cf << ") : ";
|
row_ << "MERGE(" << cf << ") : ";
|
||||||
commonPutMerge(key, value);
|
commonPutMerge(cf, key, value);
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2695,21 +2729,21 @@ class InMemoryHandler : public WriteBatch::Handler {
|
||||||
|
|
||||||
Status DeleteCF(uint32_t cf, const Slice& key) override {
|
Status DeleteCF(uint32_t cf, const Slice& key) override {
|
||||||
row_ << "DELETE(" << cf << ") : ";
|
row_ << "DELETE(" << cf << ") : ";
|
||||||
row_ << LDBCommand::StringToHex(key.ToString()) << " ";
|
row_ << PrintKey(cf, key) << " ";
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status SingleDeleteCF(uint32_t cf, const Slice& key) override {
|
Status SingleDeleteCF(uint32_t cf, const Slice& key) override {
|
||||||
row_ << "SINGLE_DELETE(" << cf << ") : ";
|
row_ << "SINGLE_DELETE(" << cf << ") : ";
|
||||||
row_ << LDBCommand::StringToHex(key.ToString()) << " ";
|
row_ << PrintKey(cf, key) << " ";
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status DeleteRangeCF(uint32_t cf, const Slice& begin_key,
|
Status DeleteRangeCF(uint32_t cf, const Slice& begin_key,
|
||||||
const Slice& end_key) override {
|
const Slice& end_key) override {
|
||||||
row_ << "DELETE_RANGE(" << cf << ") : ";
|
row_ << "DELETE_RANGE(" << cf << ") : ";
|
||||||
row_ << LDBCommand::StringToHex(begin_key.ToString()) << " ";
|
row_ << PrintKey(cf, begin_key) << " ";
|
||||||
row_ << LDBCommand::StringToHex(end_key.ToString()) << " ";
|
row_ << PrintKey(cf, end_key) << " ";
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2754,13 +2788,37 @@ class InMemoryHandler : public WriteBatch::Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string PrintKey(uint32_t cf, const Slice& key) {
|
||||||
|
auto ucmp_iter = ucmps_.find(cf);
|
||||||
|
if (ucmp_iter == ucmps_.end()) {
|
||||||
|
// Fallback to default print slice as hex
|
||||||
|
return LDBCommand::StringToHex(key.ToString());
|
||||||
|
}
|
||||||
|
size_t ts_sz = ucmp_iter->second->timestamp_size();
|
||||||
|
if (ts_sz == 0) {
|
||||||
|
return LDBCommand::StringToHex(key.ToString());
|
||||||
|
} else {
|
||||||
|
// This could happen if there is corruption or undetected comparator
|
||||||
|
// change.
|
||||||
|
if (key.size() < ts_sz) {
|
||||||
|
return "CORRUPT KEY";
|
||||||
|
}
|
||||||
|
Slice user_key_without_ts = key;
|
||||||
|
user_key_without_ts.remove_suffix(ts_sz);
|
||||||
|
Slice ts = Slice(key.data() + key.size() - ts_sz, ts_sz);
|
||||||
|
return LDBCommand::StringToHex(user_key_without_ts.ToString()) +
|
||||||
|
"|timestamp:" + ucmp_iter->second->TimestampToString(ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
std::stringstream& row_;
|
std::stringstream& row_;
|
||||||
bool print_values_;
|
bool print_values_;
|
||||||
bool write_after_commit_;
|
bool write_after_commit_;
|
||||||
|
const std::map<uint32_t, const Comparator*> ucmps_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void DumpWalFile(Options options, std::string wal_file, bool print_header,
|
void DumpWalFile(Options options, std::string wal_file, bool print_header,
|
||||||
bool print_values, bool is_write_committed,
|
bool print_values, bool is_write_committed,
|
||||||
|
const std::map<uint32_t, const Comparator*>& ucmps,
|
||||||
LDBCommandExecuteResult* exec_state) {
|
LDBCommandExecuteResult* exec_state) {
|
||||||
const auto& fs = options.env->GetFileSystem();
|
const auto& fs = options.env->GetFileSystem();
|
||||||
FileOptions soptions(options);
|
FileOptions soptions(options);
|
||||||
|
@ -2781,6 +2839,12 @@ void DumpWalFile(Options options, std::string wal_file, bool print_header,
|
||||||
uint64_t log_number;
|
uint64_t log_number;
|
||||||
FileType type;
|
FileType type;
|
||||||
|
|
||||||
|
// Comparators are available and will be used for formatting user key if DB
|
||||||
|
// is opened for this dump wal operation.
|
||||||
|
UnorderedMap<uint32_t, size_t> running_ts_sz;
|
||||||
|
for (const auto& [cf_id, ucmp] : ucmps) {
|
||||||
|
running_ts_sz.emplace(cf_id, ucmp->timestamp_size());
|
||||||
|
}
|
||||||
// we need the log number, but ParseFilename expects dbname/NNN.log.
|
// we need the log number, but ParseFilename expects dbname/NNN.log.
|
||||||
std::string sanitized = wal_file;
|
std::string sanitized = wal_file;
|
||||||
size_t lastslash = sanitized.rfind('/');
|
size_t lastslash = sanitized.rfind('/');
|
||||||
|
@ -2793,6 +2857,7 @@ void DumpWalFile(Options options, std::string wal_file, bool print_header,
|
||||||
}
|
}
|
||||||
log::Reader reader(options.info_log, std::move(wal_file_reader), &reporter,
|
log::Reader reader(options.info_log, std::move(wal_file_reader), &reporter,
|
||||||
true /* checksum */, log_number);
|
true /* checksum */, log_number);
|
||||||
|
std::unordered_set<uint32_t> encountered_cf_ids;
|
||||||
std::string scratch;
|
std::string scratch;
|
||||||
WriteBatch batch;
|
WriteBatch batch;
|
||||||
Slice record;
|
Slice record;
|
||||||
|
@ -2821,11 +2886,51 @@ void DumpWalFile(Options options, std::string wal_file, bool print_header,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
const UnorderedMap<uint32_t, size_t> recorded_ts_sz =
|
||||||
|
reader.GetRecordedTimestampSize();
|
||||||
|
if (!running_ts_sz.empty()) {
|
||||||
|
status = HandleWriteBatchTimestampSizeDifference(
|
||||||
|
&batch, running_ts_sz, recorded_ts_sz,
|
||||||
|
TimestampSizeConsistencyMode::kVerifyConsistency,
|
||||||
|
/*new_batch=*/nullptr);
|
||||||
|
if (!status.ok()) {
|
||||||
|
std::stringstream oss;
|
||||||
|
oss << "Format for user keys in WAL file is inconsistent with the "
|
||||||
|
"comparator used to open the DB. Timestamp size recorded in "
|
||||||
|
"WAL vs specified by "
|
||||||
|
"comparator: {";
|
||||||
|
bool first_cf = true;
|
||||||
|
for (const auto& [cf_id, ts_sz] : running_ts_sz) {
|
||||||
|
if (first_cf) {
|
||||||
|
first_cf = false;
|
||||||
|
} else {
|
||||||
|
oss << ", ";
|
||||||
|
}
|
||||||
|
auto record_ts_iter = recorded_ts_sz.find(cf_id);
|
||||||
|
size_t ts_sz_in_wal = (record_ts_iter == recorded_ts_sz.end())
|
||||||
|
? 0
|
||||||
|
: record_ts_iter->second;
|
||||||
|
oss << "(cf_id: " << cf_id << ", [recorded: " << ts_sz_in_wal
|
||||||
|
<< ", comparator: " << ts_sz << "])";
|
||||||
|
}
|
||||||
|
oss << "}";
|
||||||
|
if (exec_state) {
|
||||||
|
*exec_state = LDBCommandExecuteResult::Failed(oss.str());
|
||||||
|
} else {
|
||||||
|
std::cerr << oss.str() << std::endl;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
row << WriteBatchInternal::Sequence(&batch) << ",";
|
row << WriteBatchInternal::Sequence(&batch) << ",";
|
||||||
row << WriteBatchInternal::Count(&batch) << ",";
|
row << WriteBatchInternal::Count(&batch) << ",";
|
||||||
row << WriteBatchInternal::ByteSize(&batch) << ",";
|
row << WriteBatchInternal::ByteSize(&batch) << ",";
|
||||||
row << reader.LastRecordOffset() << ",";
|
row << reader.LastRecordOffset() << ",";
|
||||||
InMemoryHandler handler(row, print_values, is_write_committed);
|
ColumnFamilyCollector cf_collector;
|
||||||
|
status = batch.Iterate(&cf_collector);
|
||||||
|
auto cf_ids = cf_collector.column_families();
|
||||||
|
encountered_cf_ids.insert(cf_ids.begin(), cf_ids.end());
|
||||||
|
InMemoryHandler handler(row, print_values, is_write_committed, ucmps);
|
||||||
status = batch.Iterate(&handler);
|
status = batch.Iterate(&handler);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
if (exec_state) {
|
if (exec_state) {
|
||||||
|
@ -2840,6 +2945,29 @@ void DumpWalFile(Options options, std::string wal_file, bool print_header,
|
||||||
}
|
}
|
||||||
std::cout << row.str();
|
std::cout << row.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::stringstream cf_ids_oss;
|
||||||
|
bool empty_cfs = true;
|
||||||
|
for (uint32_t cf_id : encountered_cf_ids) {
|
||||||
|
if (ucmps.find(cf_id) == ucmps.end()) {
|
||||||
|
if (empty_cfs) {
|
||||||
|
cf_ids_oss << "[";
|
||||||
|
empty_cfs = false;
|
||||||
|
} else {
|
||||||
|
cf_ids_oss << ",";
|
||||||
|
}
|
||||||
|
cf_ids_oss << cf_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty_cfs) {
|
||||||
|
cf_ids_oss << "]";
|
||||||
|
std::cout
|
||||||
|
<< "(Column family id: " << cf_ids_oss.str()
|
||||||
|
<< " contained in WAL are not opened in DB. Applied default "
|
||||||
|
"hex formatting for user key. Specify --db=<db_path> to "
|
||||||
|
"open DB for better user key formatting if it contains timestamp.)"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2855,7 +2983,7 @@ WALDumperCommand::WALDumperCommand(
|
||||||
const std::map<std::string, std::string>& options,
|
const std::map<std::string, std::string>& options,
|
||||||
const std::vector<std::string>& flags)
|
const std::vector<std::string>& flags)
|
||||||
: LDBCommand(options, flags, true,
|
: LDBCommand(options, flags, true,
|
||||||
BuildCmdLineOptions({ARG_WAL_FILE, ARG_WRITE_COMMITTED,
|
BuildCmdLineOptions({ARG_WAL_FILE, ARG_DB, ARG_WRITE_COMMITTED,
|
||||||
ARG_PRINT_HEADER, ARG_PRINT_VALUE})),
|
ARG_PRINT_HEADER, ARG_PRINT_VALUE})),
|
||||||
print_header_(false),
|
print_header_(false),
|
||||||
print_values_(false),
|
print_values_(false),
|
||||||
|
@ -2875,12 +3003,17 @@ WALDumperCommand::WALDumperCommand(
|
||||||
exec_state_ = LDBCommandExecuteResult::Failed("Argument " + ARG_WAL_FILE +
|
exec_state_ = LDBCommandExecuteResult::Failed("Argument " + ARG_WAL_FILE +
|
||||||
" must be specified.");
|
" must be specified.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!db_path_.empty()) {
|
||||||
|
no_db_open_ = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WALDumperCommand::Help(std::string& ret) {
|
void WALDumperCommand::Help(std::string& ret) {
|
||||||
ret.append(" ");
|
ret.append(" ");
|
||||||
ret.append(WALDumperCommand::Name());
|
ret.append(WALDumperCommand::Name());
|
||||||
ret.append(" --" + ARG_WAL_FILE + "=<write_ahead_log_file_path>");
|
ret.append(" --" + ARG_WAL_FILE + "=<write_ahead_log_file_path>");
|
||||||
|
ret.append(" [--" + ARG_DB + "=<db_path>]");
|
||||||
ret.append(" [--" + ARG_PRINT_HEADER + "] ");
|
ret.append(" [--" + ARG_PRINT_HEADER + "] ");
|
||||||
ret.append(" [--" + ARG_PRINT_VALUE + "] ");
|
ret.append(" [--" + ARG_PRINT_VALUE + "] ");
|
||||||
ret.append(" [--" + ARG_WRITE_COMMITTED + "=true|false] ");
|
ret.append(" [--" + ARG_WRITE_COMMITTED + "=true|false] ");
|
||||||
|
@ -2890,7 +3023,7 @@ void WALDumperCommand::Help(std::string& ret) {
|
||||||
void WALDumperCommand::DoCommand() {
|
void WALDumperCommand::DoCommand() {
|
||||||
PrepareOptions();
|
PrepareOptions();
|
||||||
DumpWalFile(options_, wal_file_, print_header_, print_values_,
|
DumpWalFile(options_, wal_file_, print_header_, print_values_,
|
||||||
is_write_committed_, &exec_state_);
|
is_write_committed_, ucmps_, &exec_state_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -3370,6 +3503,8 @@ void ScanCommand::DoCommand() {
|
||||||
int num_keys_scanned = 0;
|
int num_keys_scanned = 0;
|
||||||
ReadOptions scan_read_opts;
|
ReadOptions scan_read_opts;
|
||||||
ColumnFamilyHandle* cfh = GetCfHandle();
|
ColumnFamilyHandle* cfh = GetCfHandle();
|
||||||
|
const Comparator* ucmp = cfh->GetComparator();
|
||||||
|
size_t ts_sz = ucmp->timestamp_size();
|
||||||
Slice read_timestamp;
|
Slice read_timestamp;
|
||||||
Status st = MaybePopulateReadTimestamp(cfh, scan_read_opts, &read_timestamp);
|
Status st = MaybePopulateReadTimestamp(cfh, scan_read_opts, &read_timestamp);
|
||||||
if (!st.ok()) {
|
if (!st.ok()) {
|
||||||
|
@ -3426,12 +3561,15 @@ void ScanCommand::DoCommand() {
|
||||||
}
|
}
|
||||||
fprintf(stdout, "%s\n", key_str.c_str());
|
fprintf(stdout, "%s\n", key_str.c_str());
|
||||||
} else {
|
} else {
|
||||||
std::string str = is_db_ttl_ ? PrintKeyValue(it->key().ToString(),
|
std::string str =
|
||||||
it->value().ToString(),
|
is_db_ttl_
|
||||||
is_key_hex_, is_value_hex_)
|
? PrintKeyValue(it->key().ToString(),
|
||||||
: PrintKeyValueOrWideColumns(
|
ts_sz == 0 ? "" : it->timestamp().ToString(),
|
||||||
it->key(), it->value(), it->columns(),
|
it->value().ToString(), is_key_hex_,
|
||||||
is_key_hex_, is_value_hex_);
|
is_value_hex_, ucmp)
|
||||||
|
: PrintKeyValueOrWideColumns(
|
||||||
|
it->key(), ts_sz == 0 ? "" : it->timestamp(), it->value(),
|
||||||
|
it->columns(), is_key_hex_, is_value_hex_, ucmp);
|
||||||
fprintf(stdout, "%s\n", str.c_str());
|
fprintf(stdout, "%s\n", str.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3769,8 +3907,11 @@ void DBQuerierCommand::DoCommand() {
|
||||||
key = (is_key_hex_ ? HexToString(tokens[1]) : tokens[1]);
|
key = (is_key_hex_ ? HexToString(tokens[1]) : tokens[1]);
|
||||||
s = db_->Get(read_options, GetCfHandle(), Slice(key), &value);
|
s = db_->Get(read_options, GetCfHandle(), Slice(key), &value);
|
||||||
if (s.ok()) {
|
if (s.ok()) {
|
||||||
|
// TODO: add read timestamp support in querier
|
||||||
fprintf(stdout, "%s\n",
|
fprintf(stdout, "%s\n",
|
||||||
PrintKeyValue(key, value, is_key_hex_, is_value_hex_).c_str());
|
PrintKeyValue(key, "", value, is_key_hex_, is_value_hex_,
|
||||||
|
GetCfHandle()->GetComparator())
|
||||||
|
.c_str());
|
||||||
} else {
|
} else {
|
||||||
if (s.IsNotFound()) {
|
if (s.IsNotFound()) {
|
||||||
fprintf(stdout, "Not found %s\n", tokens[1].c_str());
|
fprintf(stdout, "Not found %s\n", tokens[1].c_str());
|
||||||
|
@ -4230,7 +4371,7 @@ void DBFileDumperCommand::DoCommand() {
|
||||||
std::cout << filename << std::endl;
|
std::cout << filename << std::endl;
|
||||||
// TODO(myabandeh): allow configuring is_write_commited
|
// TODO(myabandeh): allow configuring is_write_commited
|
||||||
DumpWalFile(options_, filename, true, true, true /* is_write_commited */,
|
DumpWalFile(options_, filename, true, true, true /* is_write_commited */,
|
||||||
&exec_state_);
|
ucmps_, &exec_state_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,7 +369,7 @@ class WALDumperCommand : public LDBCommand {
|
||||||
const std::map<std::string, std::string>& options,
|
const std::map<std::string, std::string>& options,
|
||||||
const std::vector<std::string>& flags);
|
const std::vector<std::string>& flags);
|
||||||
|
|
||||||
bool NoDBOpen() override { return true; }
|
bool NoDBOpen() override { return no_db_open_; }
|
||||||
|
|
||||||
static void Help(std::string& ret);
|
static void Help(std::string& ret);
|
||||||
|
|
||||||
|
@ -380,6 +380,7 @@ class WALDumperCommand : public LDBCommand {
|
||||||
std::string wal_file_;
|
std::string wal_file_;
|
||||||
bool print_values_;
|
bool print_values_;
|
||||||
bool is_write_committed_; // default will be set to true
|
bool is_write_committed_; // default will be set to true
|
||||||
|
bool no_db_open_ = true;
|
||||||
|
|
||||||
static const std::string ARG_WAL_FILE;
|
static const std::string ARG_WAL_FILE;
|
||||||
static const std::string ARG_WRITE_COMMITTED;
|
static const std::string ARG_WRITE_COMMITTED;
|
||||||
|
|
|
@ -158,6 +158,39 @@ Status TimestampRecoveryHandler::PutCF(uint32_t cf, const Slice& key,
|
||||||
return WriteBatchInternal::Put(new_batch_.get(), cf, new_key, value);
|
return WriteBatchInternal::Put(new_batch_.get(), cf, new_key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status TimestampRecoveryHandler::PutEntityCF(uint32_t cf, const Slice& key,
|
||||||
|
const Slice& entity) {
|
||||||
|
std::string new_key_buf;
|
||||||
|
Slice new_key;
|
||||||
|
Status status = TimestampRecoveryHandler::ReconcileTimestampDiscrepancy(
|
||||||
|
cf, key, &new_key_buf, &new_key);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
Slice entity_copy = entity;
|
||||||
|
WideColumns columns;
|
||||||
|
if (!WideColumnSerialization::Deserialize(entity_copy, columns).ok()) {
|
||||||
|
return Status::Corruption("Unable to deserialize entity",
|
||||||
|
entity.ToString(/* hex */ true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteBatchInternal::PutEntity(new_batch_.get(), cf, new_key, columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status TimestampRecoveryHandler::TimedPutCF(uint32_t cf, const Slice& key,
|
||||||
|
const Slice& value,
|
||||||
|
uint64_t write_time) {
|
||||||
|
std::string new_key_buf;
|
||||||
|
Slice new_key;
|
||||||
|
Status status =
|
||||||
|
ReconcileTimestampDiscrepancy(cf, key, &new_key_buf, &new_key);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
return WriteBatchInternal::TimedPut(new_batch_.get(), cf, new_key, value,
|
||||||
|
write_time);
|
||||||
|
}
|
||||||
|
|
||||||
Status TimestampRecoveryHandler::DeleteCF(uint32_t cf, const Slice& key) {
|
Status TimestampRecoveryHandler::DeleteCF(uint32_t cf, const Slice& key) {
|
||||||
std::string new_key_buf;
|
std::string new_key_buf;
|
||||||
Slice new_key;
|
Slice new_key;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "db/wide/wide_column_serialization.h"
|
||||||
#include "db/write_batch_internal.h"
|
#include "db/write_batch_internal.h"
|
||||||
#include "rocksdb/slice.h"
|
#include "rocksdb/slice.h"
|
||||||
#include "rocksdb/status.h"
|
#include "rocksdb/status.h"
|
||||||
|
@ -116,6 +117,12 @@ class TimestampRecoveryHandler : public WriteBatch::Handler {
|
||||||
|
|
||||||
Status PutCF(uint32_t cf, const Slice& key, const Slice& value) override;
|
Status PutCF(uint32_t cf, const Slice& key, const Slice& value) override;
|
||||||
|
|
||||||
|
Status PutEntityCF(uint32_t cf, const Slice& key,
|
||||||
|
const Slice& entity) override;
|
||||||
|
|
||||||
|
Status TimedPutCF(uint32_t cf, const Slice& key, const Slice& value,
|
||||||
|
uint64_t write_time) override;
|
||||||
|
|
||||||
Status DeleteCF(uint32_t cf, const Slice& key) override;
|
Status DeleteCF(uint32_t cf, const Slice& key) override;
|
||||||
|
|
||||||
Status SingleDeleteCF(uint32_t cf, const Slice& key) override;
|
Status SingleDeleteCF(uint32_t cf, const Slice& key) override;
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||||
namespace {
|
namespace {
|
||||||
static const std::string kTestKeyWithoutTs = "key";
|
static const std::string kTestKeyWithoutTs = "key";
|
||||||
static const std::string kValuePlaceHolder = "value";
|
static const std::string kValuePlaceHolder = "value";
|
||||||
|
static const uint64_t kWriteUnixTime = 100;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class HandleTimestampSizeDifferenceTest : public testing::Test {
|
class HandleTimestampSizeDifferenceTest : public testing::Test {
|
||||||
|
@ -38,6 +39,34 @@ class HandleTimestampSizeDifferenceTest : public testing::Test {
|
||||||
return AddKey(cf, key);
|
return AddKey(cf, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status TimedPutCF(uint32_t cf, const Slice& key, const Slice& value,
|
||||||
|
uint64_t write_unix_time) override {
|
||||||
|
if (value.compare(kValuePlaceHolder) != 0) {
|
||||||
|
return Status::InvalidArgument();
|
||||||
|
}
|
||||||
|
if (write_unix_time != kWriteUnixTime) {
|
||||||
|
return Status::InvalidArgument();
|
||||||
|
}
|
||||||
|
return AddKey(cf, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status PutEntityCF(uint32_t cf, const Slice& key,
|
||||||
|
const Slice& entity) override {
|
||||||
|
Slice entity_copy = entity;
|
||||||
|
WideColumns columns;
|
||||||
|
Status s = WideColumnSerialization::Deserialize(entity_copy, columns);
|
||||||
|
if (!s.ok()) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
if (columns.size() != 1) {
|
||||||
|
return Status::InvalidArgument();
|
||||||
|
}
|
||||||
|
if (columns[0].value().compare(kValuePlaceHolder) != 0) {
|
||||||
|
return Status::InvalidArgument();
|
||||||
|
}
|
||||||
|
return AddKey(cf, key);
|
||||||
|
}
|
||||||
|
|
||||||
Status DeleteCF(uint32_t cf, const Slice& key) override {
|
Status DeleteCF(uint32_t cf, const Slice& key) override {
|
||||||
return AddKey(cf, key);
|
return AddKey(cf, key);
|
||||||
}
|
}
|
||||||
|
@ -117,6 +146,10 @@ class HandleTimestampSizeDifferenceTest : public testing::Test {
|
||||||
WriteBatchInternal::Merge(batch, cf_id, key, kValuePlaceHolder));
|
WriteBatchInternal::Merge(batch, cf_id, key, kValuePlaceHolder));
|
||||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(batch, cf_id, key,
|
ASSERT_OK(WriteBatchInternal::PutBlobIndex(batch, cf_id, key,
|
||||||
kValuePlaceHolder));
|
kValuePlaceHolder));
|
||||||
|
ASSERT_OK(WriteBatchInternal::TimedPut(
|
||||||
|
batch, cf_id, key, kValuePlaceHolder, kWriteUnixTime));
|
||||||
|
WideColumns columns{{kDefaultWideColumnName, kValuePlaceHolder}};
|
||||||
|
ASSERT_OK(WriteBatchInternal::PutEntity(batch, cf_id, key, columns));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,11 @@ class ColumnFamilyCollector : public WriteBatch::Handler {
|
||||||
return AddColumnFamilyId(column_family_id);
|
return AddColumnFamilyId(column_family_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status PutEntityCF(uint32_t column_family_id, const Slice&,
|
||||||
|
const Slice&) override {
|
||||||
|
return AddColumnFamilyId(column_family_id);
|
||||||
|
}
|
||||||
|
|
||||||
Status TimedPutCF(uint32_t column_family_id, const Slice&, const Slice&,
|
Status TimedPutCF(uint32_t column_family_id, const Slice&, const Slice&,
|
||||||
uint64_t) override {
|
uint64_t) override {
|
||||||
return AddColumnFamilyId(column_family_id);
|
return AddColumnFamilyId(column_family_id);
|
||||||
|
|
|
@ -85,9 +85,21 @@ Status GetAllKeyVersions(DB* db, ColumnFamilyHandle* cfh, Slice begin_key,
|
||||||
ScopedArenaPtr<InternalIterator> iter(
|
ScopedArenaPtr<InternalIterator> iter(
|
||||||
idb->NewInternalIterator(read_options, &arena, kMaxSequenceNumber, cfh));
|
idb->NewInternalIterator(read_options, &arena, kMaxSequenceNumber, cfh));
|
||||||
|
|
||||||
if (!begin_key.empty()) {
|
const Comparator* ucmp = icmp.user_comparator();
|
||||||
|
size_t ts_sz = ucmp->timestamp_size();
|
||||||
|
|
||||||
|
Slice from_slice = begin_key;
|
||||||
|
bool has_begin = !begin_key.empty();
|
||||||
|
Slice end_slice = end_key;
|
||||||
|
bool has_end = !end_key.empty();
|
||||||
|
std::string begin_key_buf, end_key_buf;
|
||||||
|
auto [from, end] = MaybeAddTimestampsToRange(
|
||||||
|
has_begin ? &from_slice : nullptr, has_end ? &end_slice : nullptr, ts_sz,
|
||||||
|
&begin_key_buf, &end_key_buf);
|
||||||
|
if (has_begin) {
|
||||||
|
assert(from.has_value());
|
||||||
InternalKey ikey;
|
InternalKey ikey;
|
||||||
ikey.SetMinPossibleForUserKey(begin_key);
|
ikey.SetMinPossibleForUserKey(from.value());
|
||||||
iter->Seek(ikey.Encode());
|
iter->Seek(ikey.Encode());
|
||||||
} else {
|
} else {
|
||||||
iter->SeekToFirst();
|
iter->SeekToFirst();
|
||||||
|
@ -102,8 +114,8 @@ Status GetAllKeyVersions(DB* db, ColumnFamilyHandle* cfh, Slice begin_key,
|
||||||
return pik_status;
|
return pik_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!end_key.empty() &&
|
if (has_end && end.has_value() &&
|
||||||
icmp.user_comparator()->Compare(ikey.user_key, end_key) > 0) {
|
icmp.user_comparator()->Compare(ikey.user_key, end.value()) > 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue