-
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
base: master
Are you sure you want to change the base?
Changes from all commits
3c2c514
a5ef0e0
df151fc
4da01fe
2a2a9bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
|
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. [P1] Bound idle contexts across these process-lifetime level pools. Sharing by |
||
| // 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) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.