Repository navigation
feat(catalog): add a schedule manager to the catalog worker - #3878
Open
rossnelson wants to merge 3 commits into
Open
rossnelson wants to merge 3 commits into
rossnelson wants to merge 3 commits into
Conversation
Workflow examples can declare a schedule in code. Generation validates the declaration, rejects duplicate schedule ids, and emits it into the browser descriptor. The hello example declares an hourly schedule. A new shared schedule-sync example holds the reconciler. Its workflow lists schedules in the target namespace, keeps only those carrying the uiCatalog ownership memo, and plans create, update, and delete. Foreign schedules are never touched; one squatting on a declared id is reported as blocked. Orphaned catalog-owned schedules are deleted. The worker startup sequence bootstraps an hourly ui-catalog-schedule-sync manager schedule after Nexus endpoint provisioning, rewrites its arguments to the current declared list, and triggers one sync. This works over plaintext, API key, and TLS connections. The manager is disabled by default. CATALOG_SCHEDULES=enabled in .env.catalog.local turns it on; any other value fails startup naming the variable. The banner prints the mode. The example page shows an advisory schedule readiness check that never blocks Run, with the enable setting as copyable text when the schedule is missing and a link to the schedule page when present. The list shows a Scheduled badge.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The reconciler rewrote state from the declaration on every update, so a catalog-owned schedule paused in the UI was resumed by the next sync -- at most an hour later, and immediately on the next worker restart under watch mode. Stopping a misbehaving declared schedule meant editing code and redeploying the worker, which is the wrong tool during an incident. A running state that disagrees with the declaration is now treated as a decision somebody made. The plan gains a `held` bucket, and a held schedule is skipped entirely: spec, action and state are all left alone, because rewriting the cron of a schedule someone paused mid-incident is the same surprise in a different form. Drift in either direction counts, so a declared-paused schedule somebody resumed is held too. Hold beats delete. An owned schedule that no example declares any more is reported rather than deleted when it is paused; otherwise pausing something during an incident and then dropping its example would remove it silently. A foreign schedule is still blocked whatever its state -- its running state is none of the manager's business. Pausing `ui-catalog-schedule-sync` itself now stops reconciliation outright. The startup bootstrap reads the manager's paused state and skips both the argument rewrite and the trigger, so the pause survives worker restarts instead of being undone by the next one. Held ids travel in the workflow result beside created, updated, deleted and blocked, so a hold is visible in workflow history without reading worker logs. The example page's readiness check reports the schedule as held, says which direction it drifted, and still links to it. A held schedule can drift arbitrarily far while nothing is rewriting it. Resuming it hands it back to the reconciler, which rewrites all of that on the next pass -- documented in the README, the skill, and the example setup notes, so nobody treats a hold as a place to keep hand edits.
The bootstrap received the CATALOG_TARGET_ID-filtered bindings, so the desired list it built covered only the targets this process happened to run. The reconciler deletes any catalog-owned schedule that is not on that list, so narrowing the run did not reconcile a subset -- it deleted what the other targets declare. Reachable today: CATALOG_TARGET_ID=shared-workflows with the manager enabled finds the schedule-sync example, rewrites the manager's args to the shared-only list and triggers it, removing schedules a scaffolded local example declared. The opposite filter escaped only by accident, because the manager's example was then absent and the bootstrap skipped. dev.ts now passes the unfiltered bindings, and the bootstrap takes the registry's target ids alongside them so it can prove the set is complete and refuse rather than act when it is not. That check reads the registry rather than the bindings, which is what lets it see the difference -- and it is what a caller assembling bindings elsewhere, such as a deploy step with one function per task queue, would otherwise get wrong silently. Running targets are now passed separately from declared ones. A run narrowed to a target that does not register schedule-sync reports a skip instead of triggering a sync that would sit as backlog nothing polls. Two extractions come with it, so the next caller does not copy this: - catalogScheduleManagerOperations builds the four SDK callbacks the bootstrap needs from an open connection, so every caller writes the same memo, the same spec, and the same not-found handling. dev.ts loses about ninety lines of plumbing. - toScheduleSpec moves out of the reconciler's activities into schedule-spec.ts. It is pure conversion, and living beside Client and Connection meant anything describing a schedule pulled a client in with it. Both of its imports are type-only, so it now has no runtime dependency at all.
This branch was successfully deployed
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.
What
Adds a schedule manager to the Temporal Catalog so examples can declare scheduled jobs and the catalog worker provisions them at startup.
scheduleblock: id, cron expressions or intervals, optional input, a requiredpausedflag, and a note. Generation validates every field and rejects duplicate schedule ids. Thehelloexample declares an hourly schedule.schedule-syncexample holds the reconciler. Its workflow lists schedules in the target namespace, keeps only those carrying theuiCatalogownership memo, and plans create, update, and delete. Foreign schedules are never touched. One squatting on a declared id is reported as blocked. Orphaned catalog-owned schedules are deleted.ui-catalog-schedule-syncmanager schedule exists, rewrites its args to the current declared list, and triggers one sync. Works over plaintext, API key, and TLS..env.catalog.localsetsCATALOG_SCHEDULES=enabled. Any other value fails startup naming the variable. The banner prints the mode.Why
The catalog worker needs a way to exercise Schedules end to end, and a place to add scheduled jobs to its startup sequence. The design mirrors the existing Nexus endpoint provisioning step and keeps the catalog's rules: declared in code, validated at generation, task queues from registration only.
Verification
pnpm check,pnpm lint,pnpm catalog verify: clean.src/lib/catalog/scenarios.md.Notes for reviewers
pnpm catalog schedules clearcommand is a natural follow-up.