Count number of corrupt keys during compaction

Summary:
For task #7771355, we would like to log the number of corrupt keys
during a compaction. This patch implements and tests the count
as part of CompactionJobStats.

Test Plan: make && make check

Reviewers: rven, igor, yhchiang, sdong

Reviewed By: sdong

Subscribers: dhruba, leveldb

Differential Revision: https://reviews.facebook.net/D42921
This commit is contained in:
Andres Notzli 2015-07-28 16:41:40 -07:00
parent 1bdfcef7bf
commit e95c59cd2f
5 changed files with 35 additions and 20 deletions

View File

@ -385,6 +385,10 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
has_current_user_key = false; has_current_user_key = false;
last_sequence_for_key = kMaxSequenceNumber; last_sequence_for_key = kMaxSequenceNumber;
visible_in_snapshot = kMaxSequenceNumber; visible_in_snapshot = kMaxSequenceNumber;
if (compaction_job_stats_ != nullptr) {
compaction_job_stats_->num_corrupt_keys++;
}
} else { } else {
if (compaction_job_stats_ != nullptr && ikey.type == kTypeDeletion) { if (compaction_job_stats_ != nullptr && ikey.type == kTypeDeletion) {
compaction_job_stats_->num_input_deletion_records++; compaction_job_stats_->num_input_deletion_records++;

View File

@ -467,6 +467,9 @@ class CompactionJobStatsChecker : public EventListener {
ASSERT_EQ(current_stats.num_records_replaced, ASSERT_EQ(current_stats.num_records_replaced,
stats.num_records_replaced); stats.num_records_replaced);
ASSERT_EQ(current_stats.num_corrupt_keys,
stats.num_corrupt_keys);
ASSERT_EQ( ASSERT_EQ(
std::string(current_stats.smallest_output_key_prefix), std::string(current_stats.smallest_output_key_prefix),
std::string(stats.smallest_output_key_prefix)); std::string(stats.smallest_output_key_prefix));
@ -509,6 +512,9 @@ class CompactionJobDeletionStatsChecker : public CompactionJobStatsChecker {
ASSERT_EQ( ASSERT_EQ(
current_stats.num_records_replaced, current_stats.num_records_replaced,
stats.num_records_replaced); stats.num_records_replaced);
ASSERT_EQ(current_stats.num_corrupt_keys,
stats.num_corrupt_keys);
} }
}; };

View File

@ -46,16 +46,14 @@ void VerifyInitializationOfCompactionJobStats(
ASSERT_EQ(compaction_job_stats.largest_output_key_prefix[0], 0); ASSERT_EQ(compaction_job_stats.largest_output_key_prefix[0], 0);
ASSERT_EQ(compaction_job_stats.num_records_replaced, 0U); ASSERT_EQ(compaction_job_stats.num_records_replaced, 0U);
ASSERT_EQ(compaction_job_stats.num_input_deletion_records, 0U);
ASSERT_EQ(compaction_job_stats.num_expired_deletion_records, 0U);
ASSERT_EQ(compaction_job_stats.num_corrupt_keys, 0U);
#endif // !defined(IOS_CROSS_COMPILE) #endif // !defined(IOS_CROSS_COMPILE)
} }
void VerifyCompactionJobStats(const CompactionJobStats& compaction_job_stats,
const std::vector<FileMetaData*>& files,
size_t num_output_files) {
ASSERT_GE(compaction_job_stats.elapsed_micros, 0U);
ASSERT_EQ(compaction_job_stats.num_input_files, files.size());
ASSERT_EQ(compaction_job_stats.num_output_files, num_output_files);
}
} // namespace } // namespace
// TODO(icanadi) Make it simpler once we mock out VersionSet // TODO(icanadi) Make it simpler once we mock out VersionSet
@ -197,13 +195,12 @@ class CompactionJobTest : public testing::Test {
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get()); LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
mutex_.Lock(); mutex_.Lock();
EventLogger event_logger(db_options_.info_log.get()); EventLogger event_logger(db_options_.info_log.get());
CompactionJobStats compaction_job_stats;
CompactionJob compaction_job( CompactionJob compaction_job(
0, &compaction, db_options_, env_options_, versions_.get(), 0, &compaction, db_options_, env_options_, versions_.get(),
&shutting_down_, &log_buffer, nullptr, nullptr, nullptr, {}, &shutting_down_, &log_buffer, nullptr, nullptr, nullptr, {},
table_cache_, &event_logger, false, dbname_, &compaction_job_stats); table_cache_, &event_logger, false, dbname_, &compaction_job_stats_);
VerifyInitializationOfCompactionJobStats(compaction_job_stats); VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
compaction_job.Prepare(); compaction_job.Prepare();
mutex_.Unlock(); mutex_.Unlock();
@ -214,7 +211,9 @@ class CompactionJobTest : public testing::Test {
ASSERT_OK(s); ASSERT_OK(s);
mutex_.Unlock(); mutex_.Unlock();
VerifyCompactionJobStats(compaction_job_stats, files, 1); ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
ASSERT_EQ(compaction_job_stats_.num_input_files, files.size());
ASSERT_EQ(compaction_job_stats_.num_output_files, 1U);
} }
Env* env_; Env* env_;
@ -230,6 +229,7 @@ class CompactionJobTest : public testing::Test {
InstrumentedMutex mutex_; InstrumentedMutex mutex_;
std::atomic<bool> shutting_down_; std::atomic<bool> shutting_down_;
std::shared_ptr<mock::MockTableFactory> mock_table_factory_; std::shared_ptr<mock::MockTableFactory> mock_table_factory_;
CompactionJobStats compaction_job_stats_;
}; };
TEST_F(CompactionJobTest, Simple) { TEST_F(CompactionJobTest, Simple) {
@ -248,6 +248,7 @@ TEST_F(CompactionJobTest, SimpleCorrupted) {
auto files = cfd->current()->storage_info()->LevelFiles(0); auto files = cfd->current()->storage_info()->LevelFiles(0);
RunCompaction(files); RunCompaction(files);
ASSERT_EQ(compaction_job_stats_.num_corrupt_keys, 400U);
mock_table_factory_->AssertLatestFile(expected_results); mock_table_factory_->AssertLatestFile(expected_results);
} }

View File

@ -49,12 +49,15 @@ struct CompactionJobStats {
// the number of deletion entries before compaction. Deletion entries // the number of deletion entries before compaction. Deletion entries
// can disappear after compaction because they expired // can disappear after compaction because they expired
uint64_t num_input_deletion_records; uint64_t num_input_deletion_records;
// number of deletion records that were found obsolete and discarded // number of deletion records that were found obsolete and discarded
// because it is not possible to delete any more keys with this entry // because it is not possible to delete any more keys with this entry
// (i.e. all possible deletions resulting from it have been completed) // (i.e. all possible deletions resulting from it have been completed)
uint64_t num_expired_deletion_records; uint64_t num_expired_deletion_records;
// number of corrupt keys (ParseInternalKey returned false when applied to
// the key) encountered and written out.
uint64_t num_corrupt_keys;
// 0-terminated strings storing the first 8 bytes of the smallest and // 0-terminated strings storing the first 8 bytes of the smallest and
// largest key in the output. // largest key in the output.
static const size_t kMaxPrefixLength = 8; static const size_t kMaxPrefixLength = 8;

View File

@ -3,8 +3,7 @@
// LICENSE file in the root directory of this source tree. An additional grant // LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory. // of patent rights can be found in the PATENTS file in the same directory.
#include <cstring> #include "rocksdb/compaction_job_stats.h"
#include "include/rocksdb/compaction_job_stats.h"
namespace rocksdb { namespace rocksdb {
@ -13,25 +12,27 @@ namespace rocksdb {
void CompactionJobStats::Reset() { void CompactionJobStats::Reset() {
elapsed_micros = 0; elapsed_micros = 0;
num_input_records = 0;
num_input_files = 0; num_input_files = 0;
num_input_files_at_output_level = 0; num_input_files_at_output_level = 0;
num_output_records = 0;
num_output_files = 0; num_output_files = 0;
num_input_records = 0; is_manual_compaction = 0;
num_output_records = 0;
total_input_bytes = 0; total_input_bytes = 0;
total_output_bytes = 0; total_output_bytes = 0;
num_records_replaced = 0;
total_input_raw_key_bytes = 0; total_input_raw_key_bytes = 0;
total_input_raw_value_bytes = 0; total_input_raw_value_bytes = 0;
num_records_replaced = 0;
is_manual_compaction = 0;
num_input_deletion_records = 0; num_input_deletion_records = 0;
num_expired_deletion_records = 0; num_expired_deletion_records = 0;
num_corrupt_keys = 0;
} }
#else #else