Skip to content

Promote validator registry reset() to public lifecycle API and invoke from controller stop#18240

Merged
xiangfu0 merged 1 commit into
apache:masterfrom
suvodeep-pyne:spyne/data-707-validator-registry-clear
Apr 17, 2026
Merged

Promote validator registry reset() to public lifecycle API and invoke from controller stop#18240
xiangfu0 merged 1 commit into
apache:masterfrom
suvodeep-pyne:spyne/data-707-validator-registry-clear

Conversation

@suvodeep-pyne
Copy link
Copy Markdown
Contributor

Summary

  • Promote reset() on TableConfigValidatorRegistry and InstanceConfigValidatorRegistry from a @VisibleForTesting helper to a public lifecycle API. Added unregister(validator) on both registries for API completeness.
  • Invoke TableConfigValidatorRegistry.reset() and InstanceConfigValidatorRegistry.reset() from BaseControllerStarter.stop() so cleanup is automatic for any subclass that registered validators.
  • Class Javadoc on both registries now documents the lifecycle contract.

Motivation

The validator SPI added in #18167 holds validators in static CopyOnWriteArrayLists. In production this is invisible (controller restart = JVM restart). In integration tests that restart the controller in-process — e.g. ControllerTest.restartController() — each start() registers a new validator, but the previous validator stays in the static list and retains references to a now-disconnected HelixAdmin. After N restarts, the oldest stale validator throws when the next config-mutation request iterates it, and the write is rejected with HTTP 400.

The fix is additive:

  • The SPI gains two new public methods; no existing signatures change.
  • Production behavior is unchanged: stop() previously coincided with JVM exit, which already cleared the static registry. Invoking reset() explicitly only affects in-process restart scenarios (integration tests, downstream distributions that restart controllers without JVM restart).

Test plan

  • ./mvnw -pl pinot-spi -am test -Dtest='TableConfigValidatorRegistryTest,InstanceConfigValidatorRegistryTest'
  • ./mvnw -pl pinot-spi spotless:apply checkstyle:check license:format license:check
  • ./mvnw -pl pinot-controller compile
  • ./mvnw -pl pinot-controller spotless:apply checkstyle:check license:format license:check
  • CI green

… from controller stop

TableConfigValidatorRegistry and InstanceConfigValidatorRegistry held validators
in static lists with only a @VisibleForTesting reset() for cleanup. In production
a controller restart is a JVM restart, so the leak is invisible; but integration
tests that restart the controller in-process (ControllerTest.restartController())
accumulate one validator per restart, and the older ones retain references to a
now-disconnected HelixAdmin. The next config-mutation request then dereferences
torn-down Helix state and rejects the write with HTTP 400.

Changes:
- Promote reset() to a public lifecycle API on both registries (drop
  @VisibleForTesting). Document the lifecycle contract in class Javadoc.
- Add unregister(validator) on both registries for API completeness.
- Invoke TableConfigValidatorRegistry.reset() and
  InstanceConfigValidatorRegistry.reset() from BaseControllerStarter.stop() so
  the cleanup is automatic for any subclass that registered validators.

This is additive: existing callers are unaffected, production behavior is
unchanged (stop() = JVM exit), and only in-process restart scenarios observe
a behavior difference — validators no longer leak across restarts.
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 16, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.38%. Comparing base (77ecc4c) to head (8a66754).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...nfig/instance/InstanceConfigValidatorRegistry.java 0.00% 1 Missing ⚠️
...spi/config/table/TableConfigValidatorRegistry.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #18240      +/-   ##
============================================
- Coverage     63.40%   63.38%   -0.03%     
  Complexity     1627     1627              
============================================
  Files          3243     3243              
  Lines        197162   197167       +5     
  Branches      30495    30495              
============================================
- Hits         125009   124970      -39     
- Misses        62151    62193      +42     
- Partials      10002    10004       +2     
Flag Coverage Δ
custom-integration1 100.00% <ø> (ø)
integration 100.00% <ø> (ø)
integration1 100.00% <ø> (ø)
integration2 0.00% <ø> (ø)
java-11 63.35% <60.00%> (-0.02%) ⬇️
java-21 63.36% <60.00%> (-0.01%) ⬇️
temurin 63.38% <60.00%> (-0.03%) ⬇️
unittests 63.37% <60.00%> (-0.03%) ⬇️
unittests1 55.32% <0.00%> (-0.03%) ⬇️
unittests2 34.99% <60.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@xiangfu0 xiangfu0 merged commit c2a648e into apache:master Apr 17, 2026
31 of 32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants