Skip to content

[fix](fe) Reject unknown binlog properties and non-boolean binlog.enable - #69375

Merged
morningman merged 1 commit into
apache:masterfrom
morningman:changelog-t7-binlog-keys
Oct 22, 2026
Merged

morningman merged 1 commit into
apache:masterfrom
morningman:changelog-t7-binlog-keys

Conversation

@morningman

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: #69311

Problem Summary:

In short. CCR binlog is configured by four properties: binlog.enable, binlog.ttl_seconds, binlog.max_bytes and binlog.max_history_nums. PropertyAnalyzer.analyzeBinlogConfig, which every DDL uses to check them, ignored any other key under binlog. and read binlog.enable with Boolean.parseBoolean, so a misspelled key or a value such as "yes" was accepted without a word. The worst case: ALTER TABLE t SET ("binlog.enable" = "yes") succeeds and turns off the CCR binlog of a table that had it on. This PR rejects both, the way #69311 already made the changelog.* properties strict.

Background.

  • Every DDL path validates the binlog properties with analyzeBinlogConfig: CREATE DATABASE and ALTER DATABASE (CreateDatabaseCommand / AlterDatabasePropertiesCommand.validate), CREATE TABLE (InternalCatalog.createOlapTable) and ALTER TABLE (ModifyTablePropertiesOp.validate, then SchemaChangeHandler.updateBinlogConfig). Database properties are stored as the user wrote them.
  • [refactor](changelog) Rename row binlog to changelog and split it from CCR binlog #69311 split changelog (formerly row binlog) from CCR binlog. Its changelog.* properties reject unknown keys (Unknown changelog property: <key>) and accept only true / false for changelog.enable; the replaced binlog.format and binlog.need_historical_value are rejected in every DDL. The four CCR keys kept the lenient parsing.

The problem, and what it cost. On a cluster running master before this change:

Statement Before
ALTER TABLE t SET ("binlog.enable" = "yes"), t has binlog on succeeds; t's binlog is now off
CREATE TABLE ... ("binlog.enable" = "yes") the table is created with binlog off
ALTER TABLE t SET ("binlog.ttl_second" = "3600") (typo) Nothing is changed. please check your alter stmt.
CREATE TABLE ... ("binlog.ttl_second" = "3600") Unknown properties: {binlog.ttl_second=3600}
CREATE DATABASE d PROPERTIES ("binlog.ttl_second" = "3600") succeeds; the key is persisted
ALTER DATABASE d SET PROPERTIES ("binlog.max_byte" = "1024") succeeds; the key is persisted
ALTER DATABASE d SET PROPERTIES ("binlog.enable" = "1") succeeds; "1" is persisted and read as false

How this PR fixes it.

  • analyzeBinlogConfig rejects any key under binlog. other than the four with Unknown binlog property: <key>. The replaced binlog.format and binlog.need_historical_value are still checked first, so they keep their message that names the replacement.
  • binlog.enable accepts only true or false, in any case, and otherwise fails with the existing Invalid binlog enable value: <value>, which the old try / catch around Boolean.parseBoolean could never reach.
  • CREATE TABLE runs this check on the properties as written, where it only checked the replaced keys before. Its later call to analyzeBinlogConfig sees the effective binlog config (the database default merged with the table properties) that createOlapTable puts back over them, and by then "yes" has already become "false".
  • Persisted metadata is not validated again: image load and edit log replay read binlog properties through BinlogConfig.mergeFromProperties, which is unchanged, so a database that already stored such a key loads as before. A database's binlog default reaches CREATE TABLE as a BinlogConfig object, not as properties, so it does not make CREATE TABLE fail either.

Results.

Statement After
ALTER TABLE t SET ("binlog.enable" = "yes") Invalid binlog enable value: yes; t keeps its binlog setting
CREATE TABLE ... ("binlog.enable" = "yes") Invalid binlog enable value: yes
ALTER TABLE / CREATE TABLE / CREATE DATABASE with "binlog.ttl_second" Unknown binlog property: binlog.ttl_second
ALTER DATABASE ... ("binlog.max_byte" = "1024") Unknown binlog property: binlog.max_byte
ALTER DATABASE ... ("binlog.enable" = "1") Invalid binlog enable value: 1
"binlog.enable" = "TRUE" / "False" accepted, stored as true / false (unchanged)

Release note

The binlog.* properties of tables and databases are validated strictly: CREATE / ALTER TABLE and CREATE / ALTER DATABASE reject a key under binlog. other than binlog.enable, binlog.ttl_seconds, binlog.max_bytes and binlog.max_history_nums, and binlog.enable accepts only true or false. Before, such keys were ignored or stored, and any other binlog.enable value was read as false.

Check List (For Author)

  • Test: Unit Test, Regression test, Manual test
    • FE UT: PropertyAnalyzerTest.testAnalyzeBinlogConfig and ChangelogConfigSplitTest.testDdlValidatesBinlogProperties (new; CREATE TABLE and ALTER TABLE), plus the other test classes that create tables or databases with binlog properties.
    • Regression: ddl_p0/test_binlog_property_validation (new) covers each row of the Results table on tables and databases.
    • Manual: the Before table was taken on a local cluster running master before this change.
  • Behavior changed: Yes. DDL that set an unknown binlog.* key or a non-boolean binlog.enable now fails instead of being ignored, stored, or read as false.
  • Does this need documentation: No. The current docs do not describe other binlog.* keys or other binlog.enable values.

### What problem does this PR solve?

Issue Number: None

Related PR: apache#69311

Problem Summary:

**In short.** CCR binlog is configured by four properties: `binlog.enable`, `binlog.ttl_seconds`, `binlog.max_bytes` and `binlog.max_history_nums`. `PropertyAnalyzer.analyzeBinlogConfig`, which every DDL uses to check them, ignored any other key under `binlog.` and read `binlog.enable` with `Boolean.parseBoolean`, so a misspelled key or a value such as `"yes"` was accepted without a word. The worst case: `ALTER TABLE t SET ("binlog.enable" = "yes")` succeeds and turns off the CCR binlog of a table that had it on. This PR rejects both, the way apache#69311 already made the `changelog.*` properties strict.

**Background.**
- Every DDL path validates the binlog properties with `analyzeBinlogConfig`: CREATE DATABASE and ALTER DATABASE (`CreateDatabaseCommand` / `AlterDatabasePropertiesCommand.validate`), CREATE TABLE (`InternalCatalog.createOlapTable`) and ALTER TABLE (`ModifyTablePropertiesOp.validate`, then `SchemaChangeHandler.updateBinlogConfig`). Database properties are stored as the user wrote them.
- apache#69311 split changelog (formerly row binlog) from CCR binlog. Its `changelog.*` properties reject unknown keys (`Unknown changelog property: <key>`) and accept only `true` / `false` for `changelog.enable`; the replaced `binlog.format` and `binlog.need_historical_value` are rejected in every DDL. The four CCR keys kept the lenient parsing.

**The problem, and what it cost.** On a cluster running master before this change:

| Statement | Before |
|---|---|
| `ALTER TABLE t SET ("binlog.enable" = "yes")`, `t` has binlog on | succeeds; `t`'s binlog is now off |
| `CREATE TABLE ... ("binlog.enable" = "yes")` | the table is created with binlog off |
| `ALTER TABLE t SET ("binlog.ttl_second" = "3600")` (typo) | `Nothing is changed. please check your alter stmt.` |
| `CREATE TABLE ... ("binlog.ttl_second" = "3600")` | `Unknown properties: {binlog.ttl_second=3600}` |
| `CREATE DATABASE d PROPERTIES ("binlog.ttl_second" = "3600")` | succeeds; the key is persisted |
| `ALTER DATABASE d SET PROPERTIES ("binlog.max_byte" = "1024")` | succeeds; the key is persisted |
| `ALTER DATABASE d SET PROPERTIES ("binlog.enable" = "1")` | succeeds; `"1"` is persisted and read as false |

**How this PR fixes it.**
- `analyzeBinlogConfig` rejects any key under `binlog.` other than the four with `Unknown binlog property: <key>`. The replaced `binlog.format` and `binlog.need_historical_value` are still checked first, so they keep their message that names the replacement.
- `binlog.enable` accepts only `true` or `false`, in any case, and otherwise fails with the existing `Invalid binlog enable value: <value>`, which the old `try` / `catch` around `Boolean.parseBoolean` could never reach.
- CREATE TABLE runs this check on the properties as written, where it only checked the replaced keys before. Its later call to `analyzeBinlogConfig` sees the effective binlog config (the database default merged with the table properties) that `createOlapTable` puts back over them, and by then `"yes"` has already become `"false"`.
- Persisted metadata is not validated again: image load and edit log replay read binlog properties through `BinlogConfig.mergeFromProperties`, which is unchanged, so a database that already stored such a key loads as before. A database's binlog default reaches CREATE TABLE as a `BinlogConfig` object, not as properties, so it does not make CREATE TABLE fail either.

**Results.**

| Statement | After |
|---|---|
| `ALTER TABLE t SET ("binlog.enable" = "yes")` | `Invalid binlog enable value: yes`; `t` keeps its binlog setting |
| `CREATE TABLE ... ("binlog.enable" = "yes")` | `Invalid binlog enable value: yes` |
| `ALTER TABLE` / `CREATE TABLE` / `CREATE DATABASE` with `"binlog.ttl_second"` | `Unknown binlog property: binlog.ttl_second` |
| `ALTER DATABASE ... ("binlog.max_byte" = "1024")` | `Unknown binlog property: binlog.max_byte` |
| `ALTER DATABASE ... ("binlog.enable" = "1")` | `Invalid binlog enable value: 1` |
| `"binlog.enable" = "TRUE"` / `"False"` | accepted, stored as `true` / `false` (unchanged) |

### Release note

The `binlog.*` properties of tables and databases are validated strictly: CREATE / ALTER TABLE and CREATE / ALTER DATABASE reject a key under `binlog.` other than `binlog.enable`, `binlog.ttl_seconds`, `binlog.max_bytes` and `binlog.max_history_nums`, and `binlog.enable` accepts only `true` or `false`. Before, such keys were ignored or stored, and any other `binlog.enable` value was read as false.

### Check List (For Author)

- Test: Unit Test, Regression test, Manual test
    - FE UT: `PropertyAnalyzerTest.testAnalyzeBinlogConfig` and `ChangelogConfigSplitTest.testDdlValidatesBinlogProperties` (new; CREATE TABLE and ALTER TABLE), plus the other test classes that create tables or databases with binlog properties.
    - Regression: `ddl_p0/test_binlog_property_validation` (new) covers each row of the Results table on tables and databases.
    - Manual: the Before table was taken on a local cluster running master before this change.
- Behavior changed: Yes. DDL that set an unknown `binlog.*` key or a non-boolean `binlog.enable` now fails instead of being ignored, stored, or read as false.
- Does this need documentation: No. The current docs do not describe other `binlog.*` keys or other `binlog.enable` values.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@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?

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17592	5225	5091	5091
q2	2089	321	261	261
q3	10234	1373	837	837
q4	4683	457	332	332
q5	7441	795	526	526
q6	185	182	154	154
q7	735	793	603	603
q8	9244	1287	1064	1064
q9	5494	4227	4206	4206
q10	6826	1286	1016	1016
q11	429	248	231	231
q12	642	402	301	301
q13	18088	3580	2974	2974
q14	275	269	249	249
q15	q16	720	703	663	663
q17	1174	751	579	579
q18	7005	6151	6100	6100
q19	1118	957	591	591
q20	372	335	220	220
q21	5415	2419	2516	2419
q22	380	337	270	270
Total cold run time: 100141 ms
Total hot run time: 28687 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5891	5828	5924	5828
q2	723	610	567	567
q3	5586	5587	5255	5255
q4	1995	2142	1555	1555
q5	5856	5751	5752	5751
q6	244	191	147	147
q7	2153	2031	1790	1790
q8	2952	2638	2657	2638
q9	7900	7492	7442	7442
q10	3620	3592	3183	3183
q11	584	415	396	396
q12	663	690	499	499
q13	3281	3592	3013	3013
q14	282	292	267	267
q15	q16	654	671	628	628
q17	7737	7014	6851	6851
q18	12980	12198	12922	12198
q19	896	778	763	763
q20	2138	2135	1905	1905
q21	5646	4679	4776	4679
q22	512	450	413	413
Total cold run time: 72293 ms
Total hot run time: 65768 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 150228 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 938f875947c89a8853b43ed1a352eeab8ad28c19, data reload: false

query5	4303	581	446	446
query6	430	223	195	195
query7	4809	430	233	233
query8	321	166	145	145
query9	8766	3106	3108	3106
query10	493	307	257	257
query11	5971	3861	3554	3554
query12	137	90	86	86
query13	1259	433	315	315
query14	6131	3953	3631	3631
query14_1	3424	3394	3465	3394
query15	223	203	188	188
query16	943	421	395	395
query17	894	618	506	506
query18	2424	412	296	296
query19	180	158	126	126
query20	82	82	79	79
query21	210	129	101	101
query22	12946	13051	12862	12862
query23	13440	12600	12479	12479
query23_1	12349	12392	12443	12392
query24	7378	799	461	461
query24_1	463	455	465	455
query25	504	394	341	341
query26	1260	249	132	132
query27	2789	428	267	267
query28	4674	1919	1946	1919
query29	1566	584	441	441
query30	318	214	181	181
query31	1023	916	811	811
query32	144	92	90	90
query33	530	294	226	226
query34	957	836	489	489
query35	733	741	661	661
query36	813	812	721	721
query37	118	92	84	84
query38	1790	1704	1659	1659
query39	797	811	743	743
query39_1	732	726	742	726
query40	231	135	96	96
query41	72	68	68	68
query42	89	85	85	85
query43	349	362	315	315
query44	1348	713	695	695
query45	217	178	157	157
query46	821	946	561	561
query47	3042	2841	2709	2709
query48	308	315	222	222
query49	580	391	291	291
query50	676	273	216	216
query51	10300	10294	10423	10294
query52	78	82	69	69
query53	183	208	151	151
query54	242	201	178	178
query55	72	65	64	64
query56	251	215	212	212
query57	1676	1621	1575	1575
query58	288	263	249	249
query59	2055	2169	1953	1953
query60	294	238	220	220
query61	146	145	143	143
query62	389	339	286	286
query63	189	158	163	158
query64	2736	914	721	721
query65	3344	3277	3284	3277
query66	1799	425	315	315
query67	20146	19907	20011	19907
query68	2908	1001	611	611
query69	375	288	256	256
query70	855	770	786	770
query71	289	237	213	213
query72	2786	2707	2370	2370
query73	507	526	296	296
query74	4642	4505	4358	4358
query75	2319	2296	1933	1933
query76	2324	1001	582	582
query77	349	428	279	279
query78	9931	9680	9105	9105
query79	1000	848	512	512
query80	1229	417	348	348
query81	590	318	267	267
query82	571	139	119	119
query83	319	190	177	177
query84	310	120	103	103
query85	847	407	351	351
query86	395	236	230	230
query87	1994	1963	1805	1805
query88	3738	2673	2656	2656
query89	335	279	239	239
query90	1858	173	169	169
query91	170	155	125	125
query92	86	89	88	88
query93	1086	1015	562	562
query94	647	317	260	260
query95	583	340	307	307
query96	662	486	230	230
query97	2430	2423	2342	2342
query98	161	150	146	146
query99	770	766	636	636
Total cold run time: 235027 ms
Total hot run time: 150228 ms

@hello-stephen

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

query1	0.00	0.01	0.01
query2	0.10	0.05	0.04
query3	0.26	0.08	0.08
query4	1.60	0.09	0.11
query5	0.24	0.24	0.25
query6	1.17	0.69	0.68
query7	0.04	0.01	0.01
query8	0.05	0.04	0.03
query9	0.42	0.35	0.34
query10	0.56	0.57	0.58
query11	0.20	0.14	0.15
query12	0.19	0.15	0.14
query13	0.48	0.51	0.50
query14	1.04	1.02	1.01
query15	0.63	0.60	0.61
query16	0.35	0.35	0.36
query17	1.19	1.13	1.12
query18	0.24	0.19	0.20
query19	2.10	1.97	1.94
query20	0.02	0.02	0.01
query21	15.45	0.19	0.13
query22	4.90	0.05	0.06
query23	16.26	0.33	0.13
query24	3.14	0.56	0.42
query25	0.11	0.06	0.06
query26	0.99	0.22	0.16
query27	0.04	0.03	0.05
query28	3.08	0.74	0.35
query29	12.50	4.15	3.25
query30	0.30	0.13	0.10
query31	2.78	0.56	0.33
query32	3.22	0.61	0.50
query33	3.14	3.18	3.22
query34	15.36	3.96	3.39
query35	3.36	3.30	3.33
query36	0.55	0.42	0.41
query37	0.10	0.07	0.06
query38	0.06	0.04	0.04
query39	0.04	0.03	0.03
query40	0.18	0.15	0.14
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.61 s
Total hot run time: 24.24 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

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

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No substantiated new findings in the six changed files at 938f875. Review converged in one round: all three reviewers returned NO_NEW_VALUABLE_FINDINGS, all assigned coverage is complete, and both initial risk questions were dismissed with independently verified evidence. There are no existing inline findings to carry forward. No additional user focus was supplied.

The relevant flow is raw DDL properties -> shared binlog validation -> typed configuration/default merge -> metadata update. CREATE TABLE now validates before the merge can turn an invalid boolean into false. Database and table image/replay reconstruction continue through the unchanged permissive BinlogConfig parser.

Critical checkpoint conclusions:

  1. Goal: the four supported binlog keys remain accepted; unknown binlog.* keys and values other than case-insensitive true/false fail before configuration changes. Added unit and regression cases exercise this behavior.
  2. Scope and reuse: both production changes are focused and reuse the shared analyzer and existing exception handling.
  3. Concurrency and locking: validation uses statement-owned map copies, introduces no shared state or locks, and preserves the existing database-default read-lock snapshot. No new lock-order or blocking-IO concern was found.
  4. Lifecycle: no resource, task, static-initialization or ownership mechanism is added; early CREATE validation precedes table/tablet allocation.
  5. Configuration: no configuration item is added. The existing property grammar becomes strict while mixed-case booleans remain supported.
  6. Compatibility: no storage/wire format changes. Historical database properties remain loadable because strict validation stays in statement paths; typed inheritance avoids carrying unknown raw keys into new tables. The now-private legacy helper has no external references.
  7. Thrift/protobuf IDs: no gensrc definitions change.
  8. Parallel paths: CREATE/ALTER TABLE and DATABASE use the analyzer. Ordinary, temporary, CTAS, LIKE and cloud creation share the checked path; LIKE/CCR-generated binlog properties are canonical typed values.
  9. Conditions: the allowlist and boolean checks reject concrete invalid inputs; legacy-key migration errors retain priority. CREATE's copied-map comment explains the pre-normalization check.
  10. Tests: parser tests cover all four keys, normalization, map consumption and invalid inputs. DDL tests cover failed CREATE and preservation of an enabled table after failed ALTER; regression negatives cover all four DDL forms.
  11. Expected results: both new output blocks match the metadata emitters statically: canonical false for the table, and no matching binlog property for the untouched database.
  12. Observability: property/value-specific exceptions provide sufficient synchronous validation diagnostics; no additional metrics or logging is needed.
  13. Persistence and failover: existing journal writes, replay and image reconstruction are unchanged; strict validation does not enter replay.
  14. Data changes and atomicity: early rejection cannot mutate committed metadata. No row-data, transaction, visible-version or delete-bitmap logic changes.
  15. FE-BE variables: none are added or changed.
  16. Performance: the extra scan/map copy is limited to DDL property maps; no query hot-path work or external IO is introduced.
  17. Other concerns: the final sweep found no further substantiated issue or unresolved code-path question.

Validation limits: static review only; no builds or tests were run under the review contract. The expected-output generation provenance was not independently verified. These limits do not represent an unresolved code finding.

Review converged after 1 round(s).

@morningman
morningman merged commit 46e0bd8 into apache:master Oct 22, 2026
37 checks passed
@morningman
morningman deleted the changelog-t7-binlog-keys branch October 22, 2026 10:15
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Oct 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by one committer. reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants