From 8c7cce4fc2fe455aa64f342fffc50dd4f413932d Mon Sep 17 00:00:00 2001 From: Leonid Krugliak Date: Fri, 8 May 2026 00:14:06 +0300 Subject: [PATCH 1/5] Add RESULT_COLLECTOR profiling metric for result materialization phase Instruments the executor.GetResult() call in FetchResultInternal with a new phase timing metric to measure time spent collecting and materializing query results. This helps diagnose latency gaps between DuckDB's reported execution time and actual wall-clock time. Co-Authored-By: Claude Opus 4.6 --- src/common/enum_util.cpp | 7 +- src/common/enums/metric_type.cpp | 5 + src/common/enums/metric_type.json | 8 ++ .../duckdb/common/enums/metric_type.hpp | 1 + src/main/client_context.cpp | 3 + src/main/profiling_utils.cpp | 2 + .../test_all_profiling_settings.test | 1 + ...est_custom_profiling_result_collector.test | 121 ++++++++++++++++++ .../test_custom_profiling_using_groups.test | 8 +- 9 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 test/sql/pragma/profiling/test_custom_profiling_result_collector.test diff --git a/src/common/enum_util.cpp b/src/common/enum_util.cpp index fa0058720bb4..0c822bf8dfd5 100644 --- a/src/common/enum_util.cpp +++ b/src/common/enum_util.cpp @@ -3285,19 +3285,20 @@ const StringUtil::EnumStringLiteral *GetMetricTypeValues() { { static_cast(MetricType::PHYSICAL_PLANNER_CREATE_PLAN), "PHYSICAL_PLANNER_CREATE_PLAN" }, { static_cast(MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES), "PHYSICAL_PLANNER_RESOLVE_TYPES" }, { static_cast(MetricType::PLANNER), "PLANNER" }, - { static_cast(MetricType::PLANNER_BINDING), "PLANNER_BINDING" } + { static_cast(MetricType::PLANNER_BINDING), "PLANNER_BINDING" }, + { static_cast(MetricType::RESULT_COLLECTOR), "RESULT_COLLECTOR" } }; return values; } template<> const char* EnumUtil::ToChars(MetricType value) { - return StringUtil::EnumToString(GetMetricTypeValues(), 71, "MetricType", static_cast(value)); + return StringUtil::EnumToString(GetMetricTypeValues(), 72, "MetricType", static_cast(value)); } template<> MetricType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetMetricTypeValues(), 71, "MetricType", value)); + return static_cast(StringUtil::StringToEnum(GetMetricTypeValues(), 72, "MetricType", value)); } const StringUtil::EnumStringLiteral *GetMultiFileColumnMappingModeValues() { diff --git a/src/common/enums/metric_type.cpp b/src/common/enums/metric_type.cpp index c1038cbe03a7..2f2df8090f5b 100644 --- a/src/common/enums/metric_type.cpp +++ b/src/common/enums/metric_type.cpp @@ -69,6 +69,7 @@ profiler_settings_t MetricsUtils::GetAllMetrics() { MetricType::PLANNER, MetricType::PLANNER_BINDING, MetricType::QUERY_NAME, + MetricType::RESULT_COLLECTOR, MetricType::RESULT_SET_SIZE, MetricType::ROWS_RETURNED, MetricType::SYSTEM_PEAK_BUFFER_MEMORY, @@ -324,6 +325,7 @@ profiler_settings_t MetricsUtils::GetPhaseTimingMetrics() { MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES, MetricType::PLANNER, MetricType::PLANNER_BINDING, + MetricType::RESULT_COLLECTOR, }; } @@ -338,6 +340,7 @@ bool MetricsUtils::IsPhaseTimingMetric(MetricType type) { case MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES: case MetricType::PLANNER: case MetricType::PLANNER_BINDING: + case MetricType::RESULT_COLLECTOR: return true; default: return false; @@ -397,6 +400,7 @@ profiler_settings_t MetricsUtils::GetRootScopeMetrics() { MetricType::PLANNER, MetricType::PLANNER_BINDING, MetricType::QUERY_NAME, + MetricType::RESULT_COLLECTOR, MetricType::ROWS_RETURNED, MetricType::TOTAL_BYTES_READ, MetricType::TOTAL_BYTES_WRITTEN, @@ -460,6 +464,7 @@ bool MetricsUtils::IsRootScopeMetric(MetricType type) { case MetricType::PLANNER: case MetricType::PLANNER_BINDING: case MetricType::QUERY_NAME: + case MetricType::RESULT_COLLECTOR: case MetricType::ROWS_RETURNED: case MetricType::TOTAL_BYTES_READ: case MetricType::TOTAL_BYTES_WRITTEN: diff --git a/src/common/enums/metric_type.json b/src/common/enums/metric_type.json index 8db09b41d729..fee1ce3ec904 100644 --- a/src/common/enums/metric_type.json +++ b/src/common/enums/metric_type.json @@ -292,6 +292,14 @@ "type": "double", "unit": "milliseconds", "query_root": true + }, + { + "name": "RESULT_COLLECTOR", + "enum_value": 100, + "description": "The time spent collecting and materializing the query result.", + "type": "double", + "unit": "milliseconds", + "query_root": true } ] }, diff --git a/src/include/duckdb/common/enums/metric_type.hpp b/src/include/duckdb/common/enums/metric_type.hpp index 34a6062569e1..80afb137c36b 100644 --- a/src/include/duckdb/common/enums/metric_type.hpp +++ b/src/include/duckdb/common/enums/metric_type.hpp @@ -107,6 +107,7 @@ enum class MetricType : uint8_t { PHYSICAL_PLANNER_RESOLVE_TYPES = 24, PLANNER = 20, PLANNER_BINDING = 21, + RESULT_COLLECTOR = 100, }; struct MetricTypeHashFunction { diff --git a/src/main/client_context.cpp b/src/main/client_context.cpp index 109dae23f86c..74691792e584 100644 --- a/src/main/client_context.cpp +++ b/src/main/client_context.cpp @@ -397,7 +397,10 @@ unique_ptr ClientContext::FetchResultInternal(ClientContextLock &lo unique_ptr result; D_ASSERT(executor.HasResultCollector()); // we have a result collector - fetch the result directly from the result collector + auto &profiler = QueryProfiler::Get(*this); + profiler.StartPhase(MetricType::RESULT_COLLECTOR); result = executor.GetResult(); + profiler.EndPhase(); if (!create_stream_result) { CleanupInternal(lock, result.get(), false); } else { diff --git a/src/main/profiling_utils.cpp b/src/main/profiling_utils.cpp index 94f32c65a60f..c9a3207dd441 100644 --- a/src/main/profiling_utils.cpp +++ b/src/main/profiling_utils.cpp @@ -105,6 +105,7 @@ void ProfilingUtils::SetMetricToDefault(profiler_metrics_t &metrics, const Metri case MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES: case MetricType::PLANNER: case MetricType::PLANNER_BINDING: + case MetricType::RESULT_COLLECTOR: case MetricType::WAITING_TO_ATTACH_LATENCY: case MetricType::WRITE_TO_WAL_LATENCY: metrics[type] = Value::CreateValue(0.0); @@ -193,6 +194,7 @@ void ProfilingUtils::MetricToJson(duckdb_yyjson::yyjson_mut_doc *doc, duckdb_yyj case MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES: case MetricType::PLANNER: case MetricType::PLANNER_BINDING: + case MetricType::RESULT_COLLECTOR: case MetricType::WAITING_TO_ATTACH_LATENCY: case MetricType::WRITE_TO_WAL_LATENCY: yyjson_mut_obj_add_real(doc, dest, key_ptr, metrics[type].GetValue()); diff --git a/test/sql/pragma/profiling/test_all_profiling_settings.test b/test/sql/pragma/profiling/test_all_profiling_settings.test index 3103ea00cc9b..8231e6224299 100644 --- a/test/sql/pragma/profiling/test_all_profiling_settings.test +++ b/test/sql/pragma/profiling/test_all_profiling_settings.test @@ -90,6 +90,7 @@ SELECT unnest(res) FROM ( "PLANNER": "true" "PLANNER_BINDING": "true" "QUERY_NAME": "true" +"RESULT_COLLECTOR": "true" "RESULT_SET_SIZE": "true" "ROWS_RETURNED": "true" "SYSTEM_PEAK_BUFFER_MEMORY": "true" diff --git a/test/sql/pragma/profiling/test_custom_profiling_result_collector.test b/test/sql/pragma/profiling/test_custom_profiling_result_collector.test new file mode 100644 index 000000000000..097f1832477c --- /dev/null +++ b/test/sql/pragma/profiling/test_custom_profiling_result_collector.test @@ -0,0 +1,121 @@ +# name: test/sql/pragma/profiling/test_custom_profiling_result_collector.test +# description: Test RESULT_COLLECTOR metric. +# group: [profiling] + +require json + +statement ok +PRAGMA enable_profiling = 'json'; + +statement ok +PRAGMA profiling_output = '{TEST_DIR}/profiling_output.json'; + +statement ok +PRAGMA custom_profiling_settings='{"RESULT_COLLECTOR": "true"}'; + +statement ok +CREATE OR REPLACE TABLE test AS SELECT range AS id, hash(range) AS data FROM range(10000); + +statement ok +PRAGMA disable_profiling; + +statement ok +CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; + +# Verify that result_collector timing is present and non-negative +query I +SELECT + CASE WHEN result_collector >= 0 THEN 'true' + ELSE 'false' END +FROM metrics_output; +---- +true + +# Test with a SELECT query that materializes results +statement ok +PRAGMA enable_profiling = 'json'; + +statement ok +PRAGMA profiling_output = '{TEST_DIR}/profiling_output.json'; + +statement ok +PRAGMA custom_profiling_settings='{"RESULT_COLLECTOR": "true"}'; + +statement ok +SELECT * FROM test ORDER BY id; + +statement ok +PRAGMA disable_profiling; + +statement ok +CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; + +# Verify that result_collector is tracked +query I +SELECT + CASE WHEN result_collector >= 0 THEN 'true' + ELSE 'false' END +FROM metrics_output; +---- +true + +# Verify that result_collector only appears at root level, not in children +statement ok +PRAGMA enable_profiling = 'json'; + +statement ok +PRAGMA profiling_output = '{TEST_DIR}/profiling_output.json'; + +statement ok +PRAGMA custom_profiling_settings='{"RESULT_COLLECTOR": "true", "OPERATOR_NAME": "true", "OPERATOR_TYPE": "true"}'; + +statement ok +SELECT * FROM test WHERE id < 1000 ORDER BY id; + +statement ok +PRAGMA disable_profiling; + +statement ok +CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; + +# Check that root has result_collector +query I +SELECT + CASE WHEN result_collector >= 0 THEN 'true' + ELSE 'false' END +FROM metrics_output; +---- +true + +# Verify that children nodes don't have result_collector +statement ok +CREATE OR REPLACE TABLE metrics_children AS SELECT unnest(children, recursive := true) FROM metrics_output; + +statement error +SELECT result_collector FROM metrics_children; +---- +Binder Error + +# Test disabling the metric removes it from output +statement ok +PRAGMA enable_profiling = 'json'; + +statement ok +PRAGMA profiling_output = '{TEST_DIR}/profiling_output.json'; + +statement ok +PRAGMA custom_profiling_settings='{"RESULT_COLLECTOR": "false"}'; + +statement ok +SELECT * FROM test LIMIT 100; + +statement ok +PRAGMA disable_profiling; + +statement ok +CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; + +statement error +SELECT result_collector FROM metrics_output; +---- +Binder Error diff --git a/test/sql/pragma/profiling/test_custom_profiling_using_groups.test b/test/sql/pragma/profiling/test_custom_profiling_using_groups.test index 41dae235a1eb..05cbbb33c920 100644 --- a/test/sql/pragma/profiling/test_custom_profiling_using_groups.test +++ b/test/sql/pragma/profiling/test_custom_profiling_using_groups.test @@ -90,6 +90,7 @@ SELECT unnest(res) FROM ( "PLANNER": "true" "PLANNER_BINDING": "true" "QUERY_NAME": "true" +"RESULT_COLLECTOR": "true" "RESULT_SET_SIZE": "true" "ROWS_RETURNED": "true" "SYSTEM_PEAK_BUFFER_MEMORY": "true" @@ -161,6 +162,7 @@ SELECT ALL_OPTIMIZERS, PLANNER, PLANNER_BINDING, QUERY_NAME, + RESULT_COLLECTOR, RESULT_SET_SIZE, ROWS_RETURNED, SYSTEM_PEAK_BUFFER_MEMORY, @@ -588,6 +590,7 @@ SELECT unnest(res) FROM ( "PHYSICAL_PLANNER_RESOLVE_TYPES": "true" "PLANNER": "true" "PLANNER_BINDING": "true" +"RESULT_COLLECTOR": "true" statement ok CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; @@ -637,7 +640,8 @@ SELECT ALL_OPTIMIZERS, PHYSICAL_PLANNER_CREATE_PLAN, PHYSICAL_PLANNER_RESOLVE_TYPES, PLANNER, - PLANNER_BINDING + PLANNER_BINDING, + RESULT_COLLECTOR FROM metrics_output; statement ok @@ -907,6 +911,7 @@ SELECT unnest(res) FROM ( "PHYSICAL_PLANNER_RESOLVE_TYPES": "true" "PLANNER": "true" "PLANNER_BINDING": "true" +"RESULT_COLLECTOR": "true" "SYSTEM_PEAK_BUFFER_MEMORY": "true" "SYSTEM_PEAK_TEMP_DIR_SIZE": "true" "TOTAL_BYTES_READ": "true" @@ -970,6 +975,7 @@ SELECT ALL_OPTIMIZERS, PHYSICAL_PLANNER_RESOLVE_TYPES, PLANNER, PLANNER_BINDING, + RESULT_COLLECTOR, SYSTEM_PEAK_BUFFER_MEMORY, SYSTEM_PEAK_TEMP_DIR_SIZE, TOTAL_BYTES_READ, From 2e8b03eaae651ecd5f8c8625e401a91c0598c787 Mon Sep 17 00:00:00 2001 From: Leonid Krugliak Date: Fri, 8 May 2026 01:24:19 +0300 Subject: [PATCH 2/5] Fix MetricType count to 73 after merge with main Co-Authored-By: Claude Opus 4.6 --- src/common/enum_util.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/enum_util.cpp b/src/common/enum_util.cpp index f803d862d292..6900db695cc4 100644 --- a/src/common/enum_util.cpp +++ b/src/common/enum_util.cpp @@ -3382,12 +3382,12 @@ const StringUtil::EnumStringLiteral *GetMetricTypeValues() { template<> const char* EnumUtil::ToChars(MetricType value) { - return StringUtil::EnumToString(GetMetricTypeValues(), 72, "MetricType", static_cast(value)); + return StringUtil::EnumToString(GetMetricTypeValues(), 73, "MetricType", static_cast(value)); } template<> MetricType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetMetricTypeValues(), 72, "MetricType", value)); + return static_cast(StringUtil::StringToEnum(GetMetricTypeValues(), 73, "MetricType", value)); } const StringUtil::EnumStringLiteral *GetMonotonicityValues() { From b5216afc77a3a3573ce89c58006f5de48a672b77 Mon Sep 17 00:00:00 2001 From: Leonid Krugliak Date: Fri, 8 May 2026 14:19:27 +0300 Subject: [PATCH 3/5] Add EXECUTOR_INITIALIZE and CLEANUP profiling metrics Instruments two additional unprofiled gaps in the query execution path: - EXECUTOR_INITIALIZE: time spent building pipelines and scheduling tasks - CLEANUP: time spent in post-execution cleanup including transaction commit Together with RESULT_COLLECTOR, these cover all major gaps between profiler.StartQuery() and profiler.EndQuery(). Co-Authored-By: Claude Opus 4.6 --- src/common/enum_util.cpp | 8 +++++--- src/common/enums/metric_type.cpp | 10 ++++++++++ src/common/enums/metric_type.json | 16 ++++++++++++++++ src/include/duckdb/common/enums/metric_type.hpp | 2 ++ src/main/client_context.cpp | 5 +++++ src/main/profiling_utils.cpp | 4 ++++ .../profiling/test_all_profiling_settings.test | 2 ++ .../test_custom_profiling_using_groups.test | 12 ++++++++++++ 8 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/common/enum_util.cpp b/src/common/enum_util.cpp index 6900db695cc4..a39cf4388cb6 100644 --- a/src/common/enum_util.cpp +++ b/src/common/enum_util.cpp @@ -3375,19 +3375,21 @@ const StringUtil::EnumStringLiteral *GetMetricTypeValues() { { static_cast(MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES), "PHYSICAL_PLANNER_RESOLVE_TYPES" }, { static_cast(MetricType::PLANNER), "PLANNER" }, { static_cast(MetricType::PLANNER_BINDING), "PLANNER_BINDING" }, - { static_cast(MetricType::RESULT_COLLECTOR), "RESULT_COLLECTOR" } + { static_cast(MetricType::RESULT_COLLECTOR), "RESULT_COLLECTOR" }, + { static_cast(MetricType::EXECUTOR_INITIALIZE), "EXECUTOR_INITIALIZE" }, + { static_cast(MetricType::CLEANUP), "CLEANUP" } }; return values; } template<> const char* EnumUtil::ToChars(MetricType value) { - return StringUtil::EnumToString(GetMetricTypeValues(), 73, "MetricType", static_cast(value)); + return StringUtil::EnumToString(GetMetricTypeValues(), 75, "MetricType", static_cast(value)); } template<> MetricType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetMetricTypeValues(), 73, "MetricType", value)); + return static_cast(StringUtil::StringToEnum(GetMetricTypeValues(), 75, "MetricType", value)); } const StringUtil::EnumStringLiteral *GetMonotonicityValues() { diff --git a/src/common/enums/metric_type.cpp b/src/common/enums/metric_type.cpp index ecf79ef39e9d..dbffa3349a9f 100644 --- a/src/common/enums/metric_type.cpp +++ b/src/common/enums/metric_type.cpp @@ -13,11 +13,13 @@ profiler_settings_t MetricsUtils::GetAllMetrics() { MetricType::ATTACH_REPLAY_WAL_LATENCY, MetricType::BLOCKED_THREAD_TIME, MetricType::CHECKPOINT_LATENCY, + MetricType::CLEANUP, MetricType::COMMIT_LOCAL_STORAGE_LATENCY, MetricType::CPU_TIME, MetricType::CUMULATIVE_CARDINALITY, MetricType::CUMULATIVE_OPTIMIZER_TIMING, MetricType::CUMULATIVE_ROWS_SCANNED, + MetricType::EXECUTOR_INITIALIZE, MetricType::EXTRA_INFO, MetricType::LATENCY, MetricType::OPERATOR_CARDINALITY, @@ -318,7 +320,9 @@ OptimizerType MetricsUtils::GetOptimizerTypeByMetric(MetricType type) { profiler_settings_t MetricsUtils::GetPhaseTimingMetrics() { return { MetricType::ALL_OPTIMIZERS, + MetricType::CLEANUP, MetricType::CUMULATIVE_OPTIMIZER_TIMING, + MetricType::EXECUTOR_INITIALIZE, MetricType::PARSER, MetricType::PHYSICAL_PLANNER, MetricType::PHYSICAL_PLANNER_COLUMN_BINDING, @@ -333,7 +337,9 @@ profiler_settings_t MetricsUtils::GetPhaseTimingMetrics() { bool MetricsUtils::IsPhaseTimingMetric(MetricType type) { switch(type) { case MetricType::ALL_OPTIMIZERS: + case MetricType::CLEANUP: case MetricType::CUMULATIVE_OPTIMIZER_TIMING: + case MetricType::EXECUTOR_INITIALIZE: case MetricType::PARSER: case MetricType::PHYSICAL_PLANNER: case MetricType::PHYSICAL_PLANNER_COLUMN_BINDING: @@ -355,8 +361,10 @@ profiler_settings_t MetricsUtils::GetRootScopeMetrics() { MetricType::ATTACH_REPLAY_WAL_LATENCY, MetricType::BLOCKED_THREAD_TIME, MetricType::CHECKPOINT_LATENCY, + MetricType::CLEANUP, MetricType::COMMIT_LOCAL_STORAGE_LATENCY, MetricType::CUMULATIVE_OPTIMIZER_TIMING, + MetricType::EXECUTOR_INITIALIZE, MetricType::LATENCY, MetricType::OPTIMIZER_AGGREGATE_FUNCTION_REWRITER, MetricType::OPTIMIZER_BUILD_SIDE_PROBE_SIDE, @@ -420,8 +428,10 @@ bool MetricsUtils::IsRootScopeMetric(MetricType type) { case MetricType::ATTACH_REPLAY_WAL_LATENCY: case MetricType::BLOCKED_THREAD_TIME: case MetricType::CHECKPOINT_LATENCY: + case MetricType::CLEANUP: case MetricType::COMMIT_LOCAL_STORAGE_LATENCY: case MetricType::CUMULATIVE_OPTIMIZER_TIMING: + case MetricType::EXECUTOR_INITIALIZE: case MetricType::LATENCY: case MetricType::OPTIMIZER_AGGREGATE_FUNCTION_REWRITER: case MetricType::OPTIMIZER_BUILD_SIDE_PROBE_SIDE: diff --git a/src/common/enums/metric_type.json b/src/common/enums/metric_type.json index fee1ce3ec904..58efbea6a1c1 100644 --- a/src/common/enums/metric_type.json +++ b/src/common/enums/metric_type.json @@ -300,6 +300,22 @@ "type": "double", "unit": "milliseconds", "query_root": true + }, + { + "name": "EXECUTOR_INITIALIZE", + "enum_value": 101, + "description": "The time spent initializing the executor, building pipelines and scheduling tasks.", + "type": "double", + "unit": "milliseconds", + "query_root": true + }, + { + "name": "CLEANUP", + "enum_value": 102, + "description": "The time spent in post-execution cleanup including transaction commit and thread relaunch.", + "type": "double", + "unit": "milliseconds", + "query_root": true } ] }, diff --git a/src/include/duckdb/common/enums/metric_type.hpp b/src/include/duckdb/common/enums/metric_type.hpp index 5ed4e3f4506b..e5aba12d179f 100644 --- a/src/include/duckdb/common/enums/metric_type.hpp +++ b/src/include/duckdb/common/enums/metric_type.hpp @@ -100,7 +100,9 @@ enum class MetricType : uint8_t { OPTIMIZER_PARTITIONED_EXECUTION = 62, // PhaseTiming metrics ALL_OPTIMIZERS = 18, + CLEANUP = 102, CUMULATIVE_OPTIMIZER_TIMING = 19, + EXECUTOR_INITIALIZE = 101, PARSER = 99, PHYSICAL_PLANNER = 22, PHYSICAL_PLANNER_COLUMN_BINDING = 23, diff --git a/src/main/client_context.cpp b/src/main/client_context.cpp index 6a1738fe08cb..aeb5cde9e911 100644 --- a/src/main/client_context.cpp +++ b/src/main/client_context.cpp @@ -402,7 +402,9 @@ unique_ptr ClientContext::FetchResultInternal(ClientContextLock &lo result = executor.GetResult(); profiler.EndPhase(); if (!create_stream_result) { + profiler.StartPhase(MetricType::CLEANUP); CleanupInternal(lock, result.get(), false); + profiler.EndPhase(); } else { active_query->SetOpenResult(*result); } @@ -636,7 +638,10 @@ ClientContext::PendingPreparedStatementInternal(ClientContextLock &lock, // Get the result collector and initialize the executor. auto collector = get_collector(*this, statement_data); D_ASSERT(collector->type == PhysicalOperatorType::RESULT_COLLECTOR); + auto &profiler = QueryProfiler::Get(*this); + profiler.StartPhase(MetricType::EXECUTOR_INITIALIZE); executor.Initialize(std::move(collector)); + profiler.EndPhase(); auto types = executor.GetTypes(); D_ASSERT(types == statement_data.types); diff --git a/src/main/profiling_utils.cpp b/src/main/profiling_utils.cpp index 327c6840283b..09e87f5679a2 100644 --- a/src/main/profiling_utils.cpp +++ b/src/main/profiling_utils.cpp @@ -57,9 +57,11 @@ void ProfilingUtils::SetMetricToDefault(profiler_metrics_t &metrics, const Metri case MetricType::ATTACH_REPLAY_WAL_LATENCY: case MetricType::BLOCKED_THREAD_TIME: case MetricType::CHECKPOINT_LATENCY: + case MetricType::CLEANUP: case MetricType::COMMIT_LOCAL_STORAGE_LATENCY: case MetricType::CPU_TIME: case MetricType::CUMULATIVE_OPTIMIZER_TIMING: + case MetricType::EXECUTOR_INITIALIZE: case MetricType::LATENCY: case MetricType::OPERATOR_TIMING: case MetricType::OPTIMIZER_AGGREGATE_FUNCTION_REWRITER: @@ -147,9 +149,11 @@ void ProfilingUtils::MetricToJson(duckdb_yyjson::yyjson_mut_doc *doc, duckdb_yyj case MetricType::ATTACH_REPLAY_WAL_LATENCY: case MetricType::BLOCKED_THREAD_TIME: case MetricType::CHECKPOINT_LATENCY: + case MetricType::CLEANUP: case MetricType::COMMIT_LOCAL_STORAGE_LATENCY: case MetricType::CPU_TIME: case MetricType::CUMULATIVE_OPTIMIZER_TIMING: + case MetricType::EXECUTOR_INITIALIZE: case MetricType::LATENCY: case MetricType::OPERATOR_TIMING: case MetricType::OPTIMIZER_AGGREGATE_FUNCTION_REWRITER: diff --git a/test/sql/pragma/profiling/test_all_profiling_settings.test b/test/sql/pragma/profiling/test_all_profiling_settings.test index 76eff5195f84..e98f08e8d462 100644 --- a/test/sql/pragma/profiling/test_all_profiling_settings.test +++ b/test/sql/pragma/profiling/test_all_profiling_settings.test @@ -34,11 +34,13 @@ SELECT unnest(res) FROM ( "ATTACH_REPLAY_WAL_LATENCY": "true" "BLOCKED_THREAD_TIME": "true" "CHECKPOINT_LATENCY": "true" +"CLEANUP": "true" "COMMIT_LOCAL_STORAGE_LATENCY": "true" "CPU_TIME": "true" "CUMULATIVE_CARDINALITY": "true" "CUMULATIVE_OPTIMIZER_TIMING": "true" "CUMULATIVE_ROWS_SCANNED": "true" +"EXECUTOR_INITIALIZE": "true" "EXTRA_INFO": "true" "LATENCY": "true" "OPERATOR_CARDINALITY": "true" diff --git a/test/sql/pragma/profiling/test_custom_profiling_using_groups.test b/test/sql/pragma/profiling/test_custom_profiling_using_groups.test index df89dcf7f4dc..841f673d1aad 100644 --- a/test/sql/pragma/profiling/test_custom_profiling_using_groups.test +++ b/test/sql/pragma/profiling/test_custom_profiling_using_groups.test @@ -34,11 +34,13 @@ SELECT unnest(res) FROM ( "ATTACH_REPLAY_WAL_LATENCY": "true" "BLOCKED_THREAD_TIME": "true" "CHECKPOINT_LATENCY": "true" +"CLEANUP": "true" "COMMIT_LOCAL_STORAGE_LATENCY": "true" "CPU_TIME": "true" "CUMULATIVE_CARDINALITY": "true" "CUMULATIVE_OPTIMIZER_TIMING": "true" "CUMULATIVE_ROWS_SCANNED": "true" +"EXECUTOR_INITIALIZE": "true" "EXTRA_INFO": "true" "LATENCY": "true" "OPERATOR_CARDINALITY": "true" @@ -112,11 +114,13 @@ SELECT ALL_OPTIMIZERS, ATTACH_REPLAY_WAL_LATENCY, BLOCKED_THREAD_TIME, CHECKPOINT_LATENCY, + CLEANUP, COMMIT_LOCAL_STORAGE_LATENCY, CPU_TIME, CUMULATIVE_CARDINALITY, CUMULATIVE_OPTIMIZER_TIMING, CUMULATIVE_ROWS_SCANNED, + EXECUTOR_INITIALIZE, EXTRA_INFO, LATENCY, OPTIMIZER_AGGREGATE_FUNCTION_REWRITER, @@ -550,7 +554,9 @@ SELECT unnest(res) FROM ( ) ORDER BY ALL; ---- "ALL_OPTIMIZERS": "true" +"CLEANUP": "true" "CUMULATIVE_OPTIMIZER_TIMING": "true" +"EXECUTOR_INITIALIZE": "true" "OPTIMIZER_AGGREGATE_FUNCTION_REWRITER": "true" "OPTIMIZER_BUILD_SIDE_PROBE_SIDE": "true" "OPTIMIZER_COLUMN_LIFETIME": "true" @@ -602,7 +608,9 @@ CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_ou statement ok SELECT ALL_OPTIMIZERS, + CLEANUP, CUMULATIVE_OPTIMIZER_TIMING, + EXECUTOR_INITIALIZE, OPTIMIZER_AGGREGATE_FUNCTION_REWRITER, OPTIMIZER_BUILD_SIDE_PROBE_SIDE, OPTIMIZER_COLUMN_LIFETIME, @@ -874,8 +882,10 @@ SELECT unnest(res) FROM ( "ATTACH_REPLAY_WAL_LATENCY": "true" "BLOCKED_THREAD_TIME": "true" "CHECKPOINT_LATENCY": "true" +"CLEANUP": "true" "COMMIT_LOCAL_STORAGE_LATENCY": "true" "CUMULATIVE_OPTIMIZER_TIMING": "true" +"EXECUTOR_INITIALIZE": "true" "OPTIMIZER_AGGREGATE_FUNCTION_REWRITER": "true" "OPTIMIZER_BUILD_SIDE_PROBE_SIDE": "true" "OPTIMIZER_COLUMN_LIFETIME": "true" @@ -939,8 +949,10 @@ SELECT ALL_OPTIMIZERS, ATTACH_REPLAY_WAL_LATENCY, BLOCKED_THREAD_TIME, CHECKPOINT_LATENCY, + CLEANUP, COMMIT_LOCAL_STORAGE_LATENCY, CUMULATIVE_OPTIMIZER_TIMING, + EXECUTOR_INITIALIZE, OPTIMIZER_AGGREGATE_FUNCTION_REWRITER, OPTIMIZER_BUILD_SIDE_PROBE_SIDE, OPTIMIZER_COLUMN_LIFETIME, From 5d929107f65eef108232ad4dcaa3c408e41b5972 Mon Sep 17 00:00:00 2001 From: Leonid Krugliak Date: Fri, 8 May 2026 17:43:00 +0300 Subject: [PATCH 4/5] Fix MetricType enum_util entries to alphabetical order Co-Authored-By: Claude Opus 4.6 --- src/common/enum_util.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/enum_util.cpp b/src/common/enum_util.cpp index a39cf4388cb6..48db8c164e41 100644 --- a/src/common/enum_util.cpp +++ b/src/common/enum_util.cpp @@ -3367,7 +3367,9 @@ const StringUtil::EnumStringLiteral *GetMetricTypeValues() { { static_cast(MetricType::OPTIMIZER_ROW_NUMBER_REWRITER), "OPTIMIZER_ROW_NUMBER_REWRITER" }, { static_cast(MetricType::OPTIMIZER_PARTITIONED_EXECUTION), "OPTIMIZER_PARTITIONED_EXECUTION" }, { static_cast(MetricType::ALL_OPTIMIZERS), "ALL_OPTIMIZERS" }, + { static_cast(MetricType::CLEANUP), "CLEANUP" }, { static_cast(MetricType::CUMULATIVE_OPTIMIZER_TIMING), "CUMULATIVE_OPTIMIZER_TIMING" }, + { static_cast(MetricType::EXECUTOR_INITIALIZE), "EXECUTOR_INITIALIZE" }, { static_cast(MetricType::PARSER), "PARSER" }, { static_cast(MetricType::PHYSICAL_PLANNER), "PHYSICAL_PLANNER" }, { static_cast(MetricType::PHYSICAL_PLANNER_COLUMN_BINDING), "PHYSICAL_PLANNER_COLUMN_BINDING" }, @@ -3375,9 +3377,7 @@ const StringUtil::EnumStringLiteral *GetMetricTypeValues() { { static_cast(MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES), "PHYSICAL_PLANNER_RESOLVE_TYPES" }, { static_cast(MetricType::PLANNER), "PLANNER" }, { static_cast(MetricType::PLANNER_BINDING), "PLANNER_BINDING" }, - { static_cast(MetricType::RESULT_COLLECTOR), "RESULT_COLLECTOR" }, - { static_cast(MetricType::EXECUTOR_INITIALIZE), "EXECUTOR_INITIALIZE" }, - { static_cast(MetricType::CLEANUP), "CLEANUP" } + { static_cast(MetricType::RESULT_COLLECTOR), "RESULT_COLLECTOR" } }; return values; } From aee57094df804898cfc599d76a0d61e1138f134a Mon Sep 17 00:00:00 2001 From: Leonid Krugliak Date: Fri, 8 May 2026 18:10:24 +0300 Subject: [PATCH 5/5] Remove RESULT_COLLECTOR metric, keep only EXECUTOR_INITIALIZE and CLEANUP The result collector's work is already captured in CPU_TIME as part of pipeline execution. The EXECUTOR_INITIALIZE and CLEANUP phase metrics cover the actual unprofiled gaps in the query lifecycle. Co-Authored-By: Claude Opus 4.6 --- src/common/enum_util.cpp | 7 +- src/common/enums/metric_type.cpp | 5 - src/common/enums/metric_type.json | 8 -- .../duckdb/common/enums/metric_type.hpp | 1 - src/main/client_context.cpp | 4 +- src/main/profiling_utils.cpp | 2 - .../test_all_profiling_settings.test | 1 - ...est_custom_profiling_result_collector.test | 121 ------------------ .../test_custom_profiling_using_groups.test | 8 +- 9 files changed, 5 insertions(+), 152 deletions(-) delete mode 100644 test/sql/pragma/profiling/test_custom_profiling_result_collector.test diff --git a/src/common/enum_util.cpp b/src/common/enum_util.cpp index 48db8c164e41..3a1f01d8a260 100644 --- a/src/common/enum_util.cpp +++ b/src/common/enum_util.cpp @@ -3376,20 +3376,19 @@ const StringUtil::EnumStringLiteral *GetMetricTypeValues() { { static_cast(MetricType::PHYSICAL_PLANNER_CREATE_PLAN), "PHYSICAL_PLANNER_CREATE_PLAN" }, { static_cast(MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES), "PHYSICAL_PLANNER_RESOLVE_TYPES" }, { static_cast(MetricType::PLANNER), "PLANNER" }, - { static_cast(MetricType::PLANNER_BINDING), "PLANNER_BINDING" }, - { static_cast(MetricType::RESULT_COLLECTOR), "RESULT_COLLECTOR" } + { static_cast(MetricType::PLANNER_BINDING), "PLANNER_BINDING" } }; return values; } template<> const char* EnumUtil::ToChars(MetricType value) { - return StringUtil::EnumToString(GetMetricTypeValues(), 75, "MetricType", static_cast(value)); + return StringUtil::EnumToString(GetMetricTypeValues(), 74, "MetricType", static_cast(value)); } template<> MetricType EnumUtil::FromString(const char *value) { - return static_cast(StringUtil::StringToEnum(GetMetricTypeValues(), 75, "MetricType", value)); + return static_cast(StringUtil::StringToEnum(GetMetricTypeValues(), 74, "MetricType", value)); } const StringUtil::EnumStringLiteral *GetMonotonicityValues() { diff --git a/src/common/enums/metric_type.cpp b/src/common/enums/metric_type.cpp index dbffa3349a9f..e5cf39852fa1 100644 --- a/src/common/enums/metric_type.cpp +++ b/src/common/enums/metric_type.cpp @@ -72,7 +72,6 @@ profiler_settings_t MetricsUtils::GetAllMetrics() { MetricType::PLANNER, MetricType::PLANNER_BINDING, MetricType::QUERY_NAME, - MetricType::RESULT_COLLECTOR, MetricType::RESULT_SET_SIZE, MetricType::ROWS_RETURNED, MetricType::SYSTEM_PEAK_BUFFER_MEMORY, @@ -330,7 +329,6 @@ profiler_settings_t MetricsUtils::GetPhaseTimingMetrics() { MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES, MetricType::PLANNER, MetricType::PLANNER_BINDING, - MetricType::RESULT_COLLECTOR, }; } @@ -347,7 +345,6 @@ bool MetricsUtils::IsPhaseTimingMetric(MetricType type) { case MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES: case MetricType::PLANNER: case MetricType::PLANNER_BINDING: - case MetricType::RESULT_COLLECTOR: return true; default: return false; @@ -410,7 +407,6 @@ profiler_settings_t MetricsUtils::GetRootScopeMetrics() { MetricType::PLANNER, MetricType::PLANNER_BINDING, MetricType::QUERY_NAME, - MetricType::RESULT_COLLECTOR, MetricType::ROWS_RETURNED, MetricType::TOTAL_BYTES_READ, MetricType::TOTAL_BYTES_WRITTEN, @@ -477,7 +473,6 @@ bool MetricsUtils::IsRootScopeMetric(MetricType type) { case MetricType::PLANNER: case MetricType::PLANNER_BINDING: case MetricType::QUERY_NAME: - case MetricType::RESULT_COLLECTOR: case MetricType::ROWS_RETURNED: case MetricType::TOTAL_BYTES_READ: case MetricType::TOTAL_BYTES_WRITTEN: diff --git a/src/common/enums/metric_type.json b/src/common/enums/metric_type.json index 58efbea6a1c1..a3d83bb89131 100644 --- a/src/common/enums/metric_type.json +++ b/src/common/enums/metric_type.json @@ -293,14 +293,6 @@ "unit": "milliseconds", "query_root": true }, - { - "name": "RESULT_COLLECTOR", - "enum_value": 100, - "description": "The time spent collecting and materializing the query result.", - "type": "double", - "unit": "milliseconds", - "query_root": true - }, { "name": "EXECUTOR_INITIALIZE", "enum_value": 101, diff --git a/src/include/duckdb/common/enums/metric_type.hpp b/src/include/duckdb/common/enums/metric_type.hpp index e5aba12d179f..e797677bf589 100644 --- a/src/include/duckdb/common/enums/metric_type.hpp +++ b/src/include/duckdb/common/enums/metric_type.hpp @@ -110,7 +110,6 @@ enum class MetricType : uint8_t { PHYSICAL_PLANNER_RESOLVE_TYPES = 24, PLANNER = 20, PLANNER_BINDING = 21, - RESULT_COLLECTOR = 100, }; struct MetricTypeHashFunction { diff --git a/src/main/client_context.cpp b/src/main/client_context.cpp index aeb5cde9e911..13dd9891dfa0 100644 --- a/src/main/client_context.cpp +++ b/src/main/client_context.cpp @@ -397,10 +397,8 @@ unique_ptr ClientContext::FetchResultInternal(ClientContextLock &lo unique_ptr result; D_ASSERT(executor.HasResultCollector()); // we have a result collector - fetch the result directly from the result collector - auto &profiler = QueryProfiler::Get(*this); - profiler.StartPhase(MetricType::RESULT_COLLECTOR); result = executor.GetResult(); - profiler.EndPhase(); + auto &profiler = QueryProfiler::Get(*this); if (!create_stream_result) { profiler.StartPhase(MetricType::CLEANUP); CleanupInternal(lock, result.get(), false); diff --git a/src/main/profiling_utils.cpp b/src/main/profiling_utils.cpp index 09e87f5679a2..ce4cf209e22d 100644 --- a/src/main/profiling_utils.cpp +++ b/src/main/profiling_utils.cpp @@ -108,7 +108,6 @@ void ProfilingUtils::SetMetricToDefault(profiler_metrics_t &metrics, const Metri case MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES: case MetricType::PLANNER: case MetricType::PLANNER_BINDING: - case MetricType::RESULT_COLLECTOR: case MetricType::WAITING_TO_ATTACH_LATENCY: case MetricType::WRITE_TO_WAL_LATENCY: metrics[type] = Value::CreateValue(0.0); @@ -200,7 +199,6 @@ void ProfilingUtils::MetricToJson(duckdb_yyjson::yyjson_mut_doc *doc, duckdb_yyj case MetricType::PHYSICAL_PLANNER_RESOLVE_TYPES: case MetricType::PLANNER: case MetricType::PLANNER_BINDING: - case MetricType::RESULT_COLLECTOR: case MetricType::WAITING_TO_ATTACH_LATENCY: case MetricType::WRITE_TO_WAL_LATENCY: yyjson_mut_obj_add_real(doc, dest, key_ptr, metrics[type].GetValue()); diff --git a/test/sql/pragma/profiling/test_all_profiling_settings.test b/test/sql/pragma/profiling/test_all_profiling_settings.test index e98f08e8d462..04f7a50a87c3 100644 --- a/test/sql/pragma/profiling/test_all_profiling_settings.test +++ b/test/sql/pragma/profiling/test_all_profiling_settings.test @@ -93,7 +93,6 @@ SELECT unnest(res) FROM ( "PLANNER": "true" "PLANNER_BINDING": "true" "QUERY_NAME": "true" -"RESULT_COLLECTOR": "true" "RESULT_SET_SIZE": "true" "ROWS_RETURNED": "true" "SYSTEM_PEAK_BUFFER_MEMORY": "true" diff --git a/test/sql/pragma/profiling/test_custom_profiling_result_collector.test b/test/sql/pragma/profiling/test_custom_profiling_result_collector.test deleted file mode 100644 index 097f1832477c..000000000000 --- a/test/sql/pragma/profiling/test_custom_profiling_result_collector.test +++ /dev/null @@ -1,121 +0,0 @@ -# name: test/sql/pragma/profiling/test_custom_profiling_result_collector.test -# description: Test RESULT_COLLECTOR metric. -# group: [profiling] - -require json - -statement ok -PRAGMA enable_profiling = 'json'; - -statement ok -PRAGMA profiling_output = '{TEST_DIR}/profiling_output.json'; - -statement ok -PRAGMA custom_profiling_settings='{"RESULT_COLLECTOR": "true"}'; - -statement ok -CREATE OR REPLACE TABLE test AS SELECT range AS id, hash(range) AS data FROM range(10000); - -statement ok -PRAGMA disable_profiling; - -statement ok -CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; - -# Verify that result_collector timing is present and non-negative -query I -SELECT - CASE WHEN result_collector >= 0 THEN 'true' - ELSE 'false' END -FROM metrics_output; ----- -true - -# Test with a SELECT query that materializes results -statement ok -PRAGMA enable_profiling = 'json'; - -statement ok -PRAGMA profiling_output = '{TEST_DIR}/profiling_output.json'; - -statement ok -PRAGMA custom_profiling_settings='{"RESULT_COLLECTOR": "true"}'; - -statement ok -SELECT * FROM test ORDER BY id; - -statement ok -PRAGMA disable_profiling; - -statement ok -CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; - -# Verify that result_collector is tracked -query I -SELECT - CASE WHEN result_collector >= 0 THEN 'true' - ELSE 'false' END -FROM metrics_output; ----- -true - -# Verify that result_collector only appears at root level, not in children -statement ok -PRAGMA enable_profiling = 'json'; - -statement ok -PRAGMA profiling_output = '{TEST_DIR}/profiling_output.json'; - -statement ok -PRAGMA custom_profiling_settings='{"RESULT_COLLECTOR": "true", "OPERATOR_NAME": "true", "OPERATOR_TYPE": "true"}'; - -statement ok -SELECT * FROM test WHERE id < 1000 ORDER BY id; - -statement ok -PRAGMA disable_profiling; - -statement ok -CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; - -# Check that root has result_collector -query I -SELECT - CASE WHEN result_collector >= 0 THEN 'true' - ELSE 'false' END -FROM metrics_output; ----- -true - -# Verify that children nodes don't have result_collector -statement ok -CREATE OR REPLACE TABLE metrics_children AS SELECT unnest(children, recursive := true) FROM metrics_output; - -statement error -SELECT result_collector FROM metrics_children; ----- -Binder Error - -# Test disabling the metric removes it from output -statement ok -PRAGMA enable_profiling = 'json'; - -statement ok -PRAGMA profiling_output = '{TEST_DIR}/profiling_output.json'; - -statement ok -PRAGMA custom_profiling_settings='{"RESULT_COLLECTOR": "false"}'; - -statement ok -SELECT * FROM test LIMIT 100; - -statement ok -PRAGMA disable_profiling; - -statement ok -CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; - -statement error -SELECT result_collector FROM metrics_output; ----- -Binder Error diff --git a/test/sql/pragma/profiling/test_custom_profiling_using_groups.test b/test/sql/pragma/profiling/test_custom_profiling_using_groups.test index 841f673d1aad..5a928177d21d 100644 --- a/test/sql/pragma/profiling/test_custom_profiling_using_groups.test +++ b/test/sql/pragma/profiling/test_custom_profiling_using_groups.test @@ -93,7 +93,6 @@ SELECT unnest(res) FROM ( "PLANNER": "true" "PLANNER_BINDING": "true" "QUERY_NAME": "true" -"RESULT_COLLECTOR": "true" "RESULT_SET_SIZE": "true" "ROWS_RETURNED": "true" "SYSTEM_PEAK_BUFFER_MEMORY": "true" @@ -168,7 +167,6 @@ SELECT ALL_OPTIMIZERS, PLANNER, PLANNER_BINDING, QUERY_NAME, - RESULT_COLLECTOR, RESULT_SET_SIZE, ROWS_RETURNED, SYSTEM_PEAK_BUFFER_MEMORY, @@ -601,7 +599,6 @@ SELECT unnest(res) FROM ( "PHYSICAL_PLANNER_RESOLVE_TYPES": "true" "PLANNER": "true" "PLANNER_BINDING": "true" -"RESULT_COLLECTOR": "true" statement ok CREATE OR REPLACE TABLE metrics_output AS SELECT * FROM '{TEST_DIR}/profiling_output.json'; @@ -654,8 +651,7 @@ SELECT ALL_OPTIMIZERS, PHYSICAL_PLANNER_CREATE_PLAN, PHYSICAL_PLANNER_RESOLVE_TYPES, PLANNER, - PLANNER_BINDING, - RESULT_COLLECTOR + PLANNER_BINDING FROM metrics_output; statement ok @@ -930,7 +926,6 @@ SELECT unnest(res) FROM ( "PHYSICAL_PLANNER_RESOLVE_TYPES": "true" "PLANNER": "true" "PLANNER_BINDING": "true" -"RESULT_COLLECTOR": "true" "SYSTEM_PEAK_BUFFER_MEMORY": "true" "SYSTEM_PEAK_TEMP_DIR_SIZE": "true" "TOTAL_BYTES_READ": "true" @@ -997,7 +992,6 @@ SELECT ALL_OPTIMIZERS, PHYSICAL_PLANNER_RESOLVE_TYPES, PLANNER, PLANNER_BINDING, - RESULT_COLLECTOR, SYSTEM_PEAK_BUFFER_MEMORY, SYSTEM_PEAK_TEMP_DIR_SIZE, TOTAL_BYTES_READ,