feat(triggers): cron & one-shot schedules, multi-workflow triggers, and telemetry - #318
Merged
07prajwal2000 merged 3 commits intoSep 9, 2026
Merged
Conversation
Closes Fluxify-rest#304. A trigger can now be fired by the clock: a six-field cron expression, a fixed interval, or a single instant. The broker keeps the time. A schedule is a message carrying ADR-51's `Nats-Schedule` headers, which forces three things: - It needs its own stream. `FLUXIFY_TRIGGERS` and `FLUXIFY_JOBS` are both work-queue, and on those an ack removes the message — so the first consumer to read a schedule would delete it. `FLUXIFY_SCHEDULES` is `limits`. - The fire subject must be captured by that same stream, so the hop from a fire to the job queue is structural rather than a design preference. - One subject holds one schedule, so each trigger gets its own and an update is a republish. Postgres stays authoritative. Every write republishes, and `loadSchedules` reconciles both directions at boot: schedules NATS never got (a wiped volume, a restored backup) and schedules whose trigger is gone. Both failures are silent otherwise — a trigger that never runs, or one that runs forever. Jitter and TTL live in the reconciler, not the UI. Every `@daily` schedule is `0 0 0 * * *`, so without jitter they all fire on the same second; the seconds field is rewritten to `hash(triggerId) % 60` unless the user named one. Fires carry a TTL of half the interval (capped at 5m) so a server restarted after a weekend does not replay every overdue fire at once. The fire consumer runs on the workers rather than beside the reconciler: the worker fleet is the always-on tier, so a control-plane node going down delays schedule edits and not the schedules themselves. Its job id is `<triggerId>:<firedAt>`, so a redelivered fire — or one seen by both a project worker and a catch-all worker — enqueues exactly one job. Cron is validated at the API boundary, because a bad cron that fails silently at 3am is the worst failure mode this feature has. Five-field expressions are rejected rather than run 60x too often, and timezones are IANA names only — a fixed offset silently opts out of daylight saving. New schedules default to UTC, with a warning in the form when they do not. Found on a real 2.14 server, not in review: it rejects a timezone sent with `@every` or `@at` outright ("message schedules pattern is invalid"), so the header goes out for cron and predefined aliases only. Also pins the NATS image to 2.14 in all three compose files. On an older server a scheduled trigger is accepted and simply never fires. Not automated: clustered R3 behaviour, which ADR-51 does not specify. The catch-up storm is covered by an e2e against a real broker; standing up a three-node cluster in the test harness is its own piece of work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ti-target dispatch
…s UI table management
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.
Summary
Closes #304
This PR implements scheduled triggers (cron and one-shot schedules) backed by NATS JetStream message schedules, multi-target trigger dispatch to workflows, OpenTelemetry tracing/metrics for workflows, and UI management improvements across portal routes and workflows.
1. Cron & One-Shot Schedules (NATS JetStream)
FLUXIFY_TRIGGERSstream (allow_msg_schedules: true,allow_msg_ttl: true,retention: limits,discard: old) decoupled from work-queue streams.fluxify.trigger.sched.<projectId>.<triggerId>andfluxify.trigger.fire.<projectId>.<triggerId>.<triggerId>:<firedAt>).2. Multi-Workflow Trigger Relations & Wizard
trigger_workflowsjoin table (0061_trigger_workflows.sql) supporting M:N relations between triggers and workflows.FormWizard,TriggerWizard, and modal editors for creating/editing triggers with workflow selection inapps/portal.3. OpenTelemetry Tracing & Metrics for Workflows
@fluxify/common/otlpwithfluxify.workflow.executionsandfluxify.workflow.durationmetrics along with consumer span kinds for background workflow runs.workflowJob.tsandcompiledWorker.tswith dedicated unit test coverage.4. Portal UI Improvements
Verification
tsgo --noEmit)fta-cli --score-cap 70)