xiangfu0 opened a new pull request, #19262:
URL: https://github.com/apache/pinot/pull/19262

   > **Stacked on #19259** — this branch includes that PR's commit; only the 
second commit is new here. Will rebase once #19259 merges.
   
   ## Summary
   
   Follow-up to #19259. Three call sites hand-rolled the same ServiceLoader 
enumeration pattern — load from the thread context classloader, then from every 
classloader returned by `PluginManager.get().getPluginClassLoaders()`, 
de-duplicating providers by fully-qualified class name:
   
   - `PinotRuleSet.loadFromServiceLoader()` (pinot-query-planner)
   - `OperatorTypeRegistry` static initializer (pinot-query-runtime)
   - `TransformFunctionFactory.registerServiceProviders(...)` (pinot-core)
   
   This PR extracts the shared mechanism into pinot-spi as 
`PluginManager#loadServiceProviders(Class)` and migrates all three call sites. 
Any future fix to the enumeration mechanism (dedup semantics, realm ordering, 
error context) now lands in one place.
   
   ## The helper
   
   ```java
   List<PluginManager.ServiceProvider<MyService>> providers =
       PluginManager.get().loadServiceProviders(MyService.class);
   ```
   
   - Enumerates the thread context classloader first (the application classpath 
in a standard deployment), then every plugin classloader, in load order.
   - De-duplicates providers by fully-qualified class name across classloaders 
(overlapping classpaths — e.g. fat-jar + plugin realm — surface the same 
provider through multiple loaders; the first sighting wins). A same-named 
provider whose `Class` object differs from the first sighting indicates version 
skew between classloaders and is logged at WARN before being skipped.
   - Each returned `ServiceProvider<S>` pairs the provider instance with a 
human-readable source description for call-site log/error messages.
   - Enumeration failures (`ServiceConfigurationError` — malformed descriptors, 
missing/incompatible/unconstructable provider classes) fail fast as 
`IllegalStateException` carrying the source description with the original error 
preserved as the cause.
   - Per-provider validation and registration policy stay with the caller 
(id-floor checks in `OperatorTypeRegistry`, name canonicalization/collision 
rules in `TransformFunctionFactory`, ordering in `PinotRuleSet`).
   
   ## New public SPI surface (reviewer attention)
   
   This adds a permanent additive contract to pinot-spi that plugin authors can 
depend on: `PluginManager#loadServiceProviders(Class)` and the nested immutable 
`PluginManager.ServiceProvider<S>` pair type, with these semantics: 
context-classloader-first discovery order, first-sighting-wins dedup by class 
name, WARN-and-skip on classloader version skew, an unmodifiable freshly 
computed result list, and fail-fast `IllegalStateException` wrapping of 
`ServiceConfigurationError` with source context. No existing signature changed; 
`getPluginClassLoaders()` remains for callers needing the raw classloaders.
   
   ## Behavior notes
   
   - `TransformFunctionFactory`: no behavior change — it previously used the 
exact logic now hosted in the helper.
   - `PinotRuleSet` / `OperatorTypeRegistry` (deliberate unification onto the 
`TransformFunctionFactory` fail-fast contract): a malformed descriptor or 
failing provider constructor now surfaces as `IllegalStateException` with 
classloader-source context and the `ServiceConfigurationError` as cause, 
instead of the raw error propagating with no context (from 
`OperatorTypeRegistry`'s static initializer both variants surface as 
`ExceptionInInitializerError`). Both paths failed startup/class-init before and 
still do; the failure just carries more information. Their silent same-FQCN 
dedup now WARNs when the duplicate is a genuinely different class (version 
skew). Error precedence can also shift: providers are fully enumerated before 
caller-side validation runs, so an enumeration failure from a later classloader 
may now surface before a caller-side validation error that previously fired 
first — either way startup fails.
   - All three modules already depended on pinot-spi; no layering change.
   
   ## Testing
   
   - New `PluginManagerServiceProviderTest` (pinot-spi): context-classloader 
discovery, plugin-realm discovery, cross-classloader dedup, discovery order, 
malformed-descriptor and constructor-failure wrapping (source + cause 
asserted), empty result. Descriptors are generated into temp dirs behind 
isolated `URLClassLoader`s and fresh `PluginManager` instances, so no fixture 
leaks into the test classpath.
   - Existing suites pass unchanged: `TransformFunctionFactoryTest` (23), 
`ServerInstanceTransformFunctionTest` (3), `PinotRuleSetTest`, 
`OperatorTypeRegistryTest`, `PluginRealmExportTest`.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to