Repository navigation
[improve][io] Replace Spring Framework with cron-utils in CronTriggerer - #26269
Merged
Merged
Conversation
### Motivation `CronTriggerer` was the only production use of the Spring Framework in the Pulsar code base. It pulled `spring-context` and `spring-core` (3.3 MB) into the batch-source connector NARs just to parse a cron expression and run a timer. Removing the dependency also removes Pulsar's exposure to Spring Framework CVEs. The pinned version, 6.2.12, falls within the affected range (6.2.0 -> 6.2.18.1) of four CVEs published on 2026-06-09: - CVE-2026-41848: denial of service via ReDoS in `AntPathMatcher` - CVE-2026-41850: algorithmic denial of service via SpEL expressions - CVE-2026-41851: denial of service via an unbounded SpEL expression cache - CVE-2026-41852: arbitrary zero-argument method invocation in SpEL Pulsar only used Spring's cron scheduling, so it neither evaluated SpEL nor used `AntPathMatcher`, and was unlikely to be exploitable through those paths. Dropping the dependency nevertheless removes the flagged artifacts from the connector NARs and ends the need to track Spring CVEs at all. ### Modifications - Replace `ThreadPoolTaskScheduler` + `CronTrigger` with cron-utils (`CronType.SPRING53`; 175 kB, Apache-2.0, `slf4j-api` its only dependency) and a self-rescheduling single-threaded `ScheduledExecutorService`. The next firing is derived from the completion time of the previous one, so firings never overlap, matching how Spring's `CronTrigger` behaved. - Lower-case leading-`@` expressions before parsing: Spring matched the `@daily` style macros case-insensitively, cron-utils only accepts lower case. - Parse the expression in `init()`, so a malformed expression fails there rather than at the first scheduling attempt. - Drop the now unused `spring-context` from `batch-data-generator` (cron-utils is bundled into the NAR transitively) and `spring-core` from the broker test dependencies, replacing the single `CollectionUtils.isEmpty()` use in `AdminApiHealthCheckTest` with `List.isEmpty()`. - Remove the `spring` version and both library aliases from the version catalog, and wire up the previously orphaned `cron-utils` version, bumped 9.1.6 -> 9.2.1 because `CronType.SPRING53` requires 9.2.0+. The cron dialect is preserved. A differential harness over 4,589,882 next-fire comparisons (528 expressions x 8 zones x 37 start instants x 6 chained steps) found no parsing differences and 99.9944% identical firing times. All 255 differing results are in DST-observing zones, where an expression falling in a shifted or repeated hour can lose one firing per year; there were none in fixed-offset zones. ### Verifying this change Adds `CronTriggererTest`, covering the cron dialect, scheduling, stop behaviour, thread naming and trigger-failure handling. Its expected firing times were generated from Spring itself, so they pin down the previous behaviour. Assisted-by: Claude Code (Opus 5)
dao-jun
approved these changes
Aug 4, 2026
merlimat
approved these changes
Aug 4, 2026
2 of 11 tasks
Radiancebobo
pushed a commit
to Radiancebobo/pulsar
that referenced
this pull request
Oct 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
CronTriggererwas the only production use of the Spring Framework in the Pulsar code base. It pulledspring-contextandspring-core(3.3 MB) into the batch-source connector NARs just to parse a cron expression and run a timer.Removing the dependency also removes Pulsar's exposure to Spring Framework CVEs. The pinned version, 6.2.12, falls within the affected range (
6.2.0->6.2.18.1) of four CVEs published on 2026-06-09:AntPathMatcher(CWE-1333)Pulsar only used Spring's cron scheduling, so it neither evaluated SpEL nor used
AntPathMatcher, and was unlikely to be exploitable through those paths. Dropping the dependency nevertheless removes the flagged artifacts from the connector NARs and ends the need to track Spring CVEs at all.Modifications
ThreadPoolTaskScheduler+CronTriggerwith cron-utils (CronType.SPRING53; 175 kB, Apache-2.0,slf4j-apiits only dependency) and a self-rescheduling single-threadedScheduledExecutorService. The next firing is derived from the completion time of the previous one, so firings never overlap — matching how Spring'sCronTriggerbehaved.@expressions before parsing: Spring matched the@dailystyle macros case-insensitively, cron-utils only accepts lower case.init(), so a malformed expression fails there rather than at the first scheduling attempt.spring-contextfrombatch-data-generator(cron-utils is bundled into the NAR transitively) andspring-corefrom the broker test dependencies, replacing the singleCollectionUtils.isEmpty()use inAdminApiHealthCheckTestwithList.isEmpty().springversion and both library aliases from the version catalog, and wire up the previously orphanedcron-utilsversion, bumped9.1.6->9.2.1becauseCronType.SPRING53requires 9.2.0+.Backward compatibility of the cron dialect
Existing
__CRON__configurations must keep working, so the replacement was validated against Spring rather than assumed. A differential harness comparedCronExpression(Spring 6.2.12) with cron-utilsSPRING53over 4,589,882 next-fire comparisons — 528 expressions x 8 time zones x 37 start instants x 6 chained steps:L/W/LW/#/?, named and numeric day-of-week, all seven macros, whitespace and case all agree, and both reject the same malformed input withIllegalArgumentException.Asia/Kolkata,America/Sao_Paulo).Quartz was also evaluated and rejected: it cannot parse 15 of 17 of the expressions used here — including
* * * * * *fromBatchSourceTest— because it forbids specifying both day-of-month and day-of-week, and it numbers day-of-week1=SUNagainst Spring's1=MON, which would silently shift every numeric schedule by a day.Verifying this change
This change added tests and can be verified as follows:
CronTriggererTest(33 cases), covering the cron dialect, repeated scheduling,stop()behaviour, thread naming, non-overlapping firings and trigger-failure handling. The expected firing times were generated from Spring itself, so they pin down the previous behaviour.CronTriggererTestgreen over 6 consecutive runs with retries disabled;quickCheck,sanityCheck,spotlessCheck,checkstyleMain,checkstyleTestall pass;BatchSourceExecutorTest,BatchSourceConfigParseTest,SourceConfigUtilsTest,TestCmdSourcesandAdminApiHealthCheckTestpass.pulsar-io-batch-data-generatorNAR now bundlescron-utils-9.2.1.jarand no Spring jars.Does this pull request potentially affect one of the following parts:
If the box was checked, please highlight the changes
Dependency changes: removes
org.springframework:spring-contextandorg.springframework:spring-core(6.2.12) from the build entirely; addscom.cronutils:cron-utils9.2.1 (Apache-2.0) topulsar-io-batch-discovery-triggerers. No LICENSE/NOTICE updates are needed — the server and shell distributions exclude.narfiles, so the Spring jars were never listed there.