Skip to content

[fix](binlog) Fix missing DELETE events in row binlog - #68034

Merged
luwei16 merged 1 commit into
apache:masterfrom
luwei16:fix/doris-28913-row-binlog-light-delete
Sep 17, 2026
Merged

luwei16 merged 1 commit into
apache:masterfrom
luwei16:fix/doris-28913-row-binlog-light-delete

Conversation

@luwei16

@luwei16 luwei16 commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: Predicate deletes only persist delete predicates and therefore cannot produce row-level DELETE events. A merge-on-write unique table with light delete enabled incorrectly used that path even when ROW binlog was enabled, leaving downstream consumers with stale rows. Route these deletes through row-bearing DELETE FROM USING execution, and reject any ROW-binlog predicate delete that still reaches the legacy handler.

Release note

Ensure DELETE on merge-on-write unique tables with ROW binlog emits row-level DELETE events even when light delete is enabled. Predicate DELETE on ROW-binlog tables now reports an explicit unsupported error.

Check List (For Author)

  • Test: Regression test, FE unit test, and manual test
    • Regression test: row_binlog_p0/test_row_binlog_mow_light_delete
    • Unit Test: org.apache.doris.nereids.trees.plans.commands.DeleteFromCommandTest
    • Manual test: verified ROW binlog DELETE events with historical values disabled and enabled
  • Behavior changed: Yes; MOW ROW-binlog deletes use row-bearing execution, while unsupported predicate deletes fail explicitly
  • Does this need documentation: No

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: Predicate deletes only persist delete predicates and therefore cannot produce row-level DELETE events. A merge-on-write unique table with light delete enabled incorrectly used that path even when ROW binlog was enabled, leaving downstream consumers with stale rows. Route these deletes through row-bearing DELETE FROM USING execution, and reject any ROW-binlog predicate delete that still reaches the legacy handler.

### Release note

Ensure DELETE on merge-on-write unique tables with ROW binlog emits row-level DELETE events even when light delete is enabled. Predicate DELETE on ROW-binlog tables now reports an explicit unsupported error.

### Check List (For Author)

- Test: Regression test, FE unit test, and manual test
    - Regression test: row_binlog_p0/test_row_binlog_mow_light_delete
    - Unit Test: org.apache.doris.nereids.trees.plans.commands.DeleteFromCommandTest
    - Manual test: verified ROW binlog DELETE events with historical values disabled and enabled
- Behavior changed: Yes; MOW ROW-binlog deletes use row-bearing execution, while unsupported predicate deletes fail explicitly
- Does this need documentation: No
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@luwei16

luwei16 commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@luwei16 luwei16 changed the title [fix](fe) Emit row binlog events for MOW deletes [fix](binlog) Emit row binlog events for MOW deletes Sep 15, 2026
@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 100.00% (2/2) 🎉
Increment coverage report
Complete coverage report

@luwei16 luwei16 changed the title [fix](binlog) Emit row binlog events for MOW deletes [fix](binlog) Fix missing DELETE events in row binlog Sep 16, 2026
@luwei16

luwei16 commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

Copy link
Copy Markdown
Contributor

Codex automated review failed and did not complete.

Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Sep 19th, 2026 8:24 AM.
Workflow run: https://github.com/apache/doris/actions/runs/35048497273

The selected account is excluded until 2026-09-19T08:24:00Z. Please trigger /review again; another configured account may be available.

@luwei16

luwei16 commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

run performance

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 17095 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 60761180f3c84e50778759c866401fd293e3ac46, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17563	3031	3032	3031
q2	2115	258	223	223
q3	10215	870	519	519
q4	4674	261	208	208
q5	7668	573	386	386
q6	135	119	96	96
q7	517	519	391	391
q8	9229	904	924	904
q9	3505	2457	2408	2408
q10	6533	871	718	718
q11	414	194	177	177
q12	609	259	206	206
q13	18135	1560	1165	1165
q14	163	150	136	136
q15	q16	449	404	381	381
q17	1347	908	816	816
q18	3114	2327	2299	2299
q19	1271	934	768	768
q20	385	289	202	202
q21	5604	1831	1838	1831
q22	331	266	230	230
Total cold run time: 93976 ms
Total hot run time: 17095 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3428	3329	3305	3305
q2	503	388	372	372
q3	2241	2343	2277	2277
q4	1219	1189	900	900
q5	2286	2169	2173	2169
q6	165	119	88	88
q7	1042	909	889	889
q8	1604	1432	1411	1411
q9	3249	3290	3220	3220
q10	1918	1844	1690	1690
q11	365	272	257	257
q12	461	441	349	349
q13	1474	1547	1162	1162
q14	184	178	173	173
q15	q16	403	400	363	363
q17	3687	3375	3388	3375
q18	4978	4617	5036	4617
q19	929	856	882	856
q20	1061	1023	851	851
q21	3833	3245	3263	3245
q22	400	349	336	336
Total cold run time: 35430 ms
Total hot run time: 31905 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 83250 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 60761180f3c84e50778759c866401fd293e3ac46, data reload: false

query5	4243	420	330	330
query6	387	145	121	121
query7	4946	437	233	233
query8	285	129	116	116
query9	8683	2870	2863	2863
query10	418	233	183	183
query11	5383	1043	924	924
query12	123	72	71	71
query13	1190	442	312	312
query14	6110	2291	2147	2147
query14_1	2043	2024	2017	2017
query15	174	131	116	116
query16	922	382	355	355
query17	858	437	383	383
query18	2331	322	232	232
query19	163	152	112	112
query20	73	71	73	71
query21	202	102	90	90
query22	5483	5511	5399	5399
query23	6907	6314	6470	6314
query23_1	6261	6105	6315	6105
query24	7341	1105	778	778
query24_1	809	796	794	794
query25	425	305	262	262
query26	1228	249	125	125
query27	2780	422	255	255
query28	4693	1482	1498	1482
query29	919	455	357	357
query30	242	155	132	132
query31	816	414	345	345
query32	127	80	79	79
query33	468	227	187	187
query34	990	833	499	499
query35	411	425	361	361
query36	589	563	525	525
query37	124	87	70	70
query38	1024	871	846	846
query39	511	518	487	487
query39_1	483	474	468	468
query40	205	96	95	95
query41	60	57	56	56
query42	78	77	75	75
query43	243	242	215	215
query44	1000	546	552	546
query45	114	107	103	103
query46	785	845	536	536
query47	782	758	727	727
query48	319	320	223	223
query49	543	246	202	202
query50	732	278	198	198
query51	8152	8123	7931	7931
query52	68	67	78	67
query53	221	201	146	146
query54	196	159	146	146
query55	76	64	55	55
query56	179	165	162	162
query57	699	675	644	644
query58	193	158	157	157
query59	1278	1277	1109	1109
query60	255	183	185	183
query61	107	103	106	103
query62	347	206	188	188
query63	173	138	139	138
query64	2795	669	578	578
query65	1716	1678	1655	1655
query66	1766	253	199	199
query67	9802	9964	9821	9821
query68	3004	1229	733	733
query69	361	218	197	197
query70	672	626	609	609
query71	242	175	215	175
query72	2312	1698	1556	1556
query73	650	579	329	329
query74	2010	1230	1150	1150
query75	1199	1153	987	987
query76	2375	735	527	527
query77	253	252	216	216
query78	4155	3971	3400	3400
query79	2239	874	595	595
query80	1546	319	282	282
query81	486	156	134	134
query82	638	123	101	101
query83	273	215	199	199
query84	290	115	88	88
query85	763	338	285	285
query86	398	180	179	179
query87	1050	1003	921	921
query88	2797	2119	2109	2109
query89	283	199	174	174
query90	1961	134	127	127
query91	137	116	103	103
query92	82	73	71	71
query93	1679	1087	732	732
query94	653	252	229	229
query95	512	254	299	254
query96	847	573	273	273
query97	1070	1124	1016	1016
query98	176	153	136	136
query99	421	351	319	319
Total cold run time: 179022 ms
Total hot run time: 83250 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 14.95 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 60761180f3c84e50778759c866401fd293e3ac46, data reload: false

query1	0.01	0.01	0.00
query2	0.08	0.04	0.04
query3	0.25	0.10	0.11
query4	1.60	0.09	0.10
query5	0.18	0.16	0.17
query6	1.20	0.71	0.70
query7	0.04	0.01	0.00
query8	0.04	0.02	0.03
query9	0.28	0.22	0.23
query10	0.35	0.37	0.38
query11	0.16	0.13	0.11
query12	0.14	0.12	0.12
query13	0.31	0.32	0.31
query14	0.47	0.47	0.46
query15	0.35	0.35	0.37
query16	0.23	0.22	0.24
query17	0.72	0.71	0.66
query18	0.18	0.18	0.17
query19	1.20	1.19	1.19
query20	0.01	0.02	0.01
query21	15.49	0.16	0.11
query22	5.09	0.04	0.04
query23	16.18	0.26	0.10
query24	3.07	0.32	0.26
query25	0.10	0.05	0.04
query26	0.75	0.17	0.11
query27	0.04	0.03	0.04
query28	3.65	0.56	0.27
query29	12.45	3.21	2.58
query30	0.26	0.12	0.12
query31	2.76	0.37	0.18
query32	3.51	0.32	0.23
query33	1.41	1.51	1.46
query34	15.42	2.29	1.83
query35	1.81	1.76	1.78
query36	0.49	0.30	0.29
query37	0.07	0.04	0.04
query38	0.04	0.03	0.03
query39	0.03	0.02	0.03
query40	0.12	0.09	0.08
query41	0.08	0.02	0.03
query42	0.03	0.02	0.02
query43	0.04	0.03	0.03
Total cold run time: 90.69 s
Total hot run time: 14.95 s

@luwei16

luwei16 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

Local review conclusion: PASS.

Reviewed the exact range 0aea7140de64a7162d0a0d764e3dc1db7f40dbae...60761180f3c84e50778759c866401fd293e3ac46. The change correctly routes ROW-binlog MOW deletes through the existing row-bearing transaction path. I found no PR-introduced correctness, concurrency, lifecycle, transaction, persistence, compatibility, partition-routing, sequence-column, observability, or performance issue. The focused regression covers historical values on/off and the unsupported DUP-table path; exact-head compile, FE UT, NonConcurrent, P0, cloud, coverage, and performance checks are green. No local build or tests were run as part of this read-only review.

schema: doris-repo-review/v1
status: PASS
pr: apache/doris#68034
commit: 60761180f3c84e50778759c866401fd293e3ac46
base: 0aea7140de64a7162d0a0d764e3dc1db7f40dbae
reviewed_at: 2026-09-17T16:22:26+08:00
reviewer: luwei16
model: gpt-5.6-sol
effort: xhigh
findings: {blocker: 0, major: 0, minor: 0, nit: 0}
rounds: 1
converged: true

@luwei16
luwei16 merged commit f7a0842 into apache:master Sep 17, 2026
54 of 55 checks passed
morningman added a commit that referenced this pull request Sep 18, 2026
…on PRs from master in merge order (#67783 #68034 #68033 #68057 #68094 #68087) (#68151)

Cherry-picked from #67783, #68034, #68033, #68057, #68094, #68087

Batch pick of every merged PR carrying the `incremental-computation`
label that `branch-incremental-computation` does not have yet (no
`incremental-computation-picked` label), in the order they landed on
master (`git log --first-parent`). One commit per PR, each created with
`git cherry-pick -x` so the message ends with `(cherry picked from
commit <master sha>)`. Follows the same convention as #67830, #68017 and
#68073.

| # | Master commit | PR | Title |
|---|---|---|---|
| 1 | e545b13 | #67783 | [fix](policy) Enforce row policies on MOW
time travel |
| 2 | f7a0842 | #68034 | [fix](binlog) Fix missing DELETE events in
row binlog |
| 3 | e85575e | #68033 | [fix](binlog) Preserve row binlog
compaction policy |
| 4 | 21160d7 | #68057 | [fix](binlog) Decouple row binlog
compaction from CCR binlog config |
| 5 | 04aa5a1 | #68094 | [fix](binlog) Persist row binlog config
updates in cloud mode |
| 6 | da8feed | #68087 | [fix](binlog) Report streams with missing
base tables as stale |

Not included on purpose:
- The 20 labelled PRs that already carry
`incremental-computation-picked` (#62606 in the fork point, #67508 via
#67712, the nine of #67830, the six of #68017, the two of #68073, and
#68050 whose content this branch got directly through #68012).
- #68012 carries the label but is a PR against this branch itself
(merged as `6f7c87fa892`); nothing to pick.
- #67820 is still open on master; this branch already carries its
content via #67861.

### Prerequisite check

For every pick I listed the master commits between the fork point
(`efedf10c7e3`) and the pick that touch the same files and are not on
this branch, and checked whether the pick's behavior depends on them.

- **#67783** declares no related PR. It builds on the MOW time-travel
rewrite (`BindRelation.buildMowTimeTravelUnion`, #67480) which is
already here. Two unlabelled master commits overlap: #66770
(authorization plugin SPI: reworks the data-mask / row-filter API of
`LogicalCheckPolicy` and its test) and #67811 (removes the
`isPlayNereidsDump()` check in `CheckPolicy`). Neither is a functional
prerequisite: the fix consists of `getPolicyTable()` unwrapping
`OlapTableWrapper`, `CheckPolicy` collecting the whole filter chain
below the policy mask, and `BindRelation` putting a `LogicalCheckPolicy`
above each union branch — none of that uses the #66770 API. Not picked;
adapted instead (see below).
- **#68034**, **#68033**, **#68087** (#68087 relates to #67173, which is
before the fork point): no unpicked master commit touches any of their
files; the picks applied cleanly and are byte-identical to master.
- **#68057**: only `be/test/cloud/cloud_compaction_test.cpp` overlaps
with the unrelated #67972 (refresh tablet meta of continuously ingested
tablets); auto-merged, the pick only adds new `TEST_F` blocks. Main-code
hunks are byte-identical to master.
- **#68094**: overlaps with #68090 / #67972 / #66598
(`cloud_tablet.cpp`), #67295 / #67618 / #68090 (`base_tablet.{h,cpp}`),
#66598 / #67637 (`meta_service.cpp`), #66598 (`cloud.proto`,
`cloud_tablet_test.cpp`) and #67761 / #66598 / #67637
(`meta_service_test.cpp`). All auto-merged. The fix —
`BaseTablet::binlog_config()` under the meta lock,
`BinlogConfig::operator==`, `CloudTablet::sync_meta()` refreshing the
binlog config, `update_tablet` in the meta service accepting
`binlog_config`, and `CloudSchemaChangeHandler` routing ROW-binlog
property updates through it — does not use anything those commits add.
Its hunks are byte-identical to master except one trailing context line
in `sync_meta()` (`last_sync_tablet_meta_time_s` comes from #67972).

Only #67783 needed adaptation, recorded in its commit message:
- `LogicalCheckPolicy.java`: the conflicting context was master's
`parsePolicyExpression()` helper (#66770), which does not exist here;
the new `getPolicyTable()` is inserted in the same place without it.
- `CheckRowPolicyTest.java`: the data-mask mock is written against this
branch's per-column `AccessControllerManager.evalDataMaskPolicy(...,
column) -> Optional<DataMaskPolicy>` instead of master's
`evalDataMaskPolicies(..., Set<String>) -> Map<String, DataMaskSpec>`.
The masks are the same (concat for the random-distribution table; the
non-movable `k2` mask plus identity masks for the hidden reconstruction
columns of the MOW table). The master-only `Or` import (#66770) is not
carried; `Collections` / `Locale` imports were added because they arrive
with #66770 on master.

### Drift check against master

Each pick's `+`/`-` lines are identical to the master commit's, except
for the #67783 adaptation above. After the six picks, the touched files
still differ from master at `da8feed859d` in: `cloud_tablet.cpp`,
`base_tablet.{h,cpp}`, `cloud_compaction_test.cpp`,
`cloud_tablet_test.cpp`, `meta_service.cpp`, `meta_service_test.cpp`,
`cloud.proto`, `CheckPolicy.java`, `LogicalCheckPolicy.java`,
`CheckRowPolicyTest.java`. Replaying the nine unpicked master commits
listed above (#67637, #67618, #66770, #66598, #67972, #67811, #67761,
#67295, #68090) in a temporary index and removing this branch's own
#67861 (`GetTsoRecoveryTransactions` in `cloud.proto` /
`meta_service_test.cpp`) brings every file to zero diff against master,
except the two `LogicalCheckPolicy.java` / `CheckRowPolicyTest.java`
hunks of #66770 that overlap the adaptation. Nothing else is left over.

### Verification

- FE: `run-fe-ut.sh --run` on this branch (regenerates thrift/protobuf,
compiles fe-core main + test) with the test classes touched by the picks
plus `DeleteFromCommandTest` from #68034's checklist: 5 classes, 58
tests, 0 failures, 0 errors, BUILD SUCCESS — `CheckRowPolicyTest` 9 (the
three tests #67783 adds included), `ExplainTableStreamPlanTest` 24,
`CloudSchemaChangeHandlerTest` 18 (the tests of #68033 and #68094
included), `DropTableStreamTest` 5, `DeleteFromCommandTest` 2.
- FE checkstyle on fe-core: 0 violations.
- BE: `-fsyntax-only` with the flags of the Release build
(`compile_commands.json`, regenerated `gen_cpp` headers incl. the new
`TabletMetaInfoPB.binlog_config`) passes for `cloud/cloud_tablet.cpp`,
`cloud/cloud_storage_engine.cpp`, `storage/olap_server.cpp`,
`storage/tablet/tablet.cpp`, `storage/tablet/base_tablet.cpp`, and with
`-DBE_TEST -fno-access-control` for
`test/cloud/cloud_compaction_test.cpp`,
`test/cloud/cloud_tablet_test.cpp`,
`test/storage/compaction/compaction_task_test.cpp`,
`test/storage/tablet/tablet_test.cpp`.
- Meta service: the two-line `update_tablet` hunk of #68094 only uses
the generated `TabletMetaInfoPB::has_binlog_config()/binlog_config()`
and `TabletMetaCloudPB::mutable_binlog_config()` accessors, all present
in the regenerated headers.
- The two new groovy suites
(`row_binlog_p0/test_row_binlog_mow_light_delete`,
`time_travel_p0/test_mow_time_travel_row_policy`) parse cleanly (groovy
parser check).

---------

Co-authored-by: morrySnow <zhangwenxin@selectdb.com>
Co-authored-by: Luwei <814383175@qq.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants