Skip to content

[feat](connector) give each connector plugin its own conf file - #66347

Merged
morningman merged 13 commits into
apache:masterfrom
morningman:connector-plugin-conf
Aug 2, 2026
Merged

[feat](connector) give each connector plugin its own conf file#66347
morningman merged 13 commits into
apache:masterfrom
morningman:connector-plugin-conf

Conversation

@morningman

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary:

Giving one connector a new deployment-level setting currently costs two edits in
the engine: an @ConfField in fe-common's Config, and a line in fe-core's
DefaultConnectorContext.buildEnvironment() that forwards it. A connector plugin
cannot read Config itself — it loads child-first, so its own bundled copy
shadows the engine's and every field reads back as a code default — so the engine
has to carry each key by name. The result is that fe-core knows the config key
names of connectors it is otherwise entirely agnostic about, and that list only
grows.

This PR gives a connector plugin a configuration file of its own. The engine
reads <pluginDir>/<name>.conf (where <name> is ConnectorProvider.name()),
parses it generically, and serves it back through the new
ConnectorContext.getConnectorConfig(). No key name of any connector reaches
fe-core, and a new connector needs no engine change at all to add a
deployment-level setting.

Connectors read a setting through one entry point:

ConnectorConf.get(context, "drivers_dir", "jdbc_drivers_dir", null)
//                         ^ key in <name>.conf   ^ the fe.conf key it used to live under

Resolution is plugin conf → fe.conf → default, with blank treated as "not
set" at each step (an operator who writes key= has not configured it, and
reading it as a set empty string would let one stray line mask the fe.conf value
actually in effect).

Six settings across five connectors are moved onto the new channel. Every
@ConfField is kept
and stays the fallback, so an existing deployment upgrades
with nothing to edit and behaves exactly as before:

connector (name()) conf file key fe.conf fallback
trino-connector trino-connector.conf plugin_dir trino_connector_plugin_dir
hms (hive) hms.conf default_file_format hive_default_file_format
hms (hive) hms.conf enable_create_bucket_table enable_create_hive_bucket_table
jdbc jdbc.conf drivers_dir jdbc_drivers_dir
jdbc jdbc.conf force_sqlserver_encrypt_false force_sqlserver_jdbc_encrypt_false
iceberg / paimon iceberg.conf / paimon.conf drivers_dir jdbc_drivers_dir
iceberg / paimon iceberg.conf / paimon.conf metastore_client_timeout_second hive_metastore_client_timeout_second

Two of these are shared by several connectors at the fe.conf end (one
jdbc_drivers_dir serves jdbc, iceberg and paimon). A per-plugin file cannot
express that, so a deployment that moves them sets the value in each plugin's
conf; leaving them commented out keeps the single shared fe.conf value, which is
what every existing deployment gets. Both templates say so.

doris_home and doris_version stay in getEnvironment() — they are not
connector settings. jdbc_driver_secure_path is dropped from it: no connector
ever read it (the JDBC allow-list is enforced in fe-core by JdbcResource, which
reads Config directly), so it was a dead key that read like a connector
setting. The Config field itself is unchanged.

Packaging. A plugin ships src/main/resources/<name>.conf.template; build.sh
seeds the live <name>.conf from it with cp -n, globbing *.conf.template with
no connector named, so a new connector needs no build.sh change either. The live
.conf is deliberately not in the plugin zip, so the ordinary upgrade —
unzipping a newer plugin build over the deployed directory — refreshes the jars
and the template but never the administrator's file.

Note the conf file is named after ConnectorProvider.name(), not after the
plugin directory: plugins/connector/hive/ holds hms.conf and
plugins/connector/trino/ holds trino-connector.conf. The directory name is the
deployer's choice and cannot be what the engine keys on. Each connector carries a
test asserting its shipped template name still tracks name().

connector.plugin.api.version stays at 1.0. The added
ConnectorContext.getConnectorConfig() is a default method, nothing outside
fe-connector-spi implements ConnectorContext, and the only decorators of it
extend the parent-first ForwardingConnectorContext — loaded from the FE's own
classpath, so they inherit the new forward without being rebuilt. A connector
plugin built before this change loads and behaves exactly as it did; it simply
never reads the new map.

Release note

Connector plugins can now carry their own deployment-level configuration file,
<DORIS_HOME>/plugins/connector/<dir>/<name>.conf, seeded from a template shipped
with each plugin. Settings there take precedence over the corresponding fe.conf
keys, which keep working unchanged — no action is required when upgrading. The
file must be maintained on every FE node and takes effect after an FE restart.

Check List (For Author)

  • Test
    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason

Unit tests — new: ConnectorConfFileTest, ConnectorConfTest (spi),
ConnectorPluginConfTest (fe-core, drives loadPlugins + createConnector on real
plugin directories), IcebergConnectorConfTest, PaimonConnectorConfTest, plus
per-connector cases and a template-name guard in TrinoBootstrapTest,
HiveConnectorMetadataDdlTest, JdbcUrlNormalizerTest. Existing
ForwardingConnectorContextTest covers the new forward by reflection.

Each was mutation-checked (precedence reversed, forward removed, surface baseline
reverted, conf not keyed per provider, conf never attached, template renamed) and
confirmed to fail.

Manual testsh build.sh --fe, then verified in output/fe/plugins/connector/:

  • hive/hms.conf, trino/trino-connector.conf, iceberg/iceberg.conf,
    jdbc/jdbc.conf, paimon/paimon.conf are created and byte-identical to their
    templates; es/, hudi/, maxcompute/ ship no template and get no file, with
    no error.

  • Every seeded file has 0 active settings (all commented out), so a fresh
    deployment behaves exactly as before.

  • Upgrade safety: hand-edited hms.conf, replayed the deploy step (unzip -o +
    the cp -n loop) from the real plugin zip — the edit survives and only the
    template is refreshed.

  • Behavior changed:

    • Yes.

Two, both narrow:

  1. A deployment-level setting now resolves from the plugin's <name>.conf before
    fe.conf. For a deployment that does not edit the seeded (all-commented) file,
    nothing changes.
  2. A blank hive_default_file_format in fe.conf now falls through to orc instead
    of reaching the metastore create as an empty format string. An empty file format
    was never a working configuration.
  • Does this need documentation?
    • Yes.

Each shipped .conf.template documents its own keys inline, and
fe/fe-connector/README.md plus fe-connector-api/package-info.java (Rule 7) are
updated for connector authors. A doris-website page for operators is still to be
written.

@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: 28507 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit e9e5b4d6fcd9509b75a575f974e0dfd5cd8c642c, data reload: false

------ Round 1 ----------------------------------
============================================
q1	18382	3869	3890	3869
q2	2310	323	201	201
q3	11128	1321	853	853
q4	4812	475	340	340
q5	8047	825	543	543
q6	175	163	132	132
q7	724	787	592	592
q8	10133	1379	1369	1369
q9	6843	3983	3992	3983
q10	7438	1613	1348	1348
q11	767	349	316	316
q12	952	566	452	452
q13	18466	3621	2677	2677
q14	257	264	251	251
q15	q16	739	725	653	653
q17	878	893	1003	893
q18	7228	5696	5810	5696
q19	1570	1356	1056	1056
q20	769	646	603	603
q21	5912	2562	2382	2382
q22	437	365	298	298
Total cold run time: 107967 ms
Total hot run time: 28507 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4254	4178	4152	4152
q2	287	312	208	208
q3	4515	4900	4349	4349
q4	2149	2282	1417	1417
q5	4204	4160	4354	4160
q6	219	177	126	126
q7	1747	1692	1521	1521
q8	2236	1908	1864	1864
q9	7119	7053	7285	7053
q10	4247	4278	3830	3830
q11	540	406	378	378
q12	710	746	522	522
q13	3180	3384	2930	2930
q14	291	296	275	275
q15	q16	715	721	640	640
q17	1415	1199	1187	1187
q18	7351	6819	7098	6819
q19	1094	1065	1061	1061
q20	2235	2253	1955	1955
q21	5525	4798	4598	4598
q22	537	508	424	424
Total cold run time: 54570 ms
Total hot run time: 49469 ms

morningman and others added 10 commits August 1, 2026 15:28
Adding a deployment-level setting for one connector meant editing two files
in the engine: an @ConfField in fe-common's Config, and a line in fe-core's
DefaultConnectorContext.buildEnvironment that forwards it. A plugin cannot
read Config itself -- it loads child-first, so its own bundled copy shadows
the engine's and every field reads back as a code default -- so the engine
had to carry each key by name, and fe-core ended up knowing the key names of
connectors it is otherwise entirely agnostic about.

Give a connector a configuration file of its own instead. The engine reads
<pluginDir>/<name>.conf, where <name> is the plugin's ConnectorProvider.name(),
and serves the parsed map back through ConnectorContext.getConnectorConfig().
The file is parsed generically, so no key name of any connector reaches
fe-core, and a new connector needs no engine change at all.

ConnectorConf.get layers that map over getEnvironment(): plugin conf first,
then the fe.conf key the setting used to live under, then a default. That is
what lets the settings that already ship move to the new channel without
their @ConfFields going away, so an existing deployment keeps working after
an upgrade with nothing to edit. A setting introduced from now on passes
null for the legacy key and has no fe.conf half.

Blank is "not set" at every step. An operator who writes 'key=' means they
have not configured it, and reading it as a set empty string would let one
stray line mask the fe.conf value actually in effect -- with nothing in
either file to show which won. ConnectorConfFile still keeps blank-valued
keys in the map it returns, so the map reflects the file as written and the
decision stays in one place.

The engine side of the split is deliberate: the file is located, parsed and
defaulted once here rather than once per plugin, and getConnectorConfig() is
a narrower thing to hand a plugin than the plugin directory path would be.

This adds a method to ConnectorContext, so the recorded plugin API surface
baseline is refreshed in the same commit. connector.plugin.api.version stays
at 1.0: the method is a default, nothing outside fe-connector-spi implements
ConnectorContext, and the only decorators of it extend the parent-first
ForwardingConnectorContext -- which is loaded from the FE's own classpath, so
they inherit the new forward without being rebuilt. A connector plugin built
before this change therefore loads and behaves exactly as it did; it simply
never reads the new map.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
The SPI half of this landed with getConnectorConfig() answering an empty map
for everyone. This is the engine half: after a plugin directory is admitted,
read <pluginDir>/<name>.conf and hand it to that plugin's connectors.

The map is keyed by provider instance in an IdentityHashMap, so a lookup
never calls equals/hashCode on plugin code -- the same rule
DirectoryPluginRuntimeManager follows by snapshotting name() once at load and
never re-entering the plugin on a query path.

Attaching it has to happen in createConnector rather than in the context
itself: fe-core builds a ConnectorContext for a catalog before it knows which
plugin will claim the type, so the conf can only be layered on once the
provider is picked. Keeping it out of DefaultConnectorContext is also what
keeps the engine's context free of any connector's key names -- which is the
point of the whole change.

A sibling connector comes back through this same method, so it is handed its
own plugin's conf rather than inheriting the gateway's:
DefaultConnectorContext.createSiblingConnector passes the unwrapped engine
context, which is then wrapped with the sibling provider's own map. The conf
belongs to the plugin, not to the catalog.

A conf file that cannot be read is logged and skipped, and the plugin is
still registered. Refusing it would make the catalog type vanish, and the
only thing a user would see is CREATE CATALOG answering "no provider supports
type" -- which points nowhere near a bad file. Every setting reachable this
way has a default or a fe.conf fallback, so proceeding is a real degradation
path rather than a guess.

The tests drive loadPlugins + createConnector on real plugin directories,
because the two things worth proving -- that the file is found beside the
jars, and that each provider gets its own -- exist only on that path. The
probe plugins live outside org.apache.doris.connector. so their API version
is read from the jar that defines them; they report back through a sink class
that IS in that (parent-first) package, which is what makes the result
readable across the classloader split. One test deploys a plugin into a
directory NOT named after it, so nothing can pass by reading a file named
after the directory instead of after the provider.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
Seeds <name>.conf from the <name>.conf.template the plugin zip carries, with
cp -n, right after the zip is unpacked.

The live .conf is deliberately NOT in the zip. That is what makes the ordinary
upgrade -- unzip a newer plugin build over the deployed directory -- refresh
the jars and the template while leaving whatever the administrator configured
untouched. Shipping the .conf itself would silently revert their settings on
every upgrade.

The loop globs *.conf.template and names no connector, so a new connector that
ships a template needs no change here. It sits inside the deploy loop because
conn_plugin_target is unset once the loop ends. cp -n exits 0 when it skips an
existing file, so it is safe under this script's set -eo pipefail; the [ -e ]
guard covers a plugin that ships no template, where the glob stays literal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
Moves trino_connector_plugin_dir onto the connector's own settings file.
plugin_dir in trino-connector.conf now wins; fe.conf's key stays as the
fallback, so a deployment that changes nothing keeps resolving exactly as
before and its @ConfField keeps working.

resolvePluginDir takes the resolved directory rather than the engine
environment map. Which file a deployment-level setting comes from is the
connector's business, not this helper's, and threading the map through meant
the fe.conf key name was baked into a function that has no other reason to
know it.

Note the conf file is trino-connector.conf, not trino.conf: the engine names
it after ConnectorProvider.name(), which is this connector's type, while the
plugin directory it sits in is plugins/connector/trino. The two are allowed
to differ -- the directory name is the deployer's choice and cannot be what
the engine keys on. A test asserts the shipped template's name still tracks
name(), so renaming getType() cannot silently deploy a file nothing opens.

The fail-loud on a missing value is kept, and its message now names both
places the setting can come from, since after this either one could be the
missing half.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
Moves hive_default_file_format and enable_create_hive_bucket_table onto the
connector's own settings file, as default_file_format and
enable_create_bucket_table in hms.conf. The plugin conf wins; fe.conf stays as
the fallback, so a deployment that changes nothing behaves exactly as before
and both @ConfFields keep working.

The keys lose their hive_ prefix because the file name already namespaces
them -- hms.conf can only be read by this connector.

Note the file is hms.conf, not hive.conf: the engine names it after
ConnectorProvider.name(), which for this connector is "hms", while the plugin
directory it sits in is plugins/connector/hive. The two are allowed to differ,
and a test asserts the shipped template's name still tracks name() so renaming
getType() cannot silently deploy a file nothing opens.

doris_version stays in the engine environment. It is a build stamp rather than
something an administrator configures, so it does not belong in a settings
file at all.

One behavior difference worth naming: a blank value now counts as unset. Before,
a blank hive_default_file_format reached the metastore create as an empty
format string; now it falls through to orc. That is the intent of a blank line
in a conf file, and an empty file format was never a working configuration.

The bucket-gate rejection message now names both the conf key and the fe.conf
key, since after this either one could be the one that is off.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
Moves jdbc_drivers_dir and force_sqlserver_jdbc_encrypt_false onto the
connector's own settings file, as drivers_dir and
force_sqlserver_encrypt_false in jdbc.conf. The plugin conf wins; fe.conf
stays as the fallback, so a deployment that changes nothing behaves exactly as
before and both @ConfFields keep working.

The keys lose their jdbc_ prefix because the file name already namespaces
them.

JdbcUrlNormalizer.normalize now takes the resolved boolean rather than the
engine environment map. It had no reason to know an fe.conf key name, and
after this change the value can come from either of two files -- deciding
which is the connector's job, not the URL normalizer's.

doris_home stays in the engine environment: it is the FE install root, not
this connector's setting, and the drivers-directory default is still built
from it exactly as before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
… conf

Moves jdbc_drivers_dir and hive_metastore_client_timeout_second onto these two
connectors' own settings files, as drivers_dir and
metastore_client_timeout_second in iceberg.conf and paimon.conf. Each plugin
conf wins; fe.conf stays as the fallback, so a deployment that changes nothing
behaves exactly as before and both @ConfFields keep working.

Both settings are shared at the fe.conf end -- one jdbc_drivers_dir and one
hive_metastore_client_timeout_second serve jdbc, iceberg and paimon. A
per-plugin file cannot express that, so a deployment moving to these files
sets the value in each plugin's conf. That is the accepted cost of the
per-plugin model and is called out in both templates; leaving them commented
out keeps the single shared fe.conf value, which is what every existing
deployment gets.

JdbcDriverSupport.resolveDriverUrl now takes the drivers directory and
DORIS_HOME rather than the engine environment map. It lives in a module shared
by connectors whose conf files differ, so which file a value comes from cannot
be its decision -- the same shape AbstractHmsMetaStoreProperties already has,
taking its timeout default as a parameter instead of reading the environment.
The resolution itself is unchanged, including the
<doris_home>/plugins/jdbc_drivers fallback.

Paimon resolves the drivers directory through one accessor shared by both its
call sites (FE driver registration and the BE-bound scan options), for the same
reason both already delegate to JdbcDriverSupport: the two must resolve a given
driver_url identically or FE and BE load different jars. PaimonConnectorProperties'
javadoc claim of being a pure constant holder is amended -- HiveConnectorProperties
and JdbcConnectorProperties have carried static accessors for a while.

This has to be one commit: the signature change and its three call sites span
two plugin modules, so splitting it leaves an intermediate commit that does not
compile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
…nvironment

No connector reads it. The JDBC driver allow-list is enforced in fe-core by
JdbcResource, which reads Config.jdbc_driver_secure_path directly, so this
env entry was only ever written -- a dead key that reads like a connector
setting and invites someone to build on it.

Config.jdbc_driver_secure_path itself stays: JdbcResource is a legitimate
in-engine user of it, and the fe.conf key is unchanged for operators.

The comment added to buildEnvironment says what the map is now for, so the
next person adding a deployment-level setting reaches for the plugin's own
conf instead of adding a tenth key here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
Rule 7 listed three knob channels and named the fe.conf one "the one knob
shape that requires an engine change per key". That is no longer the shape a
new connector should reach for, and leaving the rule as written would keep
pointing people at Config.java.

It now lists four: the plugin's own <name>.conf is the deployment-level
channel, and the fe.conf/getEnvironment one is marked closed to new keys and
explained as the fallback that keeps existing deployments working.

The connector README gains the corresponding step in "Adding a New Connector",
including the two things that are not guessable: the keys take no connector
prefix (the file name namespaces them), and <name> is ConnectorProvider.name()
rather than the plugin directory name -- plugins/connector/hive/ holds
hms.conf.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
… check

build.sh seeds each connector's live <name>.conf from its template verbatim,
so the template's content is the file an administrator edits in
plugins/connector/<dir>/.

Listed by name rather than through a **/*.conf.template glob, so a new
template is a deliberate entry here rather than something a wildcard silently
absorbs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8r8ffK711cExL5p6SB1fr
@hello-stephen

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

query5	4379	610	483	483
query6	488	220	220	220
query7	5093	574	340	340
query8	596	183	164	164
query9	8718	4018	3977	3977
query10	490	375	317	317
query11	5813	2349	2043	2043
query12	163	100	96	96
query13	1267	601	457	457
query14	6201	4653	4373	4373
query14_1	3801	3799	3796	3796
query15	212	201	178	178
query16	3503	481	462	462
query17	1218	717	559	559
query18	2337	463	344	344
query19	219	190	152	152
query20	109	104	99	99
query21	887	161	137	137
query22	13467	13326	12803	12803
query23	17645	16751	16407	16407
query23_1	16371	16238	16401	16238
query24	8687	1736	1302	1302
query24_1	1310	1324	1295	1295
query25	568	423	386	386
query26	2531	360	216	216
query27	3100	584	381	381
query28	4358	2074	2053	2053
query29	1051	598	467	467
query30	375	258	229	229
query31	1107	1093	984	984
query32	97	62	55	55
query33	521	311	242	242
query34	1205	1169	632	632
query35	748	755	639	639
query36	787	806	700	700
query37	146	103	81	81
query38	1864	1692	1619	1619
query39	847	819	808	808
query39_1	789	777	828	777
query40	246	156	144	144
query41	62	58	59	58
query42	88	89	87	87
query43	319	320	271	271
query44	1409	780	758	758
query45	183	167	160	160
query46	1059	1241	753	753
query47	1544	1553	1394	1394
query48	390	395	290	290
query49	702	388	284	284
query50	1087	430	338	338
query51	10305	10353	10236	10236
query52	86	83	74	74
query53	262	267	208	208
query54	295	245	212	212
query55	74	68	66	66
query56	300	290	282	282
query57	1282	997	921	921
query58	294	256	251	251
query59	1537	1625	1369	1369
query60	321	274	261	261
query61	155	149	145	145
query62	423	318	262	262
query63	237	194	189	189
query64	2432	993	817	817
query65	3903	3823	3853	3823
query66	1711	457	345	345
query67	28209	28204	28021	28021
query68	3080	1670	991	991
query69	569	319	265	265
query70	896	809	829	809
query71	389	346	327	327
query72	3531	2781	2439	2439
query73	867	724	439	439
query74	4640	4568	4301	4301
query75	2354	2338	2005	2005
query76	1891	1123	743	743
query77	344	374	272	272
query78	11207	11189	10507	10507
query79	1617	1092	765	765
query80	1202	534	456	456
query81	563	331	278	278
query82	599	150	119	119
query83	384	323	295	295
query84	417	157	131	131
query85	988	619	516	516
query86	429	229	225	225
query87	1798	1788	1702	1702
query88	3779	2822	2767	2767
query89	395	312	275	275
query90	1908	198	189	189
query91	201	191	160	160
query92	59	64	56	56
query93	1704	1540	983	983
query94	720	357	332	332
query95	781	492	560	492
query96	1050	771	350	350
query97	2442	2453	2354	2354
query98	202	194	191	191
query99	800	731	601	601
Total cold run time: 265311 ms
Total hot run time: 169631 ms

@hello-stephen

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

query1	0.00	0.00	0.01
query2	0.38	0.05	0.05
query3	0.69	0.14	0.13
query4	2.03	0.14	0.14
query5	0.28	0.23	0.23
query6	1.90	0.81	0.82
query7	0.04	0.01	0.00
query8	0.06	0.03	0.04
query9	0.75	0.30	0.32
query10	0.55	0.55	0.55
query11	0.38	0.15	0.13
query12	0.24	0.14	0.15
query13	0.46	0.45	0.48
query14	1.03	1.00	0.99
query15	0.62	0.59	0.60
query16	0.31	0.33	0.33
query17	1.15	1.08	1.12
query18	0.21	0.19	0.21
query19	2.24	1.97	2.08
query20	0.02	0.02	0.01
query21	16.57	0.18	0.14
query22	4.78	0.05	0.05
query23	16.79	0.31	0.13
query24	17.68	0.43	0.34
query25	0.14	0.05	0.04
query26	0.77	0.20	0.15
query27	0.04	0.04	0.04
query28	3.45	0.79	0.36
query29	12.93	4.06	3.20
query30	0.27	0.16	0.17
query31	2.90	0.56	0.32
query32	3.49	0.59	0.51
query33	3.14	3.16	3.15
query34	15.74	3.91	3.27
query35	3.23	3.19	3.22
query36	0.59	0.43	0.45
query37	0.25	0.07	0.06
query38	0.05	0.04	0.03
query39	0.13	0.03	0.03
query40	0.22	0.16	0.14
query41	0.29	0.03	0.03
query42	0.20	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 117.03 s
Total hot run time: 23.95 s

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

morningman and others added 2 commits August 1, 2026 16:50
…he class

ObsFileSystemProperties picks between fs.obs.impl=OBSFileSystem and the
S3AFileSystem fallback with a static probe for
org.apache.hadoop.fs.obs.OBSFileSystem. It probed with Class.forName, which
answers a strictly harder question than the one being asked: loading the class
also links its superclass org.apache.hadoop.fs.FileSystem, and hadoop-common is
deliberately not part of this plugin -- hadoop-huaweicloud declares it provided
and plugin-zip.xml packs only the runtime closure, so lib/ holds the OBS
connector with no hadoop-common behind it.

That has one consequence already visible and one waiting:

- Wherever hadoop-common is absent, Class.forName throws NoClassDefFoundError.
  It is a LinkageError, not a ClassNotFoundException, so the catch did not hold
  it and it aborted the class's static initializer instead; every later touch of
  ObsFileSystemProperties then failed with "Could not initialize class", not
  only the hadoop map. This module's own test classpath is exactly that shape,
  so 25 of its 28 tests fail today.
- Merely widening the catch would trade the crash for a lie. The probe would
  report OBS absent and silently downgrade fs.obs.impl to S3AFileSystem as soon
  as fe-core stops carrying hadoop -- which apache#66324 leaves open as the next step
  -- even though the connector is right there in lib/, and the consumers that
  actually instantiate it (fe-connector-paimon, be-java-extensions/hadoop-deps)
  bring their own hadoop.

Resolving the class file as a resource asks the intended question, against the
same classloader and the same delegation, without linking anything. It is also
what this dependency's comment in pom.xml already claims the probe does: "the
jar has to sit in this plugin for the probe to tell the truth".

The new test pins the native impl and asserts the classpath premise it rests on,
so adding hadoop-common later fails loudly instead of quietly turning the test
into a tautology.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016YeHiB85SmvZyKCq7FzuJD
The FE UT job parses results with the report pattern
fe/*/target/surefire-reports/*.xml. That single wildcard only reaches the 17
modules sitting directly under fe/; the other 56 -- everything under
fe-filesystem/, fe-connector/, fe-authentication/ and be-java-extensions/ -- is
invisible to it. The coverage run also passes -Dmaven.test.failure.ignore=true,
which it needs in order to finish every module and still emit a jacoco report,
so maven exits 0 and the reactor prints SUCCESS for a module whose tests failed.

Together those two make a nested module's failures unobservable: the job goes
green reporting "failed: 0". Build 1012189 is the worked example -- 25 of
fe-filesystem-obs' 28 tests failed there and nothing anywhere said so.

So gate on the reports the parser cannot see, and leave the ones it can reach to
the job itself: it owns those results, and its per-test mutes have to keep
working.

Two things that are easy to get wrong here:

- The obvious way to express "skip the ones the parser already sees" is a [[ ]]
  glob against the parser's own pattern, and it is wrong: inside [[ ]] a *
  matches / as well, so fe/*/target/... also swallows every nested module this
  is meant to catch, and the check silently passes everything. Comparing the
  shape of the module path is exact.
- The totals are read from the root <testsuite> element only, since a stack
  trace quoted inside a later <testcase> can otherwise be mistaken for it.

Scoped to the full run: --run leaves every other module's reports from an
earlier invocation untouched, and those are not that run's results.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016YeHiB85SmvZyKCq7FzuJD
@morningman
morningman force-pushed the connector-plugin-conf branch from 6dc8847 to 09e138c Compare August 1, 2026 08:51
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17659	3870	3931	3870
q2	2404	316	192	192
q3	10296	1379	779	779
q4	4683	464	336	336
q5	7698	833	550	550
q6	175	172	137	137
q7	729	790	597	597
q8	9415	1458	1394	1394
q9	6714	3995	3988	3988
q10	7119	1614	1353	1353
q11	536	344	322	322
q12	771	556	453	453
q13	18223	3248	2739	2739
q14	256	258	233	233
q15	q16	744	737	657	657
q17	1010	985	953	953
q18	6807	5617	5524	5524
q19	1188	1176	1042	1042
q20	786	681	566	566
q21	5652	2457	2280	2280
q22	409	350	290	290
Total cold run time: 103274 ms
Total hot run time: 28255 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4363	4232	4242	4232
q2	277	307	205	205
q3	4496	4911	4360	4360
q4	2127	2239	1408	1408
q5	4226	4102	4080	4080
q6	226	171	132	132
q7	1666	1606	1612	1606
q8	2484	2055	2118	2055
q9	7236	7273	7241	7241
q10	4282	4281	3838	3838
q11	551	416	373	373
q12	687	720	495	495
q13	3125	3425	3051	3051
q14	301	318	276	276
q15	q16	711	733	631	631
q17	1326	1265	1293	1265
q18	7870	7230	7070	7070
q19	1142	1093	1032	1032
q20	2191	2178	1922	1922
q21	5232	4494	4334	4334
q22	526	459	410	410
Total cold run time: 55045 ms
Total hot run time: 50016 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 168296 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 09e138c241759f758577c62b17b02d8507d6393d, data reload: false

query5	4332	600	452	452
query6	480	235	201	201
query7	4875	579	343	343
query8	335	184	164	164
query9	8757	4002	3976	3976
query10	514	355	292	292
query11	5801	2194	2007	2007
query12	146	99	102	99
query13	1282	602	438	438
query14	6073	4668	4363	4363
query14_1	3809	3741	3746	3741
query15	198	192	184	184
query16	1012	438	429	429
query17	1099	669	541	541
query18	2419	447	332	332
query19	226	183	142	142
query20	127	101	102	101
query21	234	154	131	131
query22	13019	13012	12811	12811
query23	17127	16330	15929	15929
query23_1	16080	15980	16049	15980
query24	7540	1684	1245	1245
query24_1	1186	1239	1218	1218
query25	564	461	388	388
query26	1328	364	220	220
query27	2582	594	392	392
query28	4483	2026	2048	2026
query29	1086	617	485	485
query30	345	265	226	226
query31	1122	1075	984	984
query32	107	62	66	62
query33	556	322	256	256
query34	1162	1121	643	643
query35	736	757	643	643
query36	774	770	700	700
query37	160	104	91	91
query38	1817	1633	1604	1604
query39	837	829	784	784
query39_1	774	788	779	779
query40	250	172	151	151
query41	72	68	73	68
query42	96	92	89	89
query43	322	322	275	275
query44	1490	796	772	772
query45	202	186	177	177
query46	1091	1261	747	747
query47	1616	1641	1561	1561
query48	424	435	321	321
query49	637	408	303	303
query50	1037	412	346	346
query51	10429	10804	10378	10378
query52	86	87	77	77
query53	259	283	195	195
query54	290	238	242	238
query55	75	72	69	69
query56	334	318	291	291
query57	1027	1031	936	936
query58	288	286	265	265
query59	1527	1625	1367	1367
query60	330	286	263	263
query61	178	176	179	176
query62	395	317	274	274
query63	231	196	194	194
query64	2994	1004	842	842
query65	3883	3788	3826	3788
query66	1825	470	358	358
query67	28481	28156	27412	27412
query68	3058	1628	1001	1001
query69	406	312	264	264
query70	871	781	739	739
query71	362	333	337	333
query72	2982	2645	2324	2324
query73	820	808	431	431
query74	4577	4487	4310	4310
query75	2355	2363	1997	1997
query76	2316	1105	733	733
query77	341	357	257	257
query78	11100	11045	10574	10574
query79	1453	1091	750	750
query80	1304	523	449	449
query81	524	320	277	277
query82	650	147	114	114
query83	361	323	289	289
query84	286	162	130	130
query85	976	607	515	515
query86	408	226	225	225
query87	1803	1789	1718	1718
query88	3737	2813	2740	2740
query89	390	312	281	281
query90	1929	187	192	187
query91	203	188	156	156
query92	65	57	55	55
query93	1658	1547	978	978
query94	698	350	317	317
query95	762	502	554	502
query96	1053	772	347	347
query97	2469	2443	2376	2376
query98	210	194	196	194
query99	727	728	611	611
Total cold run time: 256023 ms
Total hot run time: 168296 ms

@hello-stephen

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

query1	0.00	0.00	0.00
query2	0.10	0.05	0.05
query3	0.26	0.14	0.14
query4	1.61	0.14	0.14
query5	0.24	0.22	0.22
query6	1.17	0.81	0.85
query7	0.04	0.01	0.01
query8	0.06	0.03	0.03
query9	0.37	0.33	0.32
query10	0.55	0.54	0.58
query11	0.18	0.13	0.15
query12	0.19	0.14	0.15
query13	0.46	0.47	0.46
query14	1.00	1.01	0.99
query15	0.61	0.60	0.59
query16	0.33	0.33	0.33
query17	1.08	1.14	1.11
query18	0.22	0.21	0.20
query19	2.01	2.00	2.00
query20	0.02	0.02	0.01
query21	15.44	0.22	0.13
query22	4.77	0.05	0.05
query23	16.14	0.31	0.12
query24	2.98	0.41	0.33
query25	0.12	0.04	0.05
query26	0.72	0.20	0.15
query27	0.03	0.03	0.04
query28	3.55	0.76	0.34
query29	12.50	3.99	3.20
query30	0.27	0.15	0.16
query31	2.77	0.56	0.32
query32	3.22	0.58	0.49
query33	3.14	3.17	3.18
query34	15.63	3.92	3.25
query35	3.24	3.23	3.26
query36	0.56	0.43	0.42
query37	0.09	0.06	0.06
query38	0.05	0.03	0.03
query39	0.04	0.03	0.03
query40	0.17	0.16	0.15
query41	0.07	0.03	0.03
query42	0.04	0.03	0.03
query43	0.05	0.03	0.03
Total cold run time: 96.09 s
Total hot run time: 24 s

The bucket gate moved onto the plugin conf channel, so it can now be off in
either hms.conf (enable_create_bucket_table) or fe.conf
(enable_create_hive_bucket_table), and the rejection message was reworded to
name both. test_hive_ddl still pinned the old single-key wording and failed.

Updating the assertion rather than the message: with the plugin conf winning,
a message that names only the fe.conf key sends an administrator whose
hms.conf holds the toggle false to edit a file that cannot turn it back on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17605	3926	3902	3902
q2	1981	313	200	200
q3	10316	1343	789	789
q4	4681	475	340	340
q5	7530	833	552	552
q6	174	168	135	135
q7	717	788	602	602
q8	9524	1566	1512	1512
q9	5948	4026	4049	4026
q10	6797	1632	1330	1330
q11	512	366	320	320
q12	742	598	466	466
q13	18142	3339	2743	2743
q14	262	250	236	236
q15	q16	731	731	662	662
q17	1014	943	890	890
q18	6843	5614	5456	5456
q19	1297	1175	975	975
q20	757	674	585	585
q21	5622	2569	2409	2409
q22	426	358	297	297
Total cold run time: 101621 ms
Total hot run time: 28427 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4255	4149	4147	4147
q2	274	315	224	224
q3	4567	4863	4479	4479
q4	2176	2254	1421	1421
q5	4236	4115	4086	4086
q6	224	171	124	124
q7	1682	1549	1375	1375
q8	2549	2140	2044	2044
q9	7412	7197	7189	7189
q10	4318	4257	3882	3882
q11	546	392	365	365
q12	698	720	507	507
q13	3145	3576	3056	3056
q14	329	320	263	263
q15	q16	698	734	630	630
q17	1294	1291	1275	1275
q18	7845	7241	7198	7198
q19	1118	1033	1027	1027
q20	2202	2176	1931	1931
q21	5259	4575	4415	4415
q22	521	447	413	413
Total cold run time: 55348 ms
Total hot run time: 50051 ms

@hello-stephen

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

query5	4303	608	467	467
query6	478	221	196	196
query7	4836	560	357	357
query8	339	182	161	161
query9	8778	4017	3974	3974
query10	477	347	295	295
query11	5845	2230	2047	2047
query12	151	97	100	97
query13	1248	567	465	465
query14	6104	4792	4371	4371
query14_1	3809	3771	3784	3771
query15	207	209	174	174
query16	983	474	433	433
query17	904	673	546	546
query18	2430	451	337	337
query19	199	185	142	142
query20	100	103	99	99
query21	232	155	130	130
query22	13041	13081	12810	12810
query23	17290	16395	16048	16048
query23_1	16186	16089	16138	16089
query24	7474	1697	1218	1218
query24_1	1241	1238	1239	1238
query25	528	422	352	352
query26	1327	355	215	215
query27	2590	558	388	388
query28	4467	2022	2014	2014
query29	1046	593	473	473
query30	340	268	225	225
query31	1109	1069	956	956
query32	106	59	62	59
query33	499	315	245	245
query34	1141	1148	633	633
query35	726	748	648	648
query36	776	772	681	681
query37	152	105	95	95
query38	1844	1677	1575	1575
query39	831	832	789	789
query39_1	788	802	811	802
query40	246	162	144	144
query41	65	64	65	64
query42	91	90	92	90
query43	314	321	275	275
query44	1441	780	756	756
query45	182	182	169	169
query46	1056	1157	735	735
query47	1536	1521	1437	1437
query48	412	408	296	296
query49	575	405	284	284
query50	1071	442	331	331
query51	10738	10520	10275	10275
query52	86	86	81	81
query53	260	274	208	208
query54	285	250	226	226
query55	75	72	66	66
query56	314	309	295	295
query57	1035	1018	929	929
query58	285	258	261	258
query59	1512	1600	1399	1399
query60	303	269	257	257
query61	160	158	149	149
query62	400	317	266	266
query63	253	200	193	193
query64	2807	1088	950	950
query65	3928	3852	3899	3852
query66	1832	508	376	376
query67	28472	28307	28319	28307
query68	3005	1511	962	962
query69	424	308	274	274
query70	883	795	791	791
query71	384	340	327	327
query72	3227	2807	2516	2516
query73	806	711	446	446
query74	4645	4510	4291	4291
query75	2382	2356	2005	2005
query76	2339	1146	758	758
query77	339	360	275	275
query78	11269	11263	10690	10690
query79	1421	1106	767	767
query80	1286	539	468	468
query81	521	335	296	296
query82	627	152	116	116
query83	380	329	298	298
query84	331	165	129	129
query85	966	612	518	518
query86	408	232	225	225
query87	1801	1789	1762	1762
query88	3751	2819	2783	2783
query89	395	319	287	287
query90	1903	200	189	189
query91	205	191	165	165
query92	59	59	55	55
query93	1757	1534	913	913
query94	714	357	339	339
query95	803	623	477	477
query96	1034	776	359	359
query97	2479	2462	2330	2330
query98	203	201	207	201
query99	722	720	605	605
Total cold run time: 256343 ms
Total hot run time: 169547 ms

@hello-stephen

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

query1	0.00	0.00	0.01
query2	0.09	0.05	0.05
query3	0.25	0.14	0.13
query4	1.60	0.14	0.14
query5	0.24	0.22	0.22
query6	1.16	0.82	0.82
query7	0.04	0.01	0.01
query8	0.06	0.03	0.04
query9	0.37	0.31	0.30
query10	0.56	0.57	0.53
query11	0.19	0.13	0.13
query12	0.17	0.14	0.14
query13	0.46	0.48	0.46
query14	0.99	0.98	0.99
query15	0.62	0.60	0.59
query16	0.33	0.32	0.30
query17	1.07	1.11	1.05
query18	0.22	0.19	0.20
query19	2.07	1.90	1.95
query20	0.02	0.01	0.02
query21	15.45	0.23	0.15
query22	4.73	0.05	0.05
query23	16.15	0.31	0.11
query24	2.95	0.43	0.33
query25	0.09	0.05	0.05
query26	0.72	0.20	0.16
query27	0.05	0.04	0.03
query28	3.53	0.73	0.35
query29	12.46	4.11	3.20
query30	0.27	0.16	0.15
query31	2.77	0.55	0.32
query32	3.22	0.58	0.49
query33	3.22	3.19	3.15
query34	15.66	4.00	3.33
query35	3.20	3.23	3.25
query36	0.56	0.45	0.43
query37	0.09	0.06	0.06
query38	0.06	0.04	0.03
query39	0.04	0.03	0.03
query40	0.17	0.16	0.15
query41	0.08	0.04	0.03
query42	0.04	0.02	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.06 s
Total hot run time: 23.86 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 16.67% (10/60) 🎉
Increment coverage report
Complete coverage report

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

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@morningman
morningman merged commit 82646c3 into apache:master Aug 2, 2026
30 of 31 checks passed
morningman added a commit to morningman/doris that referenced this pull request Aug 2, 2026
Both of this connector's deployment-level settings move off fe.conf and into the
plugin's own adbc.conf -- the channel apache#66347 introduced -- as drivers_dir and
driver_secure_path, read through ConnectorConf.get.

Neither has an fe.conf half. The two @ConfFields they used to be
(adbc_drivers_dir, adbc_driver_secure_path) and the two
DefaultConnectorContext env entries that forwarded them are removed rather than
kept as a fallback: this connector has never shipped, so no deployment
configured them anywhere else, and a key in fe-core is an engine change per
connector setting -- which is what that channel exists to stop.

The default drivers directory is computed in the connector from the doris_home
the engine already publishes, so it stays <DORIS_HOME>/plugins/adbc_drivers.
build.sh needs no change: it seeds a live <name>.conf from any *.conf.template
found in a plugin zip.

AdbcConnectorConfTest pins the template's name against ConnectorProvider.name()
-- a template under any other name deploys a file the engine never opens, with
every setting in it silently ignored -- and pins that an environment still
carrying the old fe.conf keys does not resurrect a channel fe-core no longer
feeds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgJjuW5w4jEKF9HorENTur
morningman added a commit to morningman/doris that referenced this pull request Aug 2, 2026
Both of this connector's deployment-level settings move off fe.conf and into the
plugin's own adbc.conf -- the channel apache#66347 introduced -- as drivers_dir and
driver_secure_path, read through ConnectorConf.get.

Neither has an fe.conf half. The two @ConfFields they used to be
(adbc_drivers_dir, adbc_driver_secure_path) and the two
DefaultConnectorContext env entries that forwarded them are removed rather than
kept as a fallback: this connector has never shipped, so no deployment
configured them anywhere else, and a key in fe-core is an engine change per
connector setting -- which is what that channel exists to stop.

The default drivers directory is computed in the connector from the doris_home
the engine already publishes, so it stays <DORIS_HOME>/plugins/adbc_drivers.
build.sh needs no change: it seeds a live <name>.conf from any *.conf.template
found in a plugin zip.

AdbcConnectorConfTest pins the template's name against ConnectorProvider.name()
-- a template under any other name deploys a file the engine never opens, with
every setting in it silently ignored -- and pins that an environment still
carrying the old fe.conf keys does not resurrect a channel fe-core no longer
feeds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgJjuW5w4jEKF9HorENTur
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.

4 participants