Skip to content

[feature] Add ModuleFactory/IndexFactory SPI and XQuery configuration observability API - #6551

Open
duncdrum wants to merge 14 commits into
eXist-db:developfrom
duncdrum:dp-module-spi
Open

duncdrum wants to merge 14 commits into
eXist-db:developfrom
duncdrum:dp-module-spi

Conversation

@duncdrum

@duncdrum duncdrum commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

ServiceLoader-based SPI for XQuery modules and index modules, so bundled implementations are auto-discovered without explicit conf.xml entries. New installs get a leaner default config; existing hand-maintained configs are unaffected — explicit entries always win over SPI, and enabled="no" (from #6550) suppresses an SPI entry without removing the JAR.

Also delivers the XQuery observability API this SPI/@enabled work created a need for (#6563): with modules and indexes now able to be active with no conf.xml entry at all, or silently suppressed, the DBA needs a way to ask "what's loaded and why."

Closes #3062 — delivers both parts: the @enabled attribute (#6550) and the SPI/CDI-style autodiscovery this PR adds.
Closes #6563 — the observability API this and #6550 created a need for.

What changed

ModuleFactory SPI — new org.exist.xquery.ModuleFactory interface; Configuration.configureModules() scans ServiceLoader<ModuleFactory> before the conf.xml loop. All 27 bundled XQuery modules register via META-INF/services/.

IndexFactory SPI — new org.exist.indexing.IndexFactory interface; IndexManager registers SPI entries whose id has no live conf.xml entry. All bundled indexes (Lucene, ngram, range, sort, spatial) wired; index name defaults to the SPI id when there's no conf.xml element to supply one; guarded against a null/blank id from the factory.

Vector model registry — integrated with Configuration; @enabled on <vector-models> children; repeated WARN for unavailable models downgraded to DEBUG after the first occurrence.

exist-distribution/src/main/config/conf.xml — <builtin-modules> and index <modules> sections trimmed; the 27 bundled modules and 5 bundled indexes no longer listed individually.

Module/index registration provenance — new ModuleRegistration record and an enabled field on the existing IndexModuleConfig record, populated alongside (not instead of) the active classMap/PROPERTY_INDEXER_MODULES — so instantiation behavior is unchanged, but every module and index now carries a retained provenance record (spi/conf.xml/built-in, and whether currently suppressed).

New system: observability functions (DBA-only) — landed in the system: namespace per discussion on #6563 (2026-07-08 → 2026-08-19): reusing util: was rejected as adding to an already-mixed bag, and a new config:/conf:/db: namespace was rejected as a compatibility hazard and unnecessary churn.

  • system:get-registered-modules() — util:registered-modules-info()'s content extended with registration-source (spi/conf.xml/built-in) and enabled, including enabled="no"-suppressed entries.
  • system:get-registered-indexes() — same shape for index modules; no equivalent existed before.
  • system:get-configuration() — the effective parsed conf.xml as an in-memory element, with credential-shaped attribute/element values redacted (including eXist's own <parameter name="password" value="..."/> idiom).
  • system:get-configuration-schema-version($schema) — reads the SchemaVersion build-time constants by schema name.
  • system:get-configuration-property($path) — an XPath/XQuery accessor into the same redacted document, evaluated by eXist's own XQuery engine.

Deprecated util: functions — util:registered-modules-info(), util:mapped-modules(), util:is-module-mapped() deprecated in place (no content change) in favor of system:get-registered-modules().

Schema HTTP endpoint — new SchemaServlet serves $EXIST_HOME/schema/*.xsd unauthenticated at /exist/schema/{name}.xsd (wired via controller-config.xml, same pattern as /status → JMXServlet), so external tooling (IDE plugins, editors) can resolve a config file's grammar without vendoring its own copy.

Compatibility

Explicit conf.xml entries always take precedence over SPI discovery for the same namespace URI/index id — no upgrade action required. enabled="no" suppresses a bundled module/index without removing its JAR.

The three deprecated util: functions keep their exact current behavior — deprecation is metadata-only, not a breaking change.

Test plan

  • mvn validate (repo-wide) — schema governance / canonical instance validation passes
  • mvn test -pl exist-core -Dtest="xquery.system.SystemTests" — new system: observability function XQSuite coverage passes
  • mvn test -pl exist-core -Dtest="xquery.util.UtilTests" — existing util: coverage passes unchanged (deprecation is non-breaking)
  • mvn test -pl exist-core -Dtest="org.exist.util.ConfigurationTest,org.exist.xquery.functions.system.ConfigurationRedactorTest,org.exist.http.servlets.SchemaServletTest" — registry provenance (incl. an injected enabled="no" module/index), credential redaction, and SchemaServlet path-traversal/404 handling
  • Codacy PMD audit (local .codacy/cli.sh) clean on all new/touched files
  • Manual: start eXist-db with the trimmed conf.xml; all 27 built-in modules available in eXide
  • Manual: start eXist-db; Lucene/ngram/range indexes active with no explicit conf.xml entries
  • Manual: enabled="no" on a module's conf.xml entry → not loaded despite SPI discovery, but reported (enabled: "no") by system:get-registered-modules()
  • Manual: curl http://localhost:8080/exist/schema/conf.xsd returns the XSD unauthenticated

🤖 Generated with Claude Code

@duncdrum
duncdrum requested a review from a team as a code owner July 6, 2026 18:38
@duncdrum duncdrum added the needs documentation Signals issues or PRs that will require an update to the documentation repo label Jul 6, 2026
Comment thread exist-core/src/main/java/org/exist/util/SaxonConfiguration.java Outdated

@line-o line-o left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Resolving modules to load by default over SPI received pushback on the community call on 2026-07-06
Security concerns need to be addressed before we can consider pulling this in.

@dizzzz specific concern was: "I see autoloading modules that are not mentioned in the configuration as a risk."

My concern is: if bundled modules are left out of the configuration I have to know they are there in order to disable them again.

Comment thread exist-core/src/test/java/org/exist/util/SchemaVersionSyncTest.java Outdated
@dizzzz

dizzzz commented Jul 12, 2026

Copy link
Copy Markdown
Member

That said, I had similar ideas re module loading a long time ago. "plug the modules you only need".
Effectively nothing changes. If one adds a new module, it will be loaded, but the author can decide differently. In that respect I am happy with this change.

My original ideas can from a slightly different angle: will this make loading java modules from a XAR file more simple?

Comment thread exist-core/src/main/java/org/exist/collections/MutableCollection.java Outdated
Comment thread exist-core/src/main/java/org/exist/collections/MutableCollection.java Outdated
Comment thread exist-core/src/main/java/org/exist/indexing/IndexManager.java

@dizzzz dizzzz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it looks that this PR combines 2 or 3 PRs (which makes the PR more difficult to review than strictly needed).

Comment thread exist-core/src/main/java/org/exist/jetty/JettyStart.java
@duncdrum

Copy link
Copy Markdown
Contributor Author

These are stacked PRs that all target develop as their GitHub base — so GitHub shows the full accumulated diff of the entire chain (schema governance → XSD 1.1 validation → fixture codegen → conf @enabled → module SPI) rather than just the incremental commits. The actual changes belonging to this PR are the commits in dp-module-spi above dp-conf-enabled-attr. The MutableCollection.java and JettyStart.java changes you see belong to dp-xsd11-validation (#6530) — they'll disappear from this diff once the earlier PRs merge.

MutableCollection.java:106 (Namespaces.java)
Good catch — XML_SCHEMA_NS was unused (callers already used XMLConstants directly), and XSD_1_1_NS is now Namespaces.XSD_1_1_NS (added to Namespaces.java). Both fixed in f7f3463460 on dp-xsd11-validation.

MutableCollection.java:116 (Caffeine / separate class)
The design concern is valid. The ConcurrentHashMap is correct here (the namespace set is finite and admin-controlled, so no eviction is needed), but moving the cache and XSD resolution logic out of MutableCollection into a dedicated class is worthwhile cleanup. Deferred to a follow-up PR so it doesn't block this chain.

IndexManager.java:146 (null/blank id)
Fixed in f52bf39b57. Configuration.configureIndexer() now validates factory.getDefaultId() before constructing an IndexModuleConfig entry (logs WARN and skips), with a belt-and-suspenders guard in IndexManager.initIndex() as well.

JettyStart.java:125 (different PR)
Agreed — split to standalone #6572. Only JettyStart.java is touched in that commit and the fix is fully independent of the XSD work.

On your question about XAR module loading:
Not directly — ServiceLoader.load(ModuleFactory.class, Configuration.class.getClassLoader()) only scans the main classpath, so a XAR's META-INF/services wouldn't be picked up automatically. But the abstraction is right: if XAR deployment registers factories with the EXistClassLoader (or a future Configuration.registerModuleFactory() hook), discovery would follow. This PR establishes the interface and wire-up pattern; XAR integration would be a natural extension.

@duncdrum
duncdrum force-pushed the dp-module-spi branch 2 times, most recently from 153a184 to 11d1c92 Compare July 13, 2026 15:07
for (int i = 0; i < params.getLength(); i++) {
final Element param = ((Element) params.item(i));

if ("no".equalsIgnoreCase(param.getAttribute("enabled"))) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"no" or "false" or both?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

"no" only — @enabled's type in conf.xsd is yes_no, an enumeration restricted to exactly yes/no (not xs:boolean), so "false" isn't a valid value to begin with. All 5 call sites (4 in Configuration.java, this one) check the same literal for that reason.

@duncdrum
duncdrum force-pushed the dp-module-spi branch 2 times, most recently from 9e0973c to c03d824 Compare August 19, 2026 10:13
@github-actions

github-actions Bot commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

📊 XQTS result comparison

Comparison of this run against develop.

Metric develop this run Change
➖ Passed 28,868 (90.74%) 28,868 (90.74%) 0 (0.00 pp)
➖ Failures 1,546 1,546 0
➖ Errors 134 134 0
➖ Skipped 1,267 1,267 0
🧪 Total tests 31,815 31,815 0

Relative to develop: 0 newly passing, 0 newly failing, 0 new errors, 0 newly skipped — counting only tests recorded in both runs whose outcome changed.

Runtime: 456.6s (+62.40s vs develop).

@duncdrum duncdrum changed the title [feature] Add ModuleFactory and IndexFactory SPI for auto-discovery of bundled modules and indexes [feature] Add ModuleFactory/IndexFactory SPI and XQuery configuration observability API Sep 17, 2026
duncdrum added a commit to duncdrum/exist that referenced this pull request Sep 17, 2026
…review

Addresses findings from a full-diff code review of the ModuleFactory/IndexFactory
SPI and system: configuration observability work, plus the schema-governance and
Codacy issues surfaced on the PR's CI run:

- Configuration.java: SPI IndexFactory auto-discovery was gated behind the
  pre-existing "no <modules> element" early return, so a conf.xml without an
  <indexer><modules> block silently got no SPI-registered indexes. The guard now
  only skips the conf.xml-parsing loop, not SPI discovery.
- VectorEmbeddingService: replaced a non-atomic get/check/create/put with
  cache.computeIfAbsent() so concurrent getProviderByPath() calls for the same
  uncached model can't both load an ONNX model (the losing instance leaked).
- ModelRegistry: configure() now writes the singleton under the same
  synchronized(ModelRegistry.class) lock getInstance() reads under, closing a
  race where a concurrent getInstance() could win and build a stale fallback
  registry that configure() would then silently overwrite.
- GetConfigurationProperty: corrected the docs to say what the function actually
  does (compiles and runs $path as a full XQuery expression, not a restricted
  XPath subset) so the capability isn't assumed safe if ever reused for a
  lesser-privileged role.
- IndexManager/Configuration: added an explicit source field (built-in/spi/
  conf.xml) to IndexModuleConfig, mirroring ModuleRegistration, and register the
  always-on structural index into the registry so it shows up in
  system:get-registered-indexes() instead of being silently omitted.
- VectorStoreServiceImpl: replaced three Class.forName().getMethod().invoke()
  reflection call sites with a new VectorExtensionHook SPI (ServiceLoader-
  discovered), mirroring the ModuleFactory/IndexFactory pattern this same PR
  introduced, instead of ad hoc reflection for the same "optional extension
  registers with core" problem.
- GetRegisteredModules: dropped a throwaway XQueryContext that reflectively
  re-instantiated every built-in module class on each call; reuses the live
  calling context instead, which already has them loaded.
- schema/conf.xsd + conf.xml, schema/controller-config.xsd + controller-config.xml:
  bumped xs:schema/@Version per schema/README.md's governance policy — conf.xsd's
  new <vector-models enabled> attribute is a MINOR addition, and
  controller-config.xml's new /schema/ forward entry required a PATCH bump on
  its paired schema even though the schema's own content didn't change.
- SchemaServletTest: three tests only called EasyMock.verify(...), which Codacy's
  "JUnit tests should include assert()" check doesn't recognize; wrapped in
  assertThatCode(...).doesNotThrowAnyException() so the existing check is
  visible to the linter.

ConfigurationRedactor's per-call redaction cost was investigated but left as-is:
eXist's memtree NodeImpl throws UnsupportedOperationException for
setUserData/getUserData, and caching a memtree Document across unrelated
XQueryContext instances is unverified/risky, so no safe low-risk fix applies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@duncdrum
duncdrum force-pushed the dp-module-spi branch 2 times, most recently from 4cf0378 to 4598cc7 Compare September 17, 2026 09:49
@duncdrum duncdrum added this to v7.0.0 Sep 22, 2026
@duncdrum duncdrum moved this to In review in v7.0.0 Sep 22, 2026
@duncdrum duncdrum added this to the eXist-7.0.0 milestone Sep 22, 2026
@duncdrum

duncdrum commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

@dizzzz go time?

@duncdrum
duncdrum force-pushed the dp-module-spi branch 2 times, most recently from 832c342 to 25464c0 Compare October 8, 2026 11:07
duncdrum and others added 14 commits October 10, 2026 12:48
…nonical conf.xml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…abled to <vector-models>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…canonical conf.xml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…f.xml element

AbstractIndex.configure() only sets name from config.getAttribute("id") when
config != null. IndexFactory SPI registration passes config=null, leaving name
null. IndexController.getWorkerByIndexName() matches on name, so SPI-registered
indexes (range-index, ngram-index, sort-index, lucene-index) were never found,
causing NPE at every range:index-keys-for-field() call.

Fix: after configure(), if name is still null, call setName(id) with the
configuration id that was used to key the indexers map.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…es covered by existing imports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
factory.getDefaultId() is a third-party contract; a null or blank
return would silently register the index under a useless key and
leave its name unset. Validate at the Configuration.java SPI loop
(primary gate) and add a belt-and-suspenders check in
IndexManager.initIndex so the name is never set to null/blank.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ModuleFactory/IndexFactory SPI autodiscovery plus enabled="no"
suppression opened a gap: a module or index can now be active with no
conf.xml entry at all, and a suppressed entry is discarded at startup
with no trace. Neither is reportable.

Adds a new ModuleRegistration record and, for indexes, an `enabled`
field on the existing IndexModuleConfig record, both populated
alongside (not instead of) the classMap/PROPERTY_INDEXER_MODULES that
actually drive module/index instantiation - so active behavior is
unchanged, but every module and index now has a retained provenance
record (spi/conf.xml/built-in, and whether currently suppressed) for
the new system: functions added in a following commit.

Configuration also now retains its parsed conf.xml Document, needed by
system:get-configuration() and system:get-configuration-property().

Refs eXist-db#6563

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New DBA-only system: functions:

- get-registered-modules() - util:registered-modules-info()'s content
  extended with registration-source (spi/conf.xml/built-in) and
  enabled, including enabled="no"-suppressed entries.
- get-registered-indexes() - same shape for index modules; no
  equivalent existed before.
- get-configuration() - the effective parsed conf.xml as an in-memory
  element, with credential-shaped attribute/element values redacted
  (ConfigurationRedactor), including eXist's own
  <parameter name="password" value="..."/> idiom.
- get-configuration-schema-version($schema) - reads the SchemaVersion
  build-time constants by schema name.
- get-configuration-property($path) - an XPath/XQuery accessor into
  the same redacted document get-configuration() returns, evaluated by
  eXist's own XQuery engine against the redacted root (the parsed
  conf.xml is eXist's own in-memory DOM, which standard javax.xml.xpath
  cannot traverse).

Landed in the system: namespace per discussion thread (2026-07-08 to
2026-08-19): reusing util: was rejected as adding to an already-mixed
bag, and a new config:/conf:/db: namespace was rejected as a
compatibility hazard (config: in particular collides with common
existing user code) and unnecessary churn - system: already hosts
get-running-xqueries() and other DBA-only introspection functions.

Refs eXist-db#6563

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
util:registered-modules-info(), util:mapped-modules(), and
util:is-module-mapped() are now fully covered by
system:get-registered-modules() (mapped-modules()/is-module-mapped()
overlap it completely - same backing data, PROPERTY_STATIC_MODULE_MAP;
registered-modules-info()'s content is a strict subset). Deprecated in
place with no content change, so existing callers keep working
unchanged; only the FunctionSignature gains a deprecation notice
pointing at the replacement.

Refs eXist-db#6563

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
$EXIST_HOME/schema/*.xsd is shipped in every distribution layout but
was only reachable on the local filesystem. Adds SchemaServlet, wired
via controller-config.xml's URL-rewriting pipeline the same way
JMXServlet's /status is, serving a bare "<name>.xsd" filename under
that directory over HTTP with no authentication - the schemas are
plain, publicly-shippable XSDs with no sensitive content, so external
tooling (IDE plugins, editors) can resolve a config file's grammar
without vendoring its own copy.

Guards against path traversal by rejecting any request path with more
than one path segment, and independently by normalizing the resolved
file path and confirming it stays under the schema directory.

Refs eXist-db#6563

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…PI work

Addresses findings from a full-diff code review of the ModuleFactory/IndexFactory
SPI and system: configuration observability work, plus schema-governance and
static-analysis issues surfaced by CI:

- Configuration.java: SPI IndexFactory auto-discovery was gated behind the
  pre-existing "no <modules> element" early return, so a conf.xml without an
  <indexer><modules> block silently got no SPI-registered indexes. The guard now
  only skips the conf.xml-parsing loop, not SPI discovery.
- VectorEmbeddingService: replaced a non-atomic get/check/create/put with
  cache.computeIfAbsent() so concurrent getProviderByPath() calls for the same
  uncached model can't both load an ONNX model (the losing instance leaked).
- ModelRegistry: configure() now writes the singleton under the same
  synchronized(ModelRegistry.class) lock getInstance() reads under, closing a
  race where a concurrent getInstance() could win and build a stale fallback
  registry that configure() would then silently overwrite.
- GetConfigurationProperty: corrected the docs to say what the function actually
  does (compiles and runs $path as a full XQuery expression, not a restricted
  XPath subset) so the capability isn't assumed safe if ever reused for a
  lesser-privileged role.
- IndexManager/Configuration: added an explicit source field (built-in/spi/
  conf.xml) to IndexModuleConfig, mirroring ModuleRegistration, and register the
  always-on structural index into the registry so it shows up in
  system:get-registered-indexes() instead of being silently omitted.
- registered-indexes.xqm: updated invalid-registration-source's allowed values to
  include "built-in" and added a dedicated assertion that structural-index reports
  registration-source "built-in" and enabled "yes" - the old test predated the
  structural-index registry fix above and failed once it always appeared.
- VectorStoreServiceImpl: replaced three Class.forName().getMethod().invoke()
  reflection call sites with a new VectorExtensionHook SPI (ServiceLoader-
  discovered), mirroring the ModuleFactory/IndexFactory pattern, instead of ad
  hoc reflection for the same "optional extension registers with core" problem.
- GetRegisteredModules: dropped a throwaway XQueryContext that reflectively
  re-instantiated every built-in module class on each call; reuses the live
  calling context instead, which already has them loaded.
- schema/conf.xsd + conf.xml, schema/controller-config.xsd + controller-config.xml:
  bumped `xs:schema/@version` per schema/README.md's governance policy: conf.xsd's
  new <vector-models enabled> attribute is a MINOR addition, and
  controller-config.xml's new /schema/ forward entry required a PATCH bump on
  its paired schema even though the schema's own content didn't change.
- SchemaServletTest: three tests only called EasyMock.verify(...), which Codacy's
  "JUnit tests should include assert()" check doesn't recognize; wrapped in
  assertThatCode(...).doesNotThrowAnyException() so the existing check is
  visible to the linter.
- VectorExtensionHook: documented the three intentionally-empty default method
  bodies (Codacy: "Document empty method body").
- VectorExtensionLifecycle: dropped the explicit no-arg constructor in favor of
  the implicit one ServiceLoader already uses (Codacy: "Avoid unnecessary
  constructors").

ConfigurationRedactor's per-call redaction cost was investigated but left as-is:
eXist's memtree NodeImpl throws UnsupportedOperationException for
setUserData/getUserData, and caching a memtree Document across unrelated
XQueryContext instances is unverified/risky, so no safe low-risk fix applies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
VectorStoreServiceImpl discovers VectorExtensionLifecycle via ServiceLoader
(META-INF/services/org.exist.storage.vector.VectorExtensionHook) instead of the
Class.forName().getMethod().invoke() reflection it previously used. That
dispatch path had no dedicated regression test: VectorOperationMetricsTest and
VectorEmbeddingJmxTest exercise the hooks' own behavior, but only incidentally
prove the SPI discovery works, and only for the startup half of the lifecycle.

Verified the new test actually catches the regression it targets: temporarily
removed the META-INF/services registration (source file and stale target/classes
copy) and confirmed both tests fail with the bridge never getting registered,
then restored the file and confirmed the suite is green again.

Add VectorExtensionHookWiringTest with two tests that only pass if the SPI
dispatch fires automatically through a real BrokerPool boot/shutdown - neither
test ever calls VectorExtensionLifecycle directly:

- startupHookRegistersMetricsBridgeWithoutManualWiring: recordEmbed() reaches
  the metrics bridge immediately after boot.
- shutdownHookUnregistersMetricsBridgeOnRealBrokerPoolShutdown: after a real
  stopDb(), recordEmbed() becomes a silent no-op, proving the shutdown hook
  actually unregistered the bridge rather than it merely still being absent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nsionHook tradeoff

Addresses two follow-ups from a senior-level review of the module/index SPI work:

- GetRegisteredModules and ModuleInfo (util:registered-modules(),
  util:registered-modules-info()) independently walked the same three module
  sources (built-in, EXPath package, conf.xml-mapped) with their own copies of
  the dedup/prefix-lookup logic. ModuleInfo's copies also still had the
  throwaway-XQueryContext performance bug already fixed in GetRegisteredModules
  (reflectively re-instantiating every built-in module class per call), since
  fixing it there didn't touch ModuleInfo's separate implementation.
- Extracted the walk into org.exist.xquery.LiveModules.collect(XQueryContext),
  colocated with Module/XQueryContext/ModuleRegistration rather than under
  either function package, since util:'s deprecated functions depending on
  system:'s implementation classes would be an odd direction. Both callers now
  map the same List<LiveModules.Entry> into their own output shape; the perf
  fix now benefits both instead of only the one that was touched directly.
  Net effect: -128 lines of duplicated logic, all three functions' behavior
  verified unchanged (UtilTests 69/69, SystemTests 36/36).
- VectorExtensionHook: added a design-note doc comment explaining why this is a
  separate, narrower SPI rather than making BrokerPoolService itself
  ServiceLoader-discoverable (which already has the same configure/startSystem/
  shutdown shape) - broader change to core startup sequencing than warranted
  for replacing one extension's reflection. Left as a documented tradeoff, not
  built speculatively; flagged as the thing to revisit if a second optional
  extension needs the same trick.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018PcvseE618pZu4eVbKtCW5

This branch has not been deployed

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

Labels

needs documentation Signals issues or PRs that will require an update to the documentation repo

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

[feature] XQuery observability API for effective server configuration modify conf.xml

5 participants