Symptom
In the default JobServicePlugin configuration (adapter: 'auto'/'db'), cron-scheduled jobs registered by plugins never fire. Interval jobs run fine. A/B measured on a real app: an interval 15s job ran 23 times while a cron */1 * * * * job ran 0 times, and none of the jobs appeared in sys_job.
Root cause (three stacked)
JobServicePlugin.init registers a placeholder IntervalJobAdapter so callers can getService('job') during init — but the placeholder silently drops cron schedules (no timer, no error; interval-job-adapter.ts "stored but not actively scheduled").
- The upgrade to the croner-backed
DbJobAdapter happens at kernel:ready, which runs after every business plugin's start() — so all business registrations land on the placeholder.
- The upgrade calls
replaceService('job', dbAdapter) without migrating the already-registered jobs: cron entries are lost for good, and the placeholder's interval timers keep running on the orphaned adapter (which is why interval jobs look healthy and nothing lands in sys_job).
Expected
Registrations made before the upgrade must follow the service: cron entries should run via the DbJobAdapter's cron routing and persist to sys_job. An adapter that cannot execute a schedule type must say so instead of silently discarding it.
Found via real-app dogfooding (os-tianshun-ehr platform defect registry PLAT-DEF-016, on 17.0.0-rc.0).
Symptom
In the default
JobServicePluginconfiguration (adapter: 'auto'/'db'), cron-scheduled jobs registered by plugins never fire. Interval jobs run fine. A/B measured on a real app: an interval 15s job ran 23 times while a cron*/1 * * * *job ran 0 times, and none of the jobs appeared insys_job.Root cause (three stacked)
JobServicePlugin.initregisters a placeholderIntervalJobAdapterso callers cangetService('job')during init — but the placeholder silently dropscronschedules (no timer, no error;interval-job-adapter.ts"stored but not actively scheduled").DbJobAdapterhappens atkernel:ready, which runs after every business plugin'sstart()— so all business registrations land on the placeholder.replaceService('job', dbAdapter)without migrating the already-registered jobs: cron entries are lost for good, and the placeholder's interval timers keep running on the orphaned adapter (which is why interval jobs look healthy and nothing lands insys_job).Expected
Registrations made before the upgrade must follow the service: cron entries should run via the DbJobAdapter's cron routing and persist to
sys_job. An adapter that cannot execute a schedule type must say so instead of silently discarding it.Found via real-app dogfooding (os-tianshun-ehr platform defect registry PLAT-DEF-016, on 17.0.0-rc.0).