This is an automated email from the ASF dual-hosted git repository.
eldenmoon pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-3.0 by this push:
new d9f270a0ed3 [cloud](Variant) limit column size in MetaService and
support handle rowset schema in backend side(#41482) (#43093)
d9f270a0ed3 is described below
commit d9f270a0ed38f2760f02f6037f72c32c219e82f8
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Nov 5 10:01:27 2024 +0800
[cloud](Variant) limit column size in MetaService and support handle rowset
schema in backend side(#41482) (#43093)
PR Body: 1. in local mode we limit column size in commit_txn phase, in
cloud mode we limit it in `write_schema_dict` which in commit_rowset
phase
2. add config to decide whether fill schema in BE side or MS side
Cherry-picked from #41482
---
be/src/cloud/cloud_meta_mgr.cpp | 8 +++-
be/src/cloud/pb_convert.cpp | 55 +++++++++++++++++++---
be/src/cloud/pb_convert.h | 12 +++--
be/src/common/config.cpp | 2 +
be/src/common/config.h | 1 +
.../rowset/segment_v2/vertical_segment_writer.cpp | 4 +-
cloud/src/common/config.h | 6 ++-
cloud/src/meta-service/meta_service.cpp | 4 +-
cloud/src/meta-service/meta_service_schema.cpp | 25 ++++++++--
cloud/src/meta-service/meta_service_schema.h | 6 +--
gensrc/proto/cloud.proto | 11 ++++-
.../suites/variant_p0/column_size_limit.groovy | 33 ++++---------
12 files changed, 119 insertions(+), 48 deletions(-)
diff --git a/be/src/cloud/cloud_meta_mgr.cpp b/be/src/cloud/cloud_meta_mgr.cpp
index 02497f6a044..e198017f17a 100644
--- a/be/src/cloud/cloud_meta_mgr.cpp
+++ b/be/src/cloud/cloud_meta_mgr.cpp
@@ -39,6 +39,7 @@
#include "cloud/cloud_tablet.h"
#include "cloud/config.h"
#include "cloud/pb_convert.h"
+#include "common/config.h"
#include "common/logging.h"
#include "common/status.h"
#include "cpp/sync_point.h"
@@ -410,6 +411,10 @@ Status CloudMetaMgr::sync_tablet_rowsets(CloudTablet*
tablet, bool warmup_delta_
req.set_cumulative_point(tablet->cumulative_layer_point());
}
req.set_end_version(-1);
+ // backend side use schema dict
+ if (config::variant_use_cloud_schema_dict) {
+ req.set_schema_op(GetRowsetRequest::RETURN_DICT);
+ }
VLOG_DEBUG << "send GetRowsetRequest: " << req.ShortDebugString();
stub->get_rowset(&cntl, &req, &resp, nullptr);
@@ -524,7 +529,8 @@ Status CloudMetaMgr::sync_tablet_rowsets(CloudTablet*
tablet, bool warmup_delta_
existed_rowset->rowset_id().to_string() ==
cloud_rs_meta_pb.rowset_id_v2()) {
continue; // Same rowset, skip it
}
- RowsetMetaPB meta_pb =
cloud_rowset_meta_to_doris(cloud_rs_meta_pb);
+ RowsetMetaPB meta_pb = cloud_rowset_meta_to_doris(
+ cloud_rs_meta_pb, resp.has_schema_dict() ?
&resp.schema_dict() : nullptr);
auto rs_meta = std::make_shared<RowsetMeta>();
rs_meta->init_from_pb(meta_pb);
RowsetSharedPtr rowset;
diff --git a/be/src/cloud/pb_convert.cpp b/be/src/cloud/pb_convert.cpp
index 550c08c5481..c65d3208be4 100644
--- a/be/src/cloud/pb_convert.cpp
+++ b/be/src/cloud/pb_convert.cpp
@@ -17,6 +17,7 @@
#include "cloud/pb_convert.h"
+#include <common/logging.h>
#include <gen_cpp/olap_file.pb.h>
#include <utility>
@@ -138,19 +139,54 @@ void doris_rowset_meta_to_cloud(RowsetMetaCloudPB* out,
RowsetMetaPB&& in) {
out->mutable_inverted_index_file_info()->Swap(in.mutable_inverted_index_file_info());
}
-RowsetMetaPB cloud_rowset_meta_to_doris(const RowsetMetaCloudPB& in) {
+static void fill_schema_with_dict(const RowsetMetaCloudPB& in, RowsetMetaPB*
out,
+ const SchemaCloudDictionary& dict) {
+ std::unordered_map<int32_t, ColumnPB*> unique_id_map;
+ //init map
+ for (ColumnPB& column : *out->mutable_tablet_schema()->mutable_column()) {
+ unique_id_map[column.unique_id()] = &column;
+ }
+ // column info
+ for (size_t i = 0; i <
in.schema_dict_key_list().column_dict_key_list_size(); ++i) {
+ int dict_key = in.schema_dict_key_list().column_dict_key_list(i);
+ const ColumnPB& dict_val = dict.column_dict().at(dict_key);
+ ColumnPB& to_add = *out->mutable_tablet_schema()->add_column();
+ to_add = dict_val;
+ VLOG_DEBUG << "fill dict column " << dict_val.ShortDebugString();
+ }
+
+ // index info
+ for (size_t i = 0; i <
in.schema_dict_key_list().index_info_dict_key_list_size(); ++i) {
+ int dict_key = in.schema_dict_key_list().index_info_dict_key_list(i);
+ const TabletIndexPB& dict_val = dict.index_dict().at(dict_key);
+ *out->mutable_tablet_schema()->add_index() = dict_val;
+ VLOG_DEBUG << "fill dict index " << dict_val.ShortDebugString();
+ }
+
+ // sparse column info
+ for (size_t i = 0; i <
in.schema_dict_key_list().sparse_column_dict_key_list_size(); ++i) {
+ int dict_key =
in.schema_dict_key_list().sparse_column_dict_key_list(i);
+ const ColumnPB& dict_val = dict.column_dict().at(dict_key);
+ *unique_id_map.at(dict_val.parent_unique_id())->add_sparse_columns() =
dict_val;
+ VLOG_DEBUG << "fill dict sparse column" << dict_val.ShortDebugString();
+ }
+}
+
+RowsetMetaPB cloud_rowset_meta_to_doris(const RowsetMetaCloudPB& in,
+ const SchemaCloudDictionary* dict) {
RowsetMetaPB out;
- cloud_rowset_meta_to_doris(&out, in);
+ cloud_rowset_meta_to_doris(&out, in, dict);
return out;
}
-RowsetMetaPB cloud_rowset_meta_to_doris(RowsetMetaCloudPB&& in) {
+RowsetMetaPB cloud_rowset_meta_to_doris(RowsetMetaCloudPB&& in, const
SchemaCloudDictionary* dict) {
RowsetMetaPB out;
- cloud_rowset_meta_to_doris(&out, std::move(in));
+ cloud_rowset_meta_to_doris(&out, std::move(in), dict);
return out;
}
-void cloud_rowset_meta_to_doris(RowsetMetaPB* out, const RowsetMetaCloudPB&
in) {
+void cloud_rowset_meta_to_doris(RowsetMetaPB* out, const RowsetMetaCloudPB& in,
+ const SchemaCloudDictionary* dict) {
// ATTN: please keep the set order aligned with the definition of proto
`TabletSchemaCloudPB`.
out->set_rowset_id(in.rowset_id());
out->set_partition_id(in.partition_id());
@@ -185,6 +221,9 @@ void cloud_rowset_meta_to_doris(RowsetMetaPB* out, const
RowsetMetaCloudPB& in)
if (in.has_tablet_schema()) {
cloud_tablet_schema_to_doris(out->mutable_tablet_schema(),
in.tablet_schema());
}
+ if (dict != nullptr) {
+ fill_schema_with_dict(in, out, *dict);
+ }
out->set_txn_expiration(in.txn_expiration());
out->set_segments_overlap_pb(in.segments_overlap_pb());
out->mutable_segments_file_size()->CopyFrom(in.segments_file_size());
@@ -198,7 +237,8 @@ void cloud_rowset_meta_to_doris(RowsetMetaPB* out, const
RowsetMetaCloudPB& in)
out->mutable_inverted_index_file_info()->CopyFrom(in.inverted_index_file_info());
}
-void cloud_rowset_meta_to_doris(RowsetMetaPB* out, RowsetMetaCloudPB&& in) {
+void cloud_rowset_meta_to_doris(RowsetMetaPB* out, RowsetMetaCloudPB&& in,
+ const SchemaCloudDictionary* dict) {
// ATTN: please keep the set order aligned with the definition of proto
`TabletSchemaCloudPB`.
out->set_rowset_id(in.rowset_id());
out->set_partition_id(in.partition_id());
@@ -234,6 +274,9 @@ void cloud_rowset_meta_to_doris(RowsetMetaPB* out,
RowsetMetaCloudPB&& in) {
cloud_tablet_schema_to_doris(out->mutable_tablet_schema(),
std::move(*in.mutable_tablet_schema()));
}
+ if (dict != nullptr) {
+ fill_schema_with_dict(in, out, *dict);
+ }
out->set_txn_expiration(in.txn_expiration());
out->set_segments_overlap_pb(in.segments_overlap_pb());
out->mutable_segments_file_size()->Swap(in.mutable_segments_file_size());
diff --git a/be/src/cloud/pb_convert.h b/be/src/cloud/pb_convert.h
index 0cfa033f293..31fe43adb11 100644
--- a/be/src/cloud/pb_convert.h
+++ b/be/src/cloud/pb_convert.h
@@ -24,10 +24,14 @@ RowsetMetaCloudPB doris_rowset_meta_to_cloud(const
RowsetMetaPB&);
RowsetMetaCloudPB doris_rowset_meta_to_cloud(RowsetMetaPB&&);
void doris_rowset_meta_to_cloud(RowsetMetaCloudPB* out, const RowsetMetaPB&
in);
void doris_rowset_meta_to_cloud(RowsetMetaCloudPB* out, RowsetMetaPB&& in);
-RowsetMetaPB cloud_rowset_meta_to_doris(const RowsetMetaCloudPB&);
-RowsetMetaPB cloud_rowset_meta_to_doris(RowsetMetaCloudPB&&);
-void cloud_rowset_meta_to_doris(RowsetMetaPB* out, const RowsetMetaCloudPB&
in);
-void cloud_rowset_meta_to_doris(RowsetMetaPB* out, RowsetMetaCloudPB&& in);
+RowsetMetaPB cloud_rowset_meta_to_doris(const RowsetMetaCloudPB&,
+ const SchemaCloudDictionary* dict =
nullptr);
+RowsetMetaPB cloud_rowset_meta_to_doris(RowsetMetaCloudPB&&,
+ const SchemaCloudDictionary* dict =
nullptr);
+void cloud_rowset_meta_to_doris(RowsetMetaPB* out, const RowsetMetaCloudPB& in,
+ const SchemaCloudDictionary* dict = nullptr);
+void cloud_rowset_meta_to_doris(RowsetMetaPB* out, RowsetMetaCloudPB&& in,
+ const SchemaCloudDictionary* dict = nullptr);
// TabletSchemaPB <=> TabletSchemaCloudPB
TabletSchemaCloudPB doris_tablet_schema_to_cloud(const TabletSchemaPB&);
diff --git a/be/src/common/config.cpp b/be/src/common/config.cpp
index bb92cf18b73..48d4565c1d3 100644
--- a/be/src/common/config.cpp
+++ b/be/src/common/config.cpp
@@ -1003,6 +1003,8 @@ DEFINE_Int32(pipeline_executor_size, "0");
DEFINE_Bool(enable_workload_group_for_scan, "false");
DEFINE_mInt64(workload_group_scan_task_wait_timeout_ms, "10000");
+// Whether use schema dict in backend side instead of MetaService side(cloud
mode)
+DEFINE_mBool(variant_use_cloud_schema_dict, "true");
DEFINE_mDouble(variant_ratio_of_defaults_as_sparse_column, "1");
DEFINE_mInt64(variant_threshold_rows_to_estimate_sparse_column, "2048");
DEFINE_mBool(variant_throw_exeception_on_invalid_json, "false");
diff --git a/be/src/common/config.h b/be/src/common/config.h
index 7e70e067f3a..27e697b0c80 100644
--- a/be/src/common/config.h
+++ b/be/src/common/config.h
@@ -1199,6 +1199,7 @@ DECLARE_mInt64(LZ4_HC_compression_level);
// Threshold of a column as sparse column
// Notice: TEST ONLY
DECLARE_mDouble(variant_ratio_of_defaults_as_sparse_column);
+DECLARE_mBool(variant_use_cloud_schema_dict);
// Threshold to estimate a column is sparsed
// Notice: TEST ONLY
DECLARE_mInt64(variant_threshold_rows_to_estimate_sparse_column);
diff --git a/be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp
b/be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp
index 0769dbe86d2..4f94189a621 100644
--- a/be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp
+++ b/be/src/olap/rowset/segment_v2/vertical_segment_writer.cpp
@@ -666,8 +666,10 @@ Status
VerticalSegmentWriter::_append_block_with_variant_subcolumns(RowsInBlock&
_opts.rowset_ctx->merged_tablet_schema =
_opts.rowset_ctx->tablet_schema;
}
TabletSchemaSPtr update_schema;
+ bool check_schema_size = true;
RETURN_IF_ERROR(vectorized::schema_util::get_least_common_schema(
- {_opts.rowset_ctx->merged_tablet_schema, _flush_schema}, nullptr,
update_schema));
+ {_opts.rowset_ctx->merged_tablet_schema, _flush_schema}, nullptr,
update_schema,
+ check_schema_size));
CHECK_GE(update_schema->num_columns(), _flush_schema->num_columns())
<< "Rowset merge schema columns count is " <<
update_schema->num_columns()
<< ", but flush_schema is larger " << _flush_schema->num_columns()
diff --git a/cloud/src/common/config.h b/cloud/src/common/config.h
index f6a0205d8a5..da865650c60 100644
--- a/cloud/src/common/config.h
+++ b/cloud/src/common/config.h
@@ -135,8 +135,10 @@ CONF_mBool(snapshot_get_tablet_stats, "true");
// Value codec version
CONF_mInt16(meta_schema_value_version, "1");
-// Limit kv size of Schema SchemaDictKeyList, default 10MB
-CONF_mInt32(schema_dict_kv_size_limit, "10485760");
+// Limit kv size of Schema SchemaDictKeyList, default 5MB
+CONF_mInt32(schema_dict_kv_size_limit, "5242880");
+// Limit the count of columns in schema dict value, default 4K
+CONF_mInt32(schema_dict_key_count_limit, "4096");
// For instance check interval
CONF_Int64(reserved_buffer_days, "3");
diff --git a/cloud/src/meta-service/meta_service.cpp
b/cloud/src/meta-service/meta_service.cpp
index 7adbc8ccf12..107ca9c0447 100644
--- a/cloud/src/meta-service/meta_service.cpp
+++ b/cloud/src/meta-service/meta_service.cpp
@@ -1587,8 +1587,8 @@ void
MetaServiceImpl::get_rowset(::google::protobuf::RpcController* controller,
}
if (need_read_schema_dict) {
- read_schema_from_dict(code, msg, instance_id, idx.index_id(),
txn.get(),
- response->mutable_rowset_meta());
+ read_schema_dict(code, msg, instance_id, idx.index_id(),
txn.get(), response,
+ request->schema_op());
if (code != MetaServiceCode::OK) return;
}
TEST_SYNC_POINT_CALLBACK("get_rowset::finish", &response);
diff --git a/cloud/src/meta-service/meta_service_schema.cpp
b/cloud/src/meta-service/meta_service_schema.cpp
index d99f026d051..ca0a15d8577 100644
--- a/cloud/src/meta-service/meta_service_schema.cpp
+++ b/cloud/src/meta-service/meta_service_schema.cpp
@@ -292,11 +292,20 @@ void write_schema_dict(MetaServiceCode& code,
std::string& msg, const std::strin
}
// Limit the size of dict value
if (dict_val.size() > config::schema_dict_kv_size_limit) {
- code = MetaServiceCode::KV_TXN_COMMIT_ERR;
+ code = MetaServiceCode::INVALID_ARGUMENT;
ss << "Failed to write dictionary for saving, txn_id=" <<
rowset_meta->txn_id()
<< ", reached the limited size threshold of SchemaDictKeyList "
<< config::schema_dict_kv_size_limit;
msg = ss.str();
+ return;
+ }
+ // Limit the count of dict keys
+ if (dict.column_dict_size() > config::schema_dict_key_count_limit) {
+ code = MetaServiceCode::INVALID_ARGUMENT;
+ ss << "Reached max column size limit " <<
config::schema_dict_key_count_limit
+ << ", txn_id=" << rowset_meta->txn_id();
+ msg = ss.str();
+ return;
}
// splitting large values (>90*1000) into multiple KVs
cloud::put(txn, dict_key, dict_val, 0);
@@ -307,9 +316,9 @@ void write_schema_dict(MetaServiceCode& code, std::string&
msg, const std::strin
}
}
-void read_schema_from_dict(MetaServiceCode& code, std::string& msg, const
std::string& instance_id,
- int64_t index_id, Transaction* txn,
-
google::protobuf::RepeatedPtrField<RowsetMetaCloudPB>* rowset_metas) {
+void read_schema_dict(MetaServiceCode& code, std::string& msg, const
std::string& instance_id,
+ int64_t index_id, Transaction* txn, GetRowsetResponse*
response,
+ GetRowsetRequest::SchemaOp schema_op) {
std::stringstream ss;
// read dict if any rowset has dict key list
@@ -331,6 +340,12 @@ void read_schema_from_dict(MetaServiceCode& code,
std::string& msg, const std::s
LOG(INFO) << "Get schema_dict, column size=" << dict.column_dict_size()
<< ", index size=" << dict.index_dict_size();
+ // Return dict, let backend to fill schema with dict info
+ if (schema_op == GetRowsetRequest::RETURN_DICT) {
+ response->mutable_schema_dict()->Swap(&dict);
+ return;
+ }
+
auto fill_schema_with_dict = [&](RowsetMetaCloudPB* out) {
std::unordered_map<int32_t, ColumnPB*> unique_id_map;
//init map
@@ -366,7 +381,7 @@ void read_schema_from_dict(MetaServiceCode& code,
std::string& msg, const std::s
};
// fill rowsets's schema with dict info
- for (auto& rowset_meta : *rowset_metas) {
+ for (auto& rowset_meta : *response->mutable_rowset_meta()) {
if (rowset_meta.has_schema_dict_key_list()) {
fill_schema_with_dict(&rowset_meta);
}
diff --git a/cloud/src/meta-service/meta_service_schema.h
b/cloud/src/meta-service/meta_service_schema.h
index d44f01f9747..ec1dcc6731f 100644
--- a/cloud/src/meta-service/meta_service_schema.h
+++ b/cloud/src/meta-service/meta_service_schema.h
@@ -35,8 +35,8 @@ void write_schema_dict(MetaServiceCode& code, std::string&
msg, const std::strin
Transaction* txn, RowsetMetaCloudPB* rowset_meta);
// Read schema from dictionary metadata, modified to rowset_metas
-void read_schema_from_dict(MetaServiceCode& code, std::string& msg, const
std::string& instance_id,
- int64_t index_id, Transaction* txn,
-
google::protobuf::RepeatedPtrField<RowsetMetaCloudPB>* rowset_metas);
+void read_schema_dict(MetaServiceCode& code, std::string& msg, const
std::string& instance_id,
+ int64_t index_id, Transaction* txn, GetRowsetResponse*
response,
+ GetRowsetRequest::SchemaOp schema_op);
} // namespace doris::cloud
diff --git a/gensrc/proto/cloud.proto b/gensrc/proto/cloud.proto
index 8ae48851601..d116a8106db 100644
--- a/gensrc/proto/cloud.proto
+++ b/gensrc/proto/cloud.proto
@@ -961,6 +961,11 @@ message CreateRowsetResponse {
}
message GetRowsetRequest {
+ enum SchemaOp {
+ FILL_WITH_DICT = 0; // fill rowset schema with SchemaCloudDictionary
value
+ RETURN_DICT = 1; // not use dict value in MS, return
SchemaCloudDictionary directly
+ NO_DICT = 2; // not read dict info, use local cached
SchemaCloudDictionary instead
+ }
optional string cloud_unique_id = 1; // For auth
optional TabletIndexPB idx = 2;
optional int64 start_version = 3;
@@ -968,13 +973,17 @@ message GetRowsetRequest {
optional int64 base_compaction_cnt = 5;
optional int64 cumulative_compaction_cnt = 6;
optional int64 cumulative_point = 7;
- // TODO: There may be more fields TBD
+ // returned schema format on rowset schema, used in variant type directly.
+ // for compability reason we use FILL_WITH_DICT as default
+ optional SchemaOp schema_op = 8 [default = FILL_WITH_DICT];
}
message GetRowsetResponse {
optional MetaServiceResponseStatus status = 1;
repeated doris.RowsetMetaCloudPB rowset_meta = 2;
optional TabletStatsPB stats = 3;
+ // Return dict value if SchemaOp is RETURN_DICT
+ optional SchemaCloudDictionary schema_dict = 4;
}
message IndexRequest {
diff --git a/regression-test/suites/variant_p0/column_size_limit.groovy
b/regression-test/suites/variant_p0/column_size_limit.groovy
index 70567d89c07..5e9d05b558e 100644
--- a/regression-test/suites/variant_p0/column_size_limit.groovy
+++ b/regression-test/suites/variant_p0/column_size_limit.groovy
@@ -16,17 +16,7 @@
// under the License.
import groovy.json.JsonBuilder
-suite("regression_test_variant_column_limit", "nonConcurrent"){
- def set_be_config = { key, value ->
- String backend_id;
- def backendId_to_backendIP = [:]
- def backendId_to_backendHttpPort = [:]
- getBackendIpHttpPort(backendId_to_backendIP,
backendId_to_backendHttpPort);
-
- backend_id = backendId_to_backendIP.keySet()[0]
- def (code, out, err) =
update_be_config(backendId_to_backendIP.get(backend_id),
backendId_to_backendHttpPort.get(backend_id), key, value)
- logger.info("update config: code=" + code + ", out=" + out + ", err="
+ err)
- }
+suite("regression_test_variant_column_limit"){
def table_name = "var_column_limit"
sql "DROP TABLE IF EXISTS ${table_name}"
sql """
@@ -38,21 +28,18 @@ suite("regression_test_variant_column_limit",
"nonConcurrent"){
DISTRIBUTED BY HASH(k) BUCKETS 1
properties("replication_num" = "1", "disable_auto_compaction" =
"false");
"""
- try {
- def jsonBuilder = new JsonBuilder()
- def root = jsonBuilder {
- // Generate 2049 fields
- (1..2049).each { fieldNumber ->
- "field$fieldNumber" fieldNumber
- }
+ def jsonBuilder = new JsonBuilder()
+ def root = jsonBuilder {
+ // Generate 4097 fields
+ (1..4097).each { fieldNumber ->
+ "field$fieldNumber" fieldNumber
}
+ }
- String jsonString = jsonBuilder.toPrettyString()
+ String jsonString = jsonBuilder.toPrettyString()
+ test {
sql """insert into ${table_name} values (1, '$jsonString')"""
- } catch(Exception ex) {
- logger.info("""INSERT INTO ${table_name} failed: """ + ex)
- assertTrue(ex.toString().contains("Reached max column"));
- } finally {
+ exception("Reached max column size limit")
}
sql """insert into ${table_name} values (1, '{"a" : 1, "b" : 2, "c" :
3}')"""
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]