This is an automated email from the ASF dual-hosted git repository. kxiao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new 9bf810a8022 [improve](http action) add http interface to calculate the crc of all files in tablet (#34915) 9bf810a8022 is described below commit 9bf810a80228da1f62c874728c8829c77c30791e Author: Sun Chenyang <csun5...@gmail.com> AuthorDate: Wed May 22 09:21:30 2024 +0800 [improve](http action) add http interface to calculate the crc of all files in tablet (#34915) --- be/src/http/action/calc_file_crc_action.cpp | 134 +++++++++++++++++++++ be/src/http/action/calc_file_crc_action.h | 53 ++++++++ be/src/olap/rowset/beta_rowset.cpp | 72 +++++++++++ be/src/olap/rowset/beta_rowset.h | 2 + be/src/olap/tablet.cpp | 32 +++++ be/src/olap/tablet.h | 2 + be/src/service/http_service.cpp | 5 + .../test_single_replica_compaction.groovy | 20 +++ .../test_calc_crc_fault_injection.groovy | 133 ++++++++++++++++++++ 9 files changed, 453 insertions(+) diff --git a/be/src/http/action/calc_file_crc_action.cpp b/be/src/http/action/calc_file_crc_action.cpp new file mode 100644 index 00000000000..c713184ddfd --- /dev/null +++ b/be/src/http/action/calc_file_crc_action.cpp @@ -0,0 +1,134 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "http/action/calc_file_crc_action.h" + +#include <rapidjson/prettywriter.h> +#include <rapidjson/rapidjson.h> +#include <rapidjson/stringbuffer.h> + +#include <algorithm> +#include <exception> +#include <string> + +#include "common/logging.h" +#include "common/status.h" +#include "http/http_channel.h" +#include "http/http_headers.h" +#include "http/http_request.h" +#include "http/http_status.h" +#include "olap/storage_engine.h" +#include "olap/tablet_manager.h" +#include "util/stopwatch.hpp" + +namespace doris { +using namespace ErrorCode; + +CalcFileCrcAction::CalcFileCrcAction(ExecEnv* exec_env, StorageEngine& engine, + TPrivilegeHier::type hier, TPrivilegeType::type ptype) + : HttpHandlerWithAuth(exec_env, hier, ptype), _engine(engine) {} + +// calculate the crc value of the files in the tablet +Status CalcFileCrcAction::_handle_calc_crc(HttpRequest* req, uint32_t* crc_value, + int64_t* start_version, int64_t* end_version, + int32_t* rowset_count, int64_t* file_count) { + uint64_t tablet_id = 0; + const auto& req_tablet_id = req->param(TABLET_ID_KEY); + if (req_tablet_id.empty()) { + return Status::InternalError("tablet id can not be empty!"); + } + + try { + tablet_id = std::stoull(req_tablet_id); + } catch (const std::exception& e) { + return Status::InternalError("convert tablet id or failed, {}", e.what()); + } + + TabletSharedPtr tablet = _engine.tablet_manager()->get_tablet(tablet_id); + if (tablet == nullptr) { + return Status::NotFound("Tablet not found. tablet_id={}", tablet_id); + } + + const auto& req_start_version = req->param(PARAM_START_VERSION); + const auto& req_end_version = req->param(PARAM_END_VERSION); + + *start_version = 0; + *end_version = tablet->max_version().second; + + if (!req_start_version.empty()) { + try { + *start_version = std::stoll(req_start_version); + } catch (const std::exception& e) { + return Status::InternalError("convert start version failed, {}", e.what()); + } + } + if (!req_end_version.empty()) { + try { + *end_version = + std::min(*end_version, static_cast<int64_t>(std::stoll(req_end_version))); + } catch (const std::exception& e) { + return Status::InternalError("convert end version failed, {}", e.what()); + } + } + + auto st = tablet->calc_local_file_crc(crc_value, *start_version, *end_version, rowset_count, + file_count); + if (!st.ok()) { + return st; + } + return Status::OK(); +} + +void CalcFileCrcAction::handle(HttpRequest* req) { + uint32_t crc_value = 0; + int64_t start_version = 0; + int64_t end_version = 0; + int32_t rowset_count = 0; + int64_t file_count = 0; + + MonotonicStopWatch timer; + timer.start(); + Status st = _handle_calc_crc(req, &crc_value, &start_version, &end_version, &rowset_count, + &file_count); + timer.stop(); + LOG(INFO) << "Calc tablet file crc finished, status = " << st << ", crc_value = " << crc_value + << ", use time = " << timer.elapsed_time() / 1000000 << "ms"; + + if (!st.ok()) { + HttpChannel::send_reply(req, HttpStatus::OK, st.to_json()); + } else { + rapidjson::StringBuffer s; + rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(s); + writer.StartObject(); + writer.Key("crc_value"); + writer.String(std::to_string(crc_value).data()); + writer.Key("used_time_ms"); + writer.String(std::to_string(timer.elapsed_time() / 1000000).data()); + writer.Key("start_version"); + writer.String(std::to_string(start_version).data()); + writer.Key("end_version"); + writer.String(std::to_string(end_version).data()); + writer.Key("rowset_count"); + writer.String(std::to_string(rowset_count).data()); + writer.Key("file_count"); + writer.String(std::to_string(file_count).data()); + writer.EndObject(); + HttpChannel::send_reply(req, HttpStatus::OK, s.GetString()); + } +} + +} // end namespace doris diff --git a/be/src/http/action/calc_file_crc_action.h b/be/src/http/action/calc_file_crc_action.h new file mode 100644 index 00000000000..2c0d19f0ca0 --- /dev/null +++ b/be/src/http/action/calc_file_crc_action.h @@ -0,0 +1,53 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include <stdint.h> + +#include <string> + +#include "common/status.h" +#include "http/http_handler_with_auth.h" + +namespace doris { +class HttpRequest; +class StorageEngine; +class ExecEnv; + +const std::string PARAM_START_VERSION = "start_version"; +const std::string PARAM_END_VERSION = "end_version"; + +// This action is used to calculate the crc value of the files in the tablet. +class CalcFileCrcAction : public HttpHandlerWithAuth { +public: + CalcFileCrcAction(ExecEnv* exec_env, StorageEngine& engine, TPrivilegeHier::type hier, + TPrivilegeType::type ptype); + + ~CalcFileCrcAction() override = default; + + void handle(HttpRequest* req) override; + +private: + Status _handle_calc_crc(HttpRequest* req, uint32_t* crc_value, int64_t* start_version, + int64_t* end_version, int32_t* rowset_count, int64_t* file_count); + +private: + StorageEngine& _engine; +}; + +} // end namespace doris diff --git a/be/src/olap/rowset/beta_rowset.cpp b/be/src/olap/rowset/beta_rowset.cpp index a1ac0d4539e..f53e88718b0 100644 --- a/be/src/olap/rowset/beta_rowset.cpp +++ b/be/src/olap/rowset/beta_rowset.cpp @@ -42,6 +42,7 @@ #include "olap/rowset/segment_v2/inverted_index_desc.h" #include "olap/tablet_schema.h" #include "olap/utils.h" +#include "util/crc32c.h" #include "util/debug_points.h" #include "util/doris_metrics.h" @@ -629,4 +630,75 @@ Status BetaRowset::add_to_binlog() { return Status::OK(); } +Status BetaRowset::calc_local_file_crc(uint32_t* crc_value, int64_t* file_count) { + DCHECK(is_local()); + auto fs = _rowset_meta->fs(); + if (!fs) { + return Status::Error<INIT_FAILED>("get fs failed"); + } + if (fs->type() != io::FileSystemType::LOCAL) { + return Status::InternalError("should be local file system"); + } + + if (num_segments() < 1) { + *crc_value = 0x92a8fc17; // magic code from crc32c table + return Status::OK(); + } + + // 1. pick up all the files including dat file and idx file + std::vector<io::Path> local_paths; + for (int i = 0; i < num_segments(); ++i) { + auto local_seg_path = segment_file_path(i); + local_paths.emplace_back(local_seg_path); + if (_schema->get_inverted_index_storage_format() != InvertedIndexStorageFormatPB::V1) { + if (_schema->has_inverted_index()) { + std::string local_inverted_index_file = + InvertedIndexDescriptor::get_index_file_name(local_seg_path); + local_paths.emplace_back(local_inverted_index_file); + } + } else { + for (auto& column : _schema->columns()) { + const TabletIndex* index_meta = _schema->get_inverted_index(*column); + if (index_meta) { + std::string local_inverted_index_file = + InvertedIndexDescriptor::get_index_file_name( + local_seg_path, index_meta->index_id(), + index_meta->get_index_suffix()); + local_paths.emplace_back(local_inverted_index_file); + } + } + } + } + + // 2. calculate the md5sum of each file + auto* local_fs = static_cast<io::LocalFileSystem*>(fs.get()); + DCHECK(local_paths.size() > 0); + std::vector<std::string> all_file_md5; + all_file_md5.reserve(local_paths.size()); + for (const auto& file_path : local_paths) { + DBUG_EXECUTE_IF("fault_inject::BetaRowset::calc_local_file_crc", { + return Status::Error<OS_ERROR>("fault_inject calc_local_file_crc error"); + }); + std::string file_md5sum; + auto status = local_fs->md5sum(file_path, &file_md5sum); + if (!status.ok()) { + return status; + } + VLOG_CRITICAL << fmt::format("calc file_md5sum finished. file_path={}, md5sum={}", + file_path.string(), file_md5sum); + all_file_md5.emplace_back(std::move(file_md5sum)); + } + std::sort(all_file_md5.begin(), all_file_md5.end()); + + // 3. calculate the crc_value based on all_file_md5 + DCHECK(local_paths.size() == all_file_md5.size()); + *crc_value = 0; + *file_count = local_paths.size(); + for (int i = 0; i < all_file_md5.size(); i++) { + *crc_value = crc32c::Extend(*crc_value, all_file_md5[i].data(), all_file_md5[i].size()); + } + + return Status::OK(); +} + } // namespace doris diff --git a/be/src/olap/rowset/beta_rowset.h b/be/src/olap/rowset/beta_rowset.h index 0fbfbda19d3..b559183ebbb 100644 --- a/be/src/olap/rowset/beta_rowset.h +++ b/be/src/olap/rowset/beta_rowset.h @@ -99,6 +99,8 @@ public: [[nodiscard]] virtual Status add_to_binlog() override; + Status calc_local_file_crc(uint32_t* crc_value, int64_t* file_count); + protected: BetaRowset(const TabletSchemaSPtr& schema, const std::string& tablet_path, const RowsetMetaSharedPtr& rowset_meta); diff --git a/be/src/olap/tablet.cpp b/be/src/olap/tablet.cpp index cb1e210cf68..ec4b6a26013 100644 --- a/be/src/olap/tablet.cpp +++ b/be/src/olap/tablet.cpp @@ -109,6 +109,7 @@ #include "service/point_query_executor.h" #include "tablet.h" #include "util/bvar_helper.h" +#include "util/crc32c.h" #include "util/debug_points.h" #include "util/defer_op.h" #include "util/doris_metrics.h" @@ -2629,4 +2630,35 @@ void Tablet::clear_cache() { recycle_segment_cache(stale_rowset_map()); } +Status Tablet::calc_local_file_crc(uint32_t* crc_value, int64_t start_version, int64_t end_version, + int32_t* rowset_count, int64_t* file_count) { + Version v(start_version, end_version); + std::vector<RowsetSharedPtr> rowsets; + traverse_rowsets([&rowsets, &v](const auto& rs) { + // get local rowsets + if (rs->is_local() && v.contains(rs->version())) { + rowsets.emplace_back(rs); + } + }); + std::sort(rowsets.begin(), rowsets.end(), Rowset::comparator); + *rowset_count = rowsets.size(); + + *crc_value = 0; + *file_count = 0; + for (const auto& rs : rowsets) { + uint32_t rs_crc_value; + int64_t rs_file_count = 0; + auto rowset = std::static_pointer_cast<BetaRowset>(rs); + auto st = rowset->calc_local_file_crc(&rs_crc_value, &rs_file_count); + if (!st.ok()) { + return st; + } + // crc_value is calculated based on the crc_value of each rowset. + *crc_value = crc32c::Extend(*crc_value, reinterpret_cast<const char*>(&rs_crc_value), + sizeof(rs_crc_value)); + *file_count += rs_file_count; + } + return Status::OK(); +} + } // namespace doris diff --git a/be/src/olap/tablet.h b/be/src/olap/tablet.h index 4f52927df75..8735b07653c 100644 --- a/be/src/olap/tablet.h +++ b/be/src/olap/tablet.h @@ -476,6 +476,8 @@ public: } inline bool is_full_compaction_running() const { return _is_full_compaction_running; } void clear_cache() override; + Status calc_local_file_crc(uint32_t* crc_value, int64_t start_version, int64_t end_version, + int32_t* rowset_count, int64_t* file_count); private: Status _init_once_action(); diff --git a/be/src/service/http_service.cpp b/be/src/service/http_service.cpp index 2ebbf5e5b69..8788877ec8d 100644 --- a/be/src/service/http_service.cpp +++ b/be/src/service/http_service.cpp @@ -30,6 +30,7 @@ #include "common/status.h" #include "http/action/adjust_log_level.h" #include "http/action/adjust_tracing_dump.h" +#include "http/action/calc_file_crc_action.h" #include "http/action/check_rpc_channel_action.h" #include "http/action/check_tablet_segment_action.h" #include "http/action/checksum_action.h" @@ -344,6 +345,10 @@ void HttpService::register_local_handler(StorageEngine& engine) { ReportAction* report_disk_action = _pool.add(new ReportAction( _env, TPrivilegeHier::GLOBAL, TPrivilegeType::ADMIN, "REPORT_DISK_STATE")); _ev_http_server->register_handler(HttpMethod::GET, "/api/report/disk", report_disk_action); + + CalcFileCrcAction* calc_crc_action = _pool.add( + new CalcFileCrcAction(_env, engine, TPrivilegeHier::GLOBAL, TPrivilegeType::ADMIN)); + _ev_http_server->register_handler(HttpMethod::GET, "/api/calc_crc", calc_crc_action); } void HttpService::register_cloud_handler(CloudStorageEngine& engine) { diff --git a/regression-test/suites/compaction/test_single_replica_compaction.groovy b/regression-test/suites/compaction/test_single_replica_compaction.groovy index 391afcfc5fa..c9a9f65ee31 100644 --- a/regression-test/suites/compaction/test_single_replica_compaction.groovy +++ b/regression-test/suites/compaction/test_single_replica_compaction.groovy @@ -31,6 +31,10 @@ suite("test_single_replica_compaction", "p2") { } } + def calc_file_crc_on_tablet = { ip, port, tablet -> + return curl("GET", String.format("http://%s:%s/api/calc_crc?tablet_id=%s", ip, port, tablet)) + } + boolean disableAutoCompaction = true boolean has_update_be_config = false try { @@ -197,6 +201,21 @@ suite("test_single_replica_compaction", "p2") { } } + def checkTabletFileCrc = { + def (master_code, master_out, master_err) = calc_file_crc_on_tablet(backendId_to_backendIP[master_backend_id], backendId_to_backendHttpPort[master_backend_id], tablet_id) + logger.info("Run calc_file_crc_on_tablet: ip=" + backendId_to_backendIP[master_backend_id] + " code=" + master_code + ", out=" + master_out + ", err=" + master_err) + + for (String backend: follower_backend_id) { + def (follower_code, follower_out, follower_err) = calc_file_crc_on_tablet(backendId_to_backendIP[backend], backendId_to_backendHttpPort[backend], tablet_id) + logger.info("Run calc_file_crc_on_tablet: ip=" + backendId_to_backendIP[backend] + " code=" + follower_code + ", out=" + follower_out + ", err=" + follower_err) + assertTrue(parseJson(follower_out.trim()).crc_value == parseJson(master_out.trim()).crc_value) + assertTrue(parseJson(follower_out.trim()).start_version == parseJson(master_out.trim()).start_version) + assertTrue(parseJson(follower_out.trim()).end_version == parseJson(master_out.trim()).end_version) + assertTrue(parseJson(follower_out.trim()).file_count == parseJson(master_out.trim()).file_count) + assertTrue(parseJson(follower_out.trim()).rowset_count == parseJson(master_out.trim()).rowset_count) + } + } + sql """ INSERT INTO ${tableName} VALUES (1, "a", 100); """ sql """ INSERT INTO ${tableName} VALUES (1, "b", 100); """ sql """ INSERT INTO ${tableName} VALUES (2, "a", 100); """ @@ -252,6 +271,7 @@ suite("test_single_replica_compaction", "p2") { // check rowsets checkCompactionResult.call() + checkTabletFileCrc.call() qt_sql """ select * from ${tableName} order by id diff --git a/regression-test/suites/fault_injection_p0/test_calc_crc_fault_injection.groovy b/regression-test/suites/fault_injection_p0/test_calc_crc_fault_injection.groovy new file mode 100644 index 00000000000..acddc368229 --- /dev/null +++ b/regression-test/suites/fault_injection_p0/test_calc_crc_fault_injection.groovy @@ -0,0 +1,133 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import org.codehaus.groovy.runtime.IOGroovyMethods + +suite("test_calc_crc") { + + if (isCloudMode()) { + return + } + + def calc_file_crc_on_tablet = { ip, port, tablet -> + return curl("GET", String.format("http://%s:%s/api/calc_crc?tablet_id=%s", ip, port, tablet)) + } + def calc_file_crc_on_tablet_with_start = { ip, port, tablet, start-> + return curl("GET", String.format("http://%s:%s/api/calc_crc?tablet_id=%s&start_version=%s", ip, port, tablet, start)) + } + def calc_file_crc_on_tablet_with_end = { ip, port, tablet, end-> + return curl("GET", String.format("http://%s:%s/api/calc_crc?tablet_id=%s&end_version=%s", ip, port, tablet, end)) + } + def calc_file_crc_on_tablet_with_start_end = { ip, port, tablet, start, end-> + return curl("GET", String.format("http://%s:%s/api/calc_crc?tablet_id=%s&start_version=%s&end_version=%s", ip, port, tablet, start, end)) + } + def backendId_to_backendIP = [:] + def backendId_to_backendHttpPort = [:] + getBackendIpHttpPort(backendId_to_backendIP, backendId_to_backendHttpPort); + + def tableName = "test_clac_crc" + + sql "DROP TABLE IF EXISTS ${tableName}" + sql """ + CREATE TABLE ${tableName} ( + `id` int(11) NULL, + `name` varchar(255) NULL, + `score` int(11) NULL, + index index_name (name) using inverted, + index index_score (score) using inverted + ) ENGINE=OLAP + DUPLICATE KEY(`id`) + COMMENT 'OLAP' + DISTRIBUTED BY HASH(`id`) BUCKETS 1 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1", + "disable_auto_compaction" = "true" + ); + """ + sql """ INSERT INTO ${tableName} VALUES (1, "andy", 100); """ + sql """ INSERT INTO ${tableName} VALUES (1, "bason", 99); """ + sql """ INSERT INTO ${tableName} VALUES (2, "andy", 100); """ + sql """ INSERT INTO ${tableName} VALUES (2, "bason", 99); """ + sql """ INSERT INTO ${tableName} VALUES (3, "andy", 100); """ + sql """ INSERT INTO ${tableName} VALUES (3, "bason", 99); """ + + tablets = sql_return_maparray """ show tablets from ${tableName}; """ + String tablet_id = tablets[0].TabletId + String backend_id = tablets[0].BackendId + String ip = backendId_to_backendIP.get(backend_id) + String port = backendId_to_backendHttpPort.get(backend_id) + def (code_0, out_0, err_0) = calc_file_crc_on_tablet(ip, port, tablet_id) + logger.info("Run calc_file_crc_on_tablet: code=" + code_0 + ", out=" + out_0 + ", err=" + err_0) + assertTrue(code_0 == 0) + assertTrue(out_0.contains("crc_value")) + assertTrue(out_0.contains("used_time_ms")) + assertEquals("0", parseJson(out_0.trim()).start_version) + assertEquals("7", parseJson(out_0.trim()).end_version) + assertEquals("7", parseJson(out_0.trim()).rowset_count) + assertEquals("18", parseJson(out_0.trim()).file_count) + + try { + GetDebugPoint().enableDebugPointForAllBEs("fault_inject::BetaRowset::calc_local_file_crc") + def (code_1, out_1, err_1) = calc_file_crc_on_tablet(ip, port, tablet_id) + logger.info("Run calc_file_crc_on_tablet: code=" + code_1 + ", out=" + out_1 + ", err=" + err_1) + assertTrue(out_1.contains("fault_inject calc_local_file_crc error")) + } finally { + GetDebugPoint().disableDebugPointForAllBEs("fault_inject::BetaRowset::calc_local_file_crc") + } + + def (code_2, out_2, err_2) = calc_file_crc_on_tablet_with_start(ip, port, tablet_id, 0) + logger.info("Run calc_file_crc_on_tablet: code=" + code_2 + ", out=" + out_2 + ", err=" + err_2) + assertTrue(code_2 == 0) + assertEquals("0", parseJson(out_2.trim()).start_version) + assertEquals("7", parseJson(out_2.trim()).end_version) + assertEquals("7", parseJson(out_2.trim()).rowset_count) + assertEquals("18", parseJson(out_2.trim()).file_count) + assertTrue(parseJson(out_0.trim()).crc_value == parseJson(out_2.trim()).crc_value) + + def (code_3, out_3, err_3) = calc_file_crc_on_tablet_with_end(ip, port, tablet_id, 7) + logger.info("Run calc_file_crc_on_tablet: code=" + code_3 + ", out=" + out_3 + ", err=" + err_3) + assertTrue(code_3 == 0) + assertEquals("0", parseJson(out_3.trim()).start_version) + assertEquals("7", parseJson(out_3.trim()).end_version) + assertEquals("7", parseJson(out_3.trim()).rowset_count) + assertEquals("18", parseJson(out_3.trim()).file_count) + assertTrue(parseJson(out_2.trim()).crc_value == parseJson(out_3.trim()).crc_value) + + def (code_4, out_4, err_4) = calc_file_crc_on_tablet_with_start_end(ip, port, tablet_id, 3, 6) + logger.info("Run calc_file_crc_on_tablet: code=" + code_4 + ", out=" + out_3 + ", err=" + err_4) + assertTrue(out_4.contains("crc_value")) + assertTrue(out_4.contains("used_time_ms")) + assertEquals("3", parseJson(out_4.trim()).start_version) + assertEquals("6", parseJson(out_4.trim()).end_version) + assertEquals("4", parseJson(out_4.trim()).rowset_count) + assertEquals("12", parseJson(out_4.trim()).file_count) + + def (code_5, out_5, err_5) = calc_file_crc_on_tablet_with_start_end(ip, port, tablet_id, 5, 9) + logger.info("Run calc_file_crc_on_tablet: code=" + code_5 + ", out=" + out_5 + ", err=" + err_5) + assertTrue(out_5.contains("crc_value")) + assertTrue(out_5.contains("used_time_ms")) + assertEquals("5", parseJson(out_5.trim()).start_version) + assertEquals("7", parseJson(out_5.trim()).end_version) + assertEquals("3", parseJson(out_5.trim()).rowset_count) + assertEquals("9", parseJson(out_5.trim()).file_count) + + def (code_6, out_6, err_6) = calc_file_crc_on_tablet(ip, port, 123) + logger.info("Run calc_file_crc_on_tablet: code=" + code_6 + ", out=" + out_6 + ", err=" + err_6) + assertTrue(out_6.contains("Tablet not found.")) + + sql "DROP TABLE IF EXISTS ${tableName}" +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org