2016-02-10 00:12:00 +01:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-16 01:03:42 +02: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-12-05 22:09:13 +01:00
|
|
|
|
|
|
|
#include "rocksdb/table_properties.h"
|
2014-11-25 05:44:49 +01:00
|
|
|
#include "port/port.h"
|
2016-08-20 00:10:31 +02:00
|
|
|
#include "rocksdb/env.h"
|
|
|
|
#include "rocksdb/iterator.h"
|
|
|
|
#include "table/block.h"
|
2015-10-13 00:06:38 +02:00
|
|
|
#include "table/internal_iterator.h"
|
2016-08-20 00:10:31 +02:00
|
|
|
#include "table/table_properties_internal.h"
|
2014-11-25 05:44:49 +01:00
|
|
|
#include "util/string_util.h"
|
2013-12-05 22:09:13 +01:00
|
|
|
|
|
|
|
namespace rocksdb {
|
|
|
|
|
2015-10-09 01:57:35 +02:00
|
|
|
const uint32_t TablePropertiesCollectorFactory::Context::kUnknownColumnFamily =
|
|
|
|
port::kMaxInt32;
|
|
|
|
|
2013-12-05 22:09:13 +01:00
|
|
|
namespace {
|
|
|
|
void AppendProperty(
|
|
|
|
std::string& props,
|
|
|
|
const std::string& key,
|
|
|
|
const std::string& value,
|
|
|
|
const std::string& prop_delim,
|
|
|
|
const std::string& kv_delim) {
|
|
|
|
props.append(key);
|
|
|
|
props.append(kv_delim);
|
|
|
|
props.append(value);
|
|
|
|
props.append(prop_delim);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class TValue>
|
|
|
|
void AppendProperty(
|
|
|
|
std::string& props,
|
|
|
|
const std::string& key,
|
|
|
|
const TValue& value,
|
|
|
|
const std::string& prop_delim,
|
|
|
|
const std::string& kv_delim) {
|
|
|
|
AppendProperty(
|
2014-11-25 05:44:49 +01:00
|
|
|
props, key, ToString(value), prop_delim, kv_delim
|
2013-12-05 22:09:13 +01:00
|
|
|
);
|
|
|
|
}
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 02:36:03 +02:00
|
|
|
|
|
|
|
// Seek to the specified meta block.
|
|
|
|
// Return true if it successfully seeks to that block.
|
|
|
|
Status SeekToMetaBlock(InternalIterator* meta_iter,
|
2016-08-20 00:10:31 +02:00
|
|
|
const std::string& block_name, bool* is_found,
|
|
|
|
BlockHandle* block_handle = nullptr) {
|
2016-11-05 17:10:51 +01:00
|
|
|
if (block_handle != nullptr) {
|
|
|
|
*block_handle = BlockHandle::NullBlockHandle();
|
|
|
|
}
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 02:36:03 +02:00
|
|
|
*is_found = true;
|
|
|
|
meta_iter->Seek(block_name);
|
2016-08-20 00:10:31 +02:00
|
|
|
if (meta_iter->status().ok()) {
|
|
|
|
if (meta_iter->Valid() && meta_iter->key() == block_name) {
|
|
|
|
*is_found = true;
|
|
|
|
if (block_handle) {
|
|
|
|
Slice v = meta_iter->value();
|
|
|
|
return block_handle->DecodeFrom(&v);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*is_found = false;
|
|
|
|
return Status::OK();
|
|
|
|
}
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 02:36:03 +02:00
|
|
|
}
|
|
|
|
return meta_iter->status();
|
|
|
|
}
|
2013-12-05 22:09:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string TableProperties::ToString(
|
|
|
|
const std::string& prop_delim,
|
|
|
|
const std::string& kv_delim) const {
|
|
|
|
std::string result;
|
|
|
|
result.reserve(1024);
|
|
|
|
|
|
|
|
// Basic Info
|
2014-02-05 01:21:47 +01:00
|
|
|
AppendProperty(result, "# data blocks", num_data_blocks, prop_delim,
|
|
|
|
kv_delim);
|
2013-12-05 22:09:13 +01:00
|
|
|
AppendProperty(result, "# entries", num_entries, prop_delim, kv_delim);
|
|
|
|
|
|
|
|
AppendProperty(result, "raw key size", raw_key_size, prop_delim, kv_delim);
|
2014-02-05 01:21:47 +01:00
|
|
|
AppendProperty(result, "raw average key size",
|
|
|
|
num_entries != 0 ? 1.0 * raw_key_size / num_entries : 0.0,
|
|
|
|
prop_delim, kv_delim);
|
|
|
|
AppendProperty(result, "raw value size", raw_value_size, prop_delim,
|
|
|
|
kv_delim);
|
|
|
|
AppendProperty(result, "raw average value size",
|
|
|
|
num_entries != 0 ? 1.0 * raw_value_size / num_entries : 0.0,
|
|
|
|
prop_delim, kv_delim);
|
2013-12-05 22:09:13 +01:00
|
|
|
|
|
|
|
AppendProperty(result, "data block size", data_size, prop_delim, kv_delim);
|
|
|
|
AppendProperty(result, "index block size", index_size, prop_delim, kv_delim);
|
2017-06-13 19:59:22 +02:00
|
|
|
if (index_partitions != 0) {
|
|
|
|
AppendProperty(result, "# index partitions", index_partitions, prop_delim,
|
|
|
|
kv_delim);
|
|
|
|
AppendProperty(result, "top-level index size", top_level_index_size, prop_delim,
|
|
|
|
kv_delim);
|
|
|
|
}
|
2014-02-05 01:21:47 +01:00
|
|
|
AppendProperty(result, "filter block size", filter_size, prop_delim,
|
|
|
|
kv_delim);
|
|
|
|
AppendProperty(result, "(estimated) table size",
|
|
|
|
data_size + index_size + filter_size, prop_delim, kv_delim);
|
2013-12-05 22:09:13 +01:00
|
|
|
|
|
|
|
AppendProperty(
|
2014-02-05 01:21:47 +01:00
|
|
|
result, "filter policy name",
|
2013-12-05 22:09:13 +01:00
|
|
|
filter_policy_name.empty() ? std::string("N/A") : filter_policy_name,
|
2014-02-05 01:21:47 +01:00
|
|
|
prop_delim, kv_delim);
|
2013-12-05 22:09:13 +01:00
|
|
|
|
2016-04-09 03:50:18 +02:00
|
|
|
AppendProperty(result, "column family ID",
|
|
|
|
column_family_id == rocksdb::TablePropertiesCollectorFactory::
|
|
|
|
Context::kUnknownColumnFamily
|
|
|
|
? std::string("N/A")
|
|
|
|
: rocksdb::ToString(column_family_id),
|
|
|
|
prop_delim, kv_delim);
|
|
|
|
AppendProperty(
|
|
|
|
result, "column family name",
|
|
|
|
column_family_name.empty() ? std::string("N/A") : column_family_name,
|
|
|
|
prop_delim, kv_delim);
|
|
|
|
|
2016-04-21 19:16:28 +02:00
|
|
|
AppendProperty(result, "comparator name",
|
|
|
|
comparator_name.empty() ? std::string("N/A") : comparator_name,
|
|
|
|
prop_delim, kv_delim);
|
|
|
|
|
|
|
|
AppendProperty(
|
|
|
|
result, "merge operator name",
|
|
|
|
merge_operator_name.empty() ? std::string("N/A") : merge_operator_name,
|
|
|
|
prop_delim, kv_delim);
|
|
|
|
|
|
|
|
AppendProperty(result, "property collectors names",
|
|
|
|
property_collectors_names.empty() ? std::string("N/A")
|
|
|
|
: property_collectors_names,
|
|
|
|
prop_delim, kv_delim);
|
|
|
|
|
2016-05-12 18:47:16 +02:00
|
|
|
AppendProperty(
|
|
|
|
result, "SST file compression algo",
|
|
|
|
compression_name.empty() ? std::string("N/A") : compression_name,
|
|
|
|
prop_delim, kv_delim);
|
|
|
|
|
2017-06-28 02:02:20 +02:00
|
|
|
AppendProperty(result, "creation time", creation_time, prop_delim, kv_delim);
|
|
|
|
|
2013-12-05 22:09:13 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-08-25 21:03:54 +02:00
|
|
|
void TableProperties::Add(const TableProperties& tp) {
|
|
|
|
data_size += tp.data_size;
|
|
|
|
index_size += tp.index_size;
|
2017-06-13 19:59:22 +02:00
|
|
|
index_partitions += tp.index_partitions;
|
|
|
|
top_level_index_size += tp.top_level_index_size;
|
2015-08-25 21:03:54 +02:00
|
|
|
filter_size += tp.filter_size;
|
|
|
|
raw_key_size += tp.raw_key_size;
|
|
|
|
raw_value_size += tp.raw_value_size;
|
|
|
|
num_data_blocks += tp.num_data_blocks;
|
|
|
|
num_entries += tp.num_entries;
|
|
|
|
}
|
|
|
|
|
2013-12-05 22:09:13 +01:00
|
|
|
const std::string TablePropertiesNames::kDataSize =
|
|
|
|
"rocksdb.data.size";
|
|
|
|
const std::string TablePropertiesNames::kIndexSize =
|
|
|
|
"rocksdb.index.size";
|
2017-06-13 19:59:22 +02:00
|
|
|
const std::string TablePropertiesNames::kIndexPartitions =
|
|
|
|
"rocksdb.index.partitions";
|
|
|
|
const std::string TablePropertiesNames::kTopLevelIndexSize =
|
|
|
|
"rocksdb.top-level.index.size";
|
2013-12-05 22:09:13 +01:00
|
|
|
const std::string TablePropertiesNames::kFilterSize =
|
|
|
|
"rocksdb.filter.size";
|
|
|
|
const std::string TablePropertiesNames::kRawKeySize =
|
|
|
|
"rocksdb.raw.key.size";
|
|
|
|
const std::string TablePropertiesNames::kRawValueSize =
|
|
|
|
"rocksdb.raw.value.size";
|
|
|
|
const std::string TablePropertiesNames::kNumDataBlocks =
|
|
|
|
"rocksdb.num.data.blocks";
|
|
|
|
const std::string TablePropertiesNames::kNumEntries =
|
|
|
|
"rocksdb.num.entries";
|
|
|
|
const std::string TablePropertiesNames::kFilterPolicy =
|
|
|
|
"rocksdb.filter.policy";
|
2013-12-20 18:35:24 +01:00
|
|
|
const std::string TablePropertiesNames::kFormatVersion =
|
|
|
|
"rocksdb.format.version";
|
|
|
|
const std::string TablePropertiesNames::kFixedKeyLen =
|
|
|
|
"rocksdb.fixed.key.length";
|
2016-04-07 08:10:32 +02:00
|
|
|
const std::string TablePropertiesNames::kColumnFamilyId =
|
|
|
|
"rocksdb.column.family.id";
|
|
|
|
const std::string TablePropertiesNames::kColumnFamilyName =
|
|
|
|
"rocksdb.column.family.name";
|
2016-04-21 19:16:28 +02:00
|
|
|
const std::string TablePropertiesNames::kComparator = "rocksdb.comparator";
|
|
|
|
const std::string TablePropertiesNames::kMergeOperator =
|
|
|
|
"rocksdb.merge.operator";
|
2016-08-26 20:46:32 +02:00
|
|
|
const std::string TablePropertiesNames::kPrefixExtractorName =
|
|
|
|
"rocksdb.prefix.extractor.name";
|
2016-04-21 19:16:28 +02:00
|
|
|
const std::string TablePropertiesNames::kPropertyCollectors =
|
|
|
|
"rocksdb.property.collectors";
|
2016-05-12 18:47:16 +02:00
|
|
|
const std::string TablePropertiesNames::kCompression = "rocksdb.compression";
|
2017-06-28 02:02:20 +02:00
|
|
|
const std::string TablePropertiesNames::kCreationTime = "rocksdb.creation.time";
|
2013-12-05 22:09:13 +01:00
|
|
|
|
2013-12-06 01:51:26 +01:00
|
|
|
extern const std::string kPropertiesBlock = "rocksdb.properties";
|
RocksDB 2.8 to be able to read files generated by 2.6
Summary:
From 2.6 to 2.7, property block name is renamed from rocksdb.stats to rocksdb.properties. Older properties were not able to be loaded. In 2.8, we seem to have added some logic that uses property block without checking null pointers, which create segment faults.
In this patch, we fix it by:
(1) try rocksdb.stats if rocksdb.properties is not found
(2) add some null checking before consuming rep->table_properties
Test Plan: make sure a file generated in 2.7 couldn't be opened now can be opened.
Reviewers: haobo, igor, yhchiang
Reviewed By: igor
CC: ljin, xjin, dhruba, kailiu, leveldb
Differential Revision: https://reviews.facebook.net/D17961
2014-04-17 04:30:33 +02:00
|
|
|
// Old property block name for backward compatibility
|
|
|
|
extern const std::string kPropertiesBlockOldName = "rocksdb.stats";
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 02:36:03 +02:00
|
|
|
extern const std::string kCompressionDictBlock = "rocksdb.compression_dict";
|
2016-08-20 00:10:31 +02:00
|
|
|
extern const std::string kRangeDelBlock = "rocksdb.range_del";
|
2013-12-06 01:51:26 +01:00
|
|
|
|
2014-04-22 02:49:47 +02:00
|
|
|
// Seek to the properties block.
|
|
|
|
// Return true if it successfully seeks to the properties block.
|
2015-10-13 00:06:38 +02:00
|
|
|
Status SeekToPropertiesBlock(InternalIterator* meta_iter, bool* is_found) {
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 02:36:03 +02:00
|
|
|
Status status = SeekToMetaBlock(meta_iter, kPropertiesBlock, is_found);
|
|
|
|
if (!*is_found && status.ok()) {
|
|
|
|
status = SeekToMetaBlock(meta_iter, kPropertiesBlockOldName, is_found);
|
2014-04-22 02:49:47 +02:00
|
|
|
}
|
Shared dictionary compression using reference block
Summary:
This adds a new metablock containing a shared dictionary that is used
to compress all data blocks in the SST file. The size of the shared dictionary
is configurable in CompressionOptions and defaults to 0. It's currently only
used for zlib/lz4/lz4hc, but the block will be stored in the SST regardless of
the compression type if the user chooses a nonzero dictionary size.
During compaction, computes the dictionary by randomly sampling the first
output file in each subcompaction. It pre-computes the intervals to sample
by assuming the output file will have the maximum allowable length. In case
the file is smaller, some of the pre-computed sampling intervals can be beyond
end-of-file, in which case we skip over those samples and the dictionary will
be a bit smaller. After the dictionary is generated using the first file in a
subcompaction, it is loaded into the compression library before writing each
block in each subsequent file of that subcompaction.
On the read path, gets the dictionary from the metablock, if it exists. Then,
loads that dictionary into the compression library before reading each block.
Test Plan: new unit test
Reviewers: yhchiang, IslamAbdelRahman, cyan, sdong
Reviewed By: sdong
Subscribers: andrewkr, yoshinorim, kradhakrishnan, dhruba, leveldb
Differential Revision: https://reviews.facebook.net/D52287
2016-04-28 02:36:03 +02:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seek to the compression dictionary block.
|
|
|
|
// Return true if it successfully seeks to that block.
|
|
|
|
Status SeekToCompressionDictBlock(InternalIterator* meta_iter, bool* is_found) {
|
|
|
|
return SeekToMetaBlock(meta_iter, kCompressionDictBlock, is_found);
|
2014-04-22 02:49:47 +02:00
|
|
|
}
|
|
|
|
|
2016-08-20 00:10:31 +02:00
|
|
|
Status SeekToRangeDelBlock(InternalIterator* meta_iter, bool* is_found,
|
|
|
|
BlockHandle* block_handle = nullptr) {
|
|
|
|
return SeekToMetaBlock(meta_iter, kRangeDelBlock, is_found, block_handle);
|
|
|
|
}
|
|
|
|
|
2013-12-05 22:09:13 +01:00
|
|
|
} // namespace rocksdb
|