Bug: SchedulerInitializer only registers one scheduler method per service
Summary
SchedulerInitializer discovers scheduler methods into a Map<Service, KFunction<*>>. When a service class exposes multiple valid scheduler methods, each discovered method overwrites the previous one for the same service key.
Result: only one scheduler method per service is validated and invoked.
Impact
This silently drops valid scheduled jobs when a service defines more than one SchedulerJobHandle registration method.
In a real backend integration, this caused:
scheduleStoryContinuityJobProcessor() to be registered and executed
scheduleEmbeddingReindexProcessor() (and other schedule methods in the same service) to never register
The queue stayed pending indefinitely (attempt_count=0, no execution logs), while other schedulers ran normally.
Affected code
katalyst-scheduler module
File: io/github/darkryh/katalyst/scheduler/lifecycle/SchedulerInitializer.kt
Current implementation:
discoverCandidateMethods(...) returns Map<Service, KFunction<*>>
- discovery loop does
candidates[service] = function
This overwrites previous methods for the same service.
Reproduction
- Create a service with 2+ methods that all:
- return
SchedulerJobHandle
- have no required params
- call
scheduleCron / schedule / scheduleFixedDelay
- Start application with
enableScheduler().
- Check scheduler initializer logs and runtime execution.
Expected
All valid scheduler methods across all services are discovered, validated, invoked, and registered.
Actual
Only one scheduler method per service is kept and registered.
Why this is risky
- Silent partial initialization of critical background jobs
- Operational drift (queues/backfills never processed)
- Misleading startup signal (
SCHEDULER PASSED) despite missing tasks
Proposed fix
1) Change candidate/validated collections to support multiple methods per service
Use one of:
List<Pair<Service, KFunction<*>>> (recommended for deterministic processing)
Map<Service, MutableList<KFunction<*>>>
2) Update all pipeline steps
discoverCandidateMethods
validateCandidatesByBytecode
- invocation loop
to iterate all (service, method) pairs, not one method per service.
3) Improve diagnostics
At startup, log:
- total candidate count
- candidates grouped by service
- final registered task names
- skipped/failed methods explicitly
4) Add regression tests
Add tests that assert:
- Single service with multiple valid scheduler methods => all are registered.
- Multiple services each with multiple methods => all are registered.
- Mixed valid/invalid methods => only valid ones are registered, none overwritten.
- Invocation order is deterministic (if required by design).
Compatibility / migration
No API-breaking changes required if change is internal to SchedulerInitializer.
Suggested acceptance criteria
- A service with N valid scheduler methods results in N registrations.
- Scheduler summary and logs reflect full method set.
- New tests fail on current implementation and pass after fix.
Bug: SchedulerInitializer only registers one scheduler method per service
Summary
SchedulerInitializerdiscovers scheduler methods into aMap<Service, KFunction<*>>. When a service class exposes multiple valid scheduler methods, each discovered method overwrites the previous one for the same service key.Result: only one scheduler method per service is validated and invoked.
Impact
This silently drops valid scheduled jobs when a service defines more than one
SchedulerJobHandleregistration method.In a real backend integration, this caused:
scheduleStoryContinuityJobProcessor()to be registered and executedscheduleEmbeddingReindexProcessor()(and other schedule methods in the same service) to never registerThe queue stayed pending indefinitely (
attempt_count=0, no execution logs), while other schedulers ran normally.Affected code
katalyst-schedulermoduleFile:
io/github/darkryh/katalyst/scheduler/lifecycle/SchedulerInitializer.ktCurrent implementation:
discoverCandidateMethods(...)returnsMap<Service, KFunction<*>>candidates[service] = functionThis overwrites previous methods for the same service.
Reproduction
SchedulerJobHandlescheduleCron/schedule/scheduleFixedDelayenableScheduler().Expected
All valid scheduler methods across all services are discovered, validated, invoked, and registered.
Actual
Only one scheduler method per service is kept and registered.
Why this is risky
SCHEDULER PASSED) despite missing tasksProposed fix
1) Change candidate/validated collections to support multiple methods per service
Use one of:
List<Pair<Service, KFunction<*>>>(recommended for deterministic processing)Map<Service, MutableList<KFunction<*>>>2) Update all pipeline steps
discoverCandidateMethodsvalidateCandidatesByBytecodeto iterate all (service, method) pairs, not one method per service.
3) Improve diagnostics
At startup, log:
4) Add regression tests
Add tests that assert:
Compatibility / migration
No API-breaking changes required if change is internal to
SchedulerInitializer.Suggested acceptance criteria