-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[feature](compression) Support per-column compression for non-cloud #66169
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
Open
zwy991114
wants to merge
3
commits into
apache:master
Choose a base branch
from
zwy991114:feature/per-column-compression-codec
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,579
−43
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,7 @@ | |
| #include <mutex> | ||
| #include <orc/Exceptions.hh> | ||
| #include <ostream> | ||
| #include <unordered_map> | ||
|
|
||
| #include "absl/strings/substitute.h" | ||
| #include "common/config.h" | ||
|
|
@@ -580,6 +581,8 @@ class Lz4HCBlockCompression : public BlockCompressionCodec { | |
| static Lz4HCBlockCompression s_instance; | ||
| return &s_instance; | ||
| } | ||
| Lz4HCBlockCompression() = default; | ||
| explicit Lz4HCBlockCompression(int level) : _compression_level(level) {} | ||
| ~Lz4HCBlockCompression() { | ||
| SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER( | ||
| ExecEnv::GetInstance()->block_compression_mem_tracker()); | ||
|
|
@@ -659,10 +662,20 @@ class Lz4HCBlockCompression : public BlockCompressionCodec { | |
| if (localCtx.get() == nullptr) { | ||
| return Status::InvalidArgument("new LZ4HC context error"); | ||
| } | ||
| localCtx->ctx = LZ4_createStreamHC(); | ||
| // Allocate the native stream under the compression tracker so its | ||
| // creation and the destructor's LZ4_freeStreamHC() hit the same tracker. | ||
| { | ||
| SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER( | ||
| ExecEnv::GetInstance()->block_compression_mem_tracker()); | ||
| localCtx->ctx = LZ4_createStreamHC(); | ||
| } | ||
| if (localCtx->ctx == nullptr) { | ||
| return Status::InvalidArgument("LZ4_createStreamHC error"); | ||
| } | ||
| // A newly created stream defaults to the library's default level, so | ||
| // apply the requested level here; otherwise the first page compressed | ||
| // by this context would ignore the configured level. | ||
| LZ4_resetStreamHC_fast(localCtx->ctx, static_cast<int>(_compression_level)); | ||
| out = std::move(localCtx); | ||
| return Status::OK(); | ||
| } | ||
|
|
@@ -1077,6 +1090,8 @@ class ZstdBlockCompression : public BlockCompressionCodec { | |
| static ZstdBlockCompression s_instance; | ||
| return &s_instance; | ||
| } | ||
| ZstdBlockCompression() = default; | ||
| explicit ZstdBlockCompression(int level) : _compression_level(level) {} | ||
| ~ZstdBlockCompression() { | ||
| SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER( | ||
| ExecEnv::GetInstance()->block_compression_mem_tracker()); | ||
|
|
@@ -1123,47 +1138,51 @@ class ZstdBlockCompression : public BlockCompressionCodec { | |
| compressed_buf.size = max_len; | ||
| } | ||
|
|
||
| // set compression level to default 3 | ||
| auto ret = ZSTD_CCtx_setParameter(context->ctx, ZSTD_c_compressionLevel, | ||
| ZSTD_CLEVEL_DEFAULT); | ||
| if (ZSTD_isError(ret)) { | ||
| return Status::InvalidArgument("ZSTD_CCtx_setParameter compression level error: {}", | ||
| ZSTD_getErrorString(ZSTD_getErrorCode(ret))); | ||
| } | ||
| // set checksum flag to 1 | ||
| ret = ZSTD_CCtx_setParameter(context->ctx, ZSTD_c_checksumFlag, 1); | ||
| if (ZSTD_isError(ret)) { | ||
| return Status::InvalidArgument("ZSTD_CCtx_setParameter checksumFlag error: {}", | ||
| ZSTD_getErrorString(ZSTD_getErrorCode(ret))); | ||
| } | ||
|
|
||
| ZSTD_outBuffer out_buf = {compressed_buf.data, compressed_buf.size, 0}; | ||
| { | ||
| SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER( | ||
| ExecEnv::GetInstance()->block_compression_mem_tracker()); | ||
| auto ret = ZSTD_CCtx_setParameter(context->ctx, ZSTD_c_compressionLevel, | ||
| _compression_level); | ||
| if (ZSTD_isError(ret)) { | ||
| return Status::InvalidArgument( | ||
| "ZSTD_CCtx_setParameter compression level error: {}", | ||
| ZSTD_getErrorString(ZSTD_getErrorCode(ret))); | ||
| } | ||
| // set checksum flag to 1 | ||
| ret = ZSTD_CCtx_setParameter(context->ctx, ZSTD_c_checksumFlag, 1); | ||
| if (ZSTD_isError(ret)) { | ||
| return Status::InvalidArgument("ZSTD_CCtx_setParameter checksumFlag error: {}", | ||
| ZSTD_getErrorString(ZSTD_getErrorCode(ret))); | ||
| } | ||
|
|
||
| for (size_t i = 0; i < inputs.size(); i++) { | ||
| ZSTD_inBuffer in_buf = {inputs[i].data, inputs[i].size, 0}; | ||
| for (size_t i = 0; i < inputs.size(); i++) { | ||
| ZSTD_inBuffer in_buf = {inputs[i].data, inputs[i].size, 0}; | ||
|
|
||
| bool last_input = (i == inputs.size() - 1); | ||
| auto mode = last_input ? ZSTD_e_end : ZSTD_e_continue; | ||
| bool last_input = (i == inputs.size() - 1); | ||
| auto mode = last_input ? ZSTD_e_end : ZSTD_e_continue; | ||
|
|
||
| bool finished = false; | ||
| do { | ||
| // do compress | ||
| ret = ZSTD_compressStream2(context->ctx, &out_buf, &in_buf, mode); | ||
| bool finished = false; | ||
| do { | ||
| // do compress | ||
| ret = ZSTD_compressStream2(context->ctx, &out_buf, &in_buf, mode); | ||
|
|
||
| if (ZSTD_isError(ret)) { | ||
| compress_failed = true; | ||
| return Status::InternalError("ZSTD_compressStream2 error: {}", | ||
| ZSTD_getErrorString(ZSTD_getErrorCode(ret))); | ||
| } | ||
| if (ZSTD_isError(ret)) { | ||
| compress_failed = true; | ||
| return Status::InternalError( | ||
| "ZSTD_compressStream2 error: {}", | ||
| ZSTD_getErrorString(ZSTD_getErrorCode(ret))); | ||
| } | ||
|
|
||
| // ret is ZSTD hint for needed output buffer size | ||
| if (ret > 0 && out_buf.pos == out_buf.size) { | ||
| compress_failed = true; | ||
| return Status::InternalError("ZSTD_compressStream2 output buffer full"); | ||
| } | ||
| // ret is ZSTD hint for needed output buffer size | ||
| if (ret > 0 && out_buf.pos == out_buf.size) { | ||
| compress_failed = true; | ||
| return Status::InternalError("ZSTD_compressStream2 output buffer full"); | ||
| } | ||
|
|
||
| finished = last_input ? (ret == 0) : (in_buf.pos == inputs[i].size); | ||
| } while (!finished); | ||
| finished = last_input ? (ret == 0) : (in_buf.pos == inputs[i].size); | ||
| } while (!finished); | ||
| } | ||
| } | ||
|
|
||
| // set compressed size for caller | ||
|
|
@@ -1215,7 +1234,13 @@ class ZstdBlockCompression : public BlockCompressionCodec { | |
| return Status::InvalidArgument("failed to new ZSTD CContext"); | ||
| } | ||
| //typedef LZ4F_cctx* LZ4F_compressionContext_t; | ||
| localCtx->ctx = ZSTD_createCCtx(); | ||
| // Allocate the native context under the compression tracker so its | ||
| // creation and the destructor's ZSTD_freeCCtx() hit the same tracker. | ||
| { | ||
| SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER( | ||
| ExecEnv::GetInstance()->block_compression_mem_tracker()); | ||
| localCtx->ctx = ZSTD_createCCtx(); | ||
| } | ||
| if (localCtx->ctx == nullptr) { | ||
| return Status::InvalidArgument("Failed to create ZSTD compress ctx"); | ||
| } | ||
|
|
@@ -1262,6 +1287,7 @@ class ZstdBlockCompression : public BlockCompressionCodec { | |
| } | ||
|
|
||
| private: | ||
| int _compression_level = ZSTD_CLEVEL_DEFAULT; | ||
| mutable std::mutex _ctx_c_mutex; | ||
| mutable std::vector<std::unique_ptr<CContext>> _ctx_c_pool; | ||
|
|
||
|
|
@@ -1615,6 +1641,84 @@ Status get_block_compression_codec(segment_v2::CompressionTypePB type, | |
| return Status::OK(); | ||
| } | ||
|
|
||
| // Process-wide registry of level-aware codecs, keyed by (type, level). All | ||
| // column writers that request the same codec+level share one instance, so its | ||
| // internal context pool is reused according to actual write concurrency rather | ||
| // than allocated once per column. Instances live for the process lifetime (like | ||
| // the type-only singletons above), so their native contexts are never torn down | ||
| // per segment. | ||
| namespace { | ||
| class LeveledCompressionCodecPool { | ||
| public: | ||
| static LeveledCompressionCodecPool& instance() { | ||
| static LeveledCompressionCodecPool s_instance; | ||
| return s_instance; | ||
| } | ||
|
|
||
| Status get(segment_v2::CompressionTypePB type, int level, BlockCompressionCodec** codec) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里如果是旧表的话会传入 level = 0,之前旧表的默认的 ZSTD 或者 LZ4HC 的 level 是 0 吗 |
||
| const int64_t key = (static_cast<int64_t>(type) << 32) | static_cast<uint32_t>(level); | ||
| { | ||
| std::lock_guard<std::mutex> l(_mutex); | ||
| auto it = _codecs.find(key); | ||
| if (it != _codecs.end()) { | ||
| *codec = it->second.get(); | ||
| return Status::OK(); | ||
| } | ||
| } | ||
|
|
||
| // Build the instance outside the lock; init() may allocate native state. | ||
| std::unique_ptr<BlockCompressionCodec> instance; | ||
| switch (type) { | ||
| case segment_v2::CompressionTypePB::ZSTD: | ||
| instance = std::make_unique<ZstdBlockCompression>(level); | ||
| break; | ||
| case segment_v2::CompressionTypePB::LZ4HC: | ||
| instance = std::make_unique<Lz4HCBlockCompression>(level); | ||
| break; | ||
| default: | ||
| return Status::InternalError("compression type({}) is not level-aware", type); | ||
| } | ||
| RETURN_IF_ERROR(instance->init()); | ||
|
|
||
| std::lock_guard<std::mutex> l(_mutex); | ||
| // Another thread may have inserted the same key while we were building. | ||
| auto it = _codecs.try_emplace(key, std::move(instance)).first; | ||
| *codec = it->second.get(); | ||
| return Status::OK(); | ||
| } | ||
|
|
||
| // Test hook: drop all pooled instances so a fresh test observes a clean pool. | ||
| void clear() { | ||
| std::lock_guard<std::mutex> l(_mutex); | ||
| _codecs.clear(); | ||
| } | ||
|
|
||
| private: | ||
| std::mutex _mutex; | ||
| std::unordered_map<int64_t, std::unique_ptr<BlockCompressionCodec>> _codecs; | ||
| }; | ||
| } // namespace | ||
|
|
||
| Status get_block_compression_codec(segment_v2::CompressionTypePB type, int level, | ||
| BlockCompressionCodec** codec) { | ||
| // level <= 0 means "use codec default" -> fall back to the stateless singleton path. | ||
| if (level <= 0) { | ||
| return get_block_compression_codec(type, codec); | ||
| } | ||
| switch (type) { | ||
| case segment_v2::CompressionTypePB::ZSTD: | ||
| case segment_v2::CompressionTypePB::LZ4HC: | ||
| return LeveledCompressionCodecPool::instance().get(type, level, codec); | ||
| default: | ||
| // types without a tunable level ignore it and use the singleton | ||
| return get_block_compression_codec(type, codec); | ||
| } | ||
| } | ||
|
|
||
| void clear_leveled_compression_codec_pool_for_test() { | ||
| LeveledCompressionCodecPool::instance().clear(); | ||
| } | ||
|
|
||
| // this can only be used in hive text write | ||
| Status get_block_compression_codec(TFileCompressType::type type, BlockCompressionCodec** codec) { | ||
| switch (type) { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
ColumnPB 中也要设置