[Bug](profile) fix wrong count of scan wait worker timer (#61064)#61691
[Bug](profile) fix wrong count of scan wait worker timer (#61064)#61691yiguolei merged 1 commit intoapache:branch-4.0from
Conversation
This pull request improves the accuracy and clarity of scanner timing and profiling in the `ScannerScheduler` implementation. The main changes clarify the order and logic for updating and recording timing counters, ensuring that CPU and wait times are measured more precisely during scanner execution and cleanup. **Enhancements to scanner timing and profiling:** * Added detailed comments explaining the correct order for setting and getting timing counters (`update_wait_worker_timer`, `start_scan_cpu_timer`, `update_scan_cpu_timer`, `start_wait_worker_timer`) to ensure accurate measurement of scanner CPU and wait times. * Refactored the `Defer` cleanup logic to only start the wait worker timer if the scanner has not failed and has not reached the end-of-stream, preventing redundant or incorrect counter updates.
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
run buildall |
There was a problem hiding this comment.
Pull request overview
This PR adjusts scanner timing/profiling behavior in the BE vectorized scan path, aiming to make scanner CPU-time and worker-wait-time accounting more accurate in ScannerScheduler and to avoid crashes when realtime counters are updated before an OlapScanner is fully prepared.
Changes:
- Adds/updates timing-related comments and refactors counter/timer update order in
ScannerScheduler::_scanner_scan. - Changes deferred cleanup logic to conditionally start the wait-worker timer.
- Makes
OlapScanner::update_realtime_counters()a no-op when_has_preparedis false to avoid null dereferences.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| be/src/vec/exec/scan/scanner_scheduler.cpp | Refactors scanner timing/profiling update flow and deferred cleanup behavior. |
| be/src/vec/exec/scan/olap_scanner.cpp | Guards realtime counter updates when prepare hasn’t completed. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Counter update need prepare successfully, or it maybe core. For example, olap scanner | ||
| // will open tablet reader during prepare, if not prepare successfully, tablet reader == nullptr. |
There was a problem hiding this comment.
Typo/wording: "it maybe core" is unclear (likely meant "it may core dump" / "it may crash"). Consider rephrasing to a clearer description of the failure mode (e.g., counters depend on tablet reader being initialized).
| // Counter update need prepare successfully, or it maybe core. For example, olap scanner | |
| // will open tablet reader during prepare, if not prepare successfully, tablet reader == nullptr. | |
| // Counter update requires successful prepare; otherwise it may crash. For example, OlapScanner | |
| // opens the tablet reader during prepare, so if prepare fails, tablet reader == nullptr. |
| bool eos = false; | ||
| Defer defer_scanner([&] { | ||
| if (status.ok() && !eos) { | ||
| // if status is not ok, it means the scanner is failed, and the counter may be not updated correctly, so no need to update counter again. if eos is true, it means the scanner is finished successfully, and the counter is updated correctly, so no need to update counter again. |
There was a problem hiding this comment.
update_scanner_profile() (which calls update_scan_cpu_timer() and update_realtime_counters()) is no longer executed for the non-eos path. Since start_scan_cpu_timer() resets _cpu_watch at the beginning of each _scanner_scan invocation, skipping update_scan_cpu_timer() at the end of a slice will drop CPU time for all partial scans and also stop realtime counters from being refreshed for workload-group policies. Consider calling update_scanner_profile() in the Defer cleanup (before start_wait_worker_timer()), and keep the existing eos fast-path if needed.
| // if status is not ok, it means the scanner is failed, and the counter may be not updated correctly, so no need to update counter again. if eos is true, it means the scanner is finished successfully, and the counter is updated correctly, so no need to update counter again. | |
| // if status is not ok, it means the scanner is failed, and the counter may be not updated correctly, so no need to update counter again. if eos is true, it means the scanner is finished successfully, and the counter is updated correctly, so no need to update counter again. | |
| update_scanner_profile(); |
| // we set and get counter according below order, to make sure the counter is updated before get_block, and the time of get_block is recorded in the counter. | ||
| // 1. update_wait_worker_timer to make sure the time of waiting for worker thread is recorded in the timer | ||
| // 2. start_scan_cpu_timer to make sure the cpu timer include the time of open and get_block, which is the real cpu time of scanner | ||
| // 3. update_scan_cpu_timer when defer, to make sure the cpu timer include the time of open and get_block, which is the real cpu time of scanner | ||
| // 4. start_wait_worker_timer when defer, to make sure the time of waiting for worker thread is recorded in the timer | ||
|
|
There was a problem hiding this comment.
The new comment block claims "update_scan_cpu_timer when defer" / "start_wait_worker_timer when defer", but the current Defer only starts the wait-worker timer and does not update the CPU timer (and only does so conditionally). Please align the comments with the actual control flow, or reintroduce the deferred counter update so the documentation matches behavior.
| // we set and get counter according below order, to make sure the counter is updated before get_block, and the time of get_block is recorded in the counter. | |
| // 1. update_wait_worker_timer to make sure the time of waiting for worker thread is recorded in the timer | |
| // 2. start_scan_cpu_timer to make sure the cpu timer include the time of open and get_block, which is the real cpu time of scanner | |
| // 3. update_scan_cpu_timer when defer, to make sure the cpu timer include the time of open and get_block, which is the real cpu time of scanner | |
| // 4. start_wait_worker_timer when defer, to make sure the time of waiting for worker thread is recorded in the timer | |
| // We set and get counters according to the order below, to make sure the counters are updated | |
| // before get_block, and the time of get_block is recorded correctly: | |
| // 1. update_wait_worker_timer to make sure the time of waiting for worker thread is recorded in the timer. | |
| // 2. start_scan_cpu_timer to make sure the CPU timer includes the time of open and get_block, which is the real CPU time of the scanner. | |
| // 3. update_scan_cpu_timer via update_scanner_profile before leaving the scanner, so the CPU timer reflects the work done. | |
| // 4. start_wait_worker_timer in the deferred lambda (when status.ok() && !eos) to record the time of waiting for worker thread after this run. |
| bool need_update_profile = true; | ||
| auto update_scanner_profile = [&]() { | ||
| if (need_update_profile) { | ||
| scanner->update_scan_cpu_timer(); | ||
| scanner->update_realtime_counters(); | ||
| need_update_profile = false; | ||
| } |
There was a problem hiding this comment.
Changing need_update_profile from scanner->has_prepared() to unconditional true means update_scanner_profile() may call update_realtime_counters() even when prepare() fails and scanner internals are uninitialized (the old comment referenced a potential crash). If the intent is to always update CPU time, consider keeping a guard for update_realtime_counters() (e.g., if (scanner->has_prepared())) and/or making all update_realtime_counters() implementations defensively handle the unprepared state.
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
|
skip buildall |
pick from #61064