Repository navigation
[feature](ivm) Report how a refresh task refreshed an IVM MV, and count the refresh tasks on the Master - #69856
Conversation
…nt the refresh tasks on the Master
### What problem does this PR solve?
Issue Number: None
Related PR: None
Problem Summary:
A refresh request of an IVM materialized view does not say how the MV gets refreshed: an AUTO or INCREMENTAL
FALLBACK request may end in the incremental refresh, a partition rebuild, a whole-MV rebuild, or several of them,
and nothing reports which. tasks("type"="mv") shows the refresh mode (the partitions covered) and the fallback
reason the incremental attempt gave, but not whether the refresh that replaced it wrote anything, so "the refresh
fell back to recomputing the MV" cannot be told from the task list, let alone alerted on. /metrics has only the
counters of all asynchronous MVs (success, failed, skip), with no result by method, no fallback and no reason.
This change makes a refresh task of an IVM MV report the kinds of work that committed data, and counts the tasks on
the Master:
- MTMVTask notes each batch that commits -- the delta of the incremental attempt, a partition rebuild, a whole-MV
rebuild -- and decides, once, as the task ends, IvmRefreshMethod: NONE, INCREMENTAL, PARTITION_REBUILD,
COMPLETE, or MIXED when more than one kind committed (typically the rebuild of invalidated partitions that an
incremental attempt runs before the delta). tasks("type"="mv") gets the IvmRefreshMethod column.
- A task falls back when the incremental attempt gives up and an attempt replacing it commits data. Building the
first baseline of an MV no refresh has succeeded on is not a fallback, though the attempt names
INCOMPLETE_REFRESH_SNAPSHOT; neither is the rebuild of invalidated partitions before the delta, nor an explicit
COMPLETE or PARTITIONS request, nor a fallback that commits nothing.
- A failed task reports why: the IvmFailureReason a request that may not fall back failed with, or else the stage
the failure happened in (QUERY_UNUSABLE, PARTITION_SYNC_FAILED, EXECUTION_FAILED, OTHER), told apart by where it
was thrown rather than by its message.
- The method, the failure reason and whether the task fell back are journaled with the task. The MV keeps a
refresh summary updated from each task result, on the Master and in the replay of the same journal record
alike: consecutive failures, the latest success and failure (with the reason and the error message, cut to 512
characters), the latest method and run time, and the fallback count with the latest reason. It does not depend
on how many tasks the history keeps, and a follower that takes over has the same summary. Nothing reads it yet;
the IVM status system table and the per-MV series that follow read it.
- New counters, every label combination registered from 0, incremented on the Master as a task of an IVM MV ends:
| Series | Labels |
| --- | --- |
| doris_fe_ivm_refresh_tasks_total | result=success/no_change/failed/canceled, method |
| doris_fe_ivm_refresh_duration_seconds_total | method; tasks that succeeded or failed, StartTime to FinishTime |
| doris_fe_ivm_fallbacks_total | reason=the IvmFailureReason values, incomplete_refresh_snapshot |
| doris_fe_ivm_refresh_failures_total | reason=the IvmFailureReason values, the four stages |
| doris_fe_ivm_schedule_rejected_total | -- tasks the full MV task queue turned away |
no_change is a SUCCESS that committed nothing. A canceled task has no finish time, so it adds no run time and
counts no fallback. A rejected task never runs: it is counted where the task queue turns it away, which both
the instant and the timer dispatch go through, by a new AbstractTask.onRejected hook; the timer dispatch
cancels the task it could not queue, as before, so that one also counts as canceled. The counters live in the
registry of every FE like the other FE event counters, and only the Master counts in them, so a query sums
them over the FEs.
### Release note
tasks("type"="mv") has a new column IvmRefreshMethod. New FE metrics doris_fe_ivm_refresh_tasks_total,
doris_fe_ivm_refresh_duration_seconds_total, doris_fe_ivm_fallbacks_total, doris_fe_ivm_refresh_failures_total
and doris_fe_ivm_schedule_rejected_total.
### Check List (For Author)
- Test: Unit Test, Regression test, Manual test
- Unit Test: the mtmv, IVM, metric and job executor tests with CreateMTMVCommandTest, ShowCreateMTMVTest and
DropMaterializedViewTest, 60 classes and 884 cases, among them the new cases of MTMVTaskTest, MTMVTest,
IvmRefreshSummaryTest, IvmRefreshMetricsTest and TaskProcessorTest
- Regression test: mtmv_p0/ivm/test_ivm_refresh_method_metrics (nonConcurrent), twice on a local
shared-nothing cluster; not run in cloud mode
- Manual test: promtool check metrics on the /metrics of the Master reports no parsing error and no lint
finding on the new series
- Behavior changed: No (a new column and new metrics)
- Does this need documentation: Yes (doris-website, with the other changelog and IVM monitoring docs)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
/review |
|
run buildall |
TPC-H: Total hot run time: 28380 ms |
There was a problem hiding this comment.
Static review of the complete authoritative diff for PR #69856. The task-method classification, failure-stage mapping, persisted IVM summary/replay path, metric registration and terminal updates, task TVF schema, queue dispatch hooks, FE tests, and the new regression case were covered. No existing inline review threads or user focus points were present. The metric key sets and summary defaults were verified; the suspected old-image and queue-accounting concerns were dismissed under the repository's IVM compatibility and documented timer-dispatch behavior. No builds or tests were run because this is a static-only review. One P1 remains: cancel(false) can publish the IVM report before an in-flight worker records a commit that finishes after cancellation, leaving tasks(), metrics, and the persisted summary stale relative to committed work.
Review converged after 2 round(s).
| throw new JobException(e.getMessage(), e); | ||
| } | ||
| recordCommitted(refreshMode == RefreshMode.COMPLETE | ||
| ? IvmRefreshMethod.COMPLETE : IvmRefreshMethod.PARTITION_REBUILD); |
There was a problem hiding this comment.
[P1] Make cancellation publish the IVM report only after the worker is quiescent. cancel(false) reaches after() without waiting; after() computes ivmRefreshMethod and records metrics/journals the task, but an in-flight incremental refresh can return successfully after cancellation and then call recordCommitted, which only updates the transient set and never revises the published method or counters. A STOP can therefore leave the MV with committed incremental or partition data while tasks() and the persisted summary say NONE (and miss the fallback). Use one quiescent or atomic terminal snapshot for cancellation, metrics, and journaling so late commits cannot escape the report.
There was a problem hiding this comment.
Thanks. The race is real, but its reach is narrower than described, and the report already follows the cut master applies to a canceled task.
- Only a cancel publishes from another thread.
onSuccessandonFailrunafter()on the worker oncerun()has returned, so a task that succeeds or fails is quiescent when it is reported. - A canceled task does not touch the summary:
IvmRefreshSummary.updatereturns on CANCELED, andrecordIvmRefreshMetricscounts no fallback and no duration for it. So the summary cannot end up sayingNONE, and no fallback is missed. What can come out short is the canceled task's ownIvmRefreshMethod, and themethodlabel itsdoris_fe_ivm_refresh_tasks_total{result="canceled"}count goes to. - Only a batch whose transaction was already committing when the cancel landed can be missed. A statement that has not started is rejected by
registerExecutor, a running one is aborted by the coordinator cancel, andrefreshPartitionschecks CANCELED after its statement and throws beforerecordCommitted. In practice the window is the commit of the delta. - That is the cut master already applies to what a canceled task publishes.
after()runs on the cancel thread without waiting for the worker, and publishes copies of the partition snapshots and the epochs, so a batch that commits afterwards is not part of the result ([feature](ivm) Track the refresh baseline per MV partition, not by a rebuild barrier #68390, see the comment inafter()). The method is decided at the same point, so it agrees with the snapshots the task publishes. Counting a late batch in the method while its snapshots stay out would make the record contradict itself. - Waiting for the worker to stop is a change to the cancellation of the job framework.
cancel(false)comes from DROP MATERIALIZED VIEW and from an ALTER that rebuilds the job (JobManager.dropJobInternalsets STOPPED), and it deliberately does not block on a refresh in flight;cancel(true)only waits for the statement (waitNotRunning), not for what the worker records after it. That is outside the scope of this PR. The other half of the suggestion, one terminal snapshot for the report, the metrics and the journal, is whatafter()does now: the method, the failure reason, the counters and the journaled record are all decided there, once.
So I'm keeping it as it is.
TPC-DS: Total hot run time: 150816 ms |
ClickBench: Total hot run time: 24.26 s |
FE UT Coverage ReportIncrement line coverage |
FE Regression Coverage ReportIncrement line coverage |
What problem does this PR solve?
Issue Number: #69907
Related PR: None
Problem Summary:
In short. A refresh of an IVM materialized view can keep the MV up incrementally or quietly recompute it, and nothing tells the two apart:
tasks("type"="mv")does not say which kinds of work a task committed, and/metricshas no IVM series at all. This PR makes each refresh task of an IVM MV report how it refreshed the MV (IvmRefreshMethodintasks()) and why it failed or fell back, keeps a refresh summary on the MV for the IVM status table that follows, and counts the tasks on the Master in five new counters.Background. An IVM MV is refreshed by a task that runs one or more attempts: the incremental attempt (the delta of its base tables' streams, after rebuilding the MV partitions an invalidation marked), a partition rebuild, and a whole-MV rebuild. A request that may fall back (AUTO, INCREMENTAL FALLBACK, PARTITIONS FALLBACK) moves on to the next attempt when one gives up, for example to COMPLETE after the delta meets a MIN/MAX boundary.
tasks()already reportsRefreshMode(which partitions the task covered),IvmFallbackReason(why the incremental attempt gave up, or why a strict request failed) andIvmRebuiltPartitions.The problem, and what it cost.
RefreshModesaysCOMPLETEfor any whole-MV scope, andIvmFallbackReasonis set even when the attempt that replaced the delta wrote nothing, or when a strict request failed.async_materialized_view_task_success_num,_failed_num,_skip_num) cover every MV and say nothing about the method, the fallbacks, or why tasks fail. An MV that falls back to COMPLETE on every run, the expensive failure mode of IVM, goes unnoticed until the load of the cluster shows it.max_persistence_task_counttasks, so how many times in a row an MV has failed, or when it last fell back, cannot be read back from it.How this PR fixes it.
MTMVTasknotes each batch that commits data (the delta, a partition rebuild, a whole-MV rebuild) and decidesIvmRefreshMethodonce, as the task publishes its result:NONE,INCREMENTAL,PARTITION_REBUILD,COMPLETE, orMIXEDwhen more than one kind committed, typically the rebuild of invalidated partitions before the delta.tasks("type"="mv")gets theIvmRefreshMethodcolumn, NULL while a task runs and for a task of an MV that is not an IVM MV.IvmFailureReasona request that may not fall back failed with, or else the stage it failed in,QUERY_UNUSABLE,PARTITION_SYNC_FAILED,EXECUTION_FAILEDorOTHER, told apart by where the failure was thrown rather than by its message.IvmRefreshSummary, updated from each task result inMTMV.addTaskResult, which the Master and the replay of the same journal record run alike, from what the record carries: consecutive failures, the latest success and failure (with the reason and the error message, cut to 512 characters), the latest method and run time, and the fallback count with the latest reason. A canceled task leaves it as it is. Nothing reads it in this PR;information_schema.ivm_mv_statusand the per-MV series of a follow-up PR do.doris_fe_ivm_refresh_tasks_totalresult=success/no_change/failed/canceled,method=none/incremental/partition_rebuild/complete/mixedno_changeis a SUCCESS that committed nothingdoris_fe_ivm_refresh_duration_seconds_totalmethoddoris_fe_ivm_fallbacks_totalreason=theIvmFailureReasonvalues,incomplete_refresh_snapshotdoris_fe_ivm_refresh_failures_totalreason=theIvmFailureReasonvalues, the four stagesdoris_fe_ivm_schedule_rejected_totalLike the other FE event counters they live in the registry of every FE and only the Master counts in them, so a query sums them over the FEs; a restart or a new Master starts them from 0.
AbstractTask.onRejected, a new hook thatTaskProcessor.addTaskcalls when the executor queue rejects a task. Both the instant dispatch (JobScheduler) and the timer dispatch (DispatchTaskHandler) add tasks through it, andMTMVTaskcounts the rejection for an IVM MV. A timer-dispatched task that is turned away is canceled as before, so it also counts ascanceled.Results. Before this PR,
tasks()has no method column and/metricshas none of these series. After it, on a local cluster, one IVM MV refreshed through each case (regressiontest_ivm_refresh_method_metrics):tasks(): Status, IvmRefreshMethod, IvmFallbackReasontasks{success,complete}tasks{success,incremental}tasks{no_change,none}tasks{failed,none},failures{min_max_boundary_hit}tasks{success,complete},fallbacks{min_max_boundary_hit}tasks{success,complete}tasks{success,mixed}The first refresh of a new MV finds every partition still to be filled and goes straight to COMPLETE; it counts no fallback. The MV holds what its base table does after each case.
Classes, and how they call each other.
IvmRefreshMethod,IvmRefreshFailureStage(org.apache.doris.mtmv.ivm): the methods, and the stages a failure is reported by.MTMVTask: notes committed batches (recordCommitted), decides whether giving up the incremental attempt is a fallback (givingUpTheIncrementalAttemptIsAFallback), moves the failure stage along, settles the method and the failure reason inafter(), and counts the task (recordIvmRefreshMetrics) and a rejection (onRejected).IvmRefreshSummary(org.apache.doris.mtmv.ivm): persisted onMTMVand updated byupdate(MTMVTask)fromMTMV.addTaskResult;MTMV.hasSucceededIvmRefreshtells the first baseline apart.MetricRepo: registers the five counters.DoubleCounterMetric(new) keeps a total in seconds without rounding each addition.AbstractTask.onRejected(new, empty by default), called byTaskProcessor.addTask.Nothing else changes: no thrift or proto change, no new config, and the existing columns and series stay as they are.
Release note
tasks("type"="mv")has a new columnIvmRefreshMethod: how a refresh task of an IVM materialized view refreshed it (NONE, INCREMENTAL, PARTITION_REBUILD, COMPLETE or MIXED). New FE metricsdoris_fe_ivm_refresh_tasks_total{result,method},doris_fe_ivm_refresh_duration_seconds_total{method},doris_fe_ivm_fallbacks_total{reason},doris_fe_ivm_refresh_failures_total{reason}anddoris_fe_ivm_schedule_rejected_total.Check List (For Author)
Test
Unit test:
MTMVTaskTestchecks the method each kind of committed batch gives, that the rebuild of invalidated partitions before the delta isMIXEDand no fallback, that only what commits after the incremental attempt gave up makes a fallback, that building the first baseline is not one, the stage a failure reports (query, partition sync, execution) and the IVM reason of a strict request, theIvmRefreshMethodcolumn, and that the report is journaled while what it is decided from is not.IvmRefreshSummaryTestchecks consecutive failures across success, no change, failure and cancel, the latest success and failure, the error message cut, and the fallback count.MTMVTestchecks that the Master and the replay of the same journal records end with the same summary with a task history of one, and that an MV that is not an IVM MV keeps none.IvmRefreshMetricsTestchecks that every series is there from 0 and how each result is counted;TaskProcessorTestthat only the task the full queue rejects is told so. The mtmv, IVM, metric and job executor tests withCreateMTMVCommandTest,ShowCreateMTMVTestandDropMaterializedViewTestpass: 60 classes, 884 cases.Regression test:
mtmv_p0/ivm/test_ivm_refresh_method_metrics(nonConcurrent: it asserts which counters of the Master moved), twice on a local shared-nothing cluster; not run in cloud mode.Manual:
promtool check metricson the/metricsof the Master reports no parsing error and no lint finding on the new series.Behavior changed:
Does this need documentation?
Check List (For Reviewer who merge this PR)