Uh oh!
There was an error while loading. Please reload this page.
feat(service-job): leader-elect scheduled cron/interval jobs across the cluster - #2219
Merged
Merged
Conversation
…he cluster CronJobAdapter fired every scheduled tick on EVERY replica, so a cron job ran N times in an N-node cluster (duplicate side effects). Scheduled fires now acquire a per-job cluster lock (fail-fast) before running — only the node that wins runs the handler; peers skip. With no cluster wired or the in-memory driver (single process) the lock always succeeds, so single-node behaviour is unchanged. Manual trigger() bypasses the gate (explicit, node-local). JobServicePlugin injects the cluster service into the adapter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 5 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
Uh oh!
There was an error while loading. Please reload this page.
xuyushun441-sys added a commit
that referenced
this pull request
Jun 22, 2026
…rve` (#2226) `os serve` constructed the Runtime without a cluster config, so it always used the in-memory cluster driver — a multi-replica deployment could not coordinate (and the split-brain guard blocked it). `os serve` now reads OS_CLUSTER_DRIVER (+ OS_REDIS_URL), dynamically imports the matching remote driver package (so it works in both config-boot and compiled-artifact mode), and forwards the cluster config to the Runtime. Open-core ships only the in-memory driver; remote drivers (e.g. @objectstack/service-cluster-redis) are provided by the EE distribution — absent ⇒ graceful fallback to in-memory. Verified via A/B boot (local redis): with OS_CLUSTER_DRIVER=redis + replicas=2 the server boots (guard passes = redis active); without it, replicas=2 trips the split-brain guard (memory). Enables the EE multi-node activation (cloud ADR-0018) of the cache-invalidation (#2213) and scheduler-leader-election (#2219) seams. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
os-zhuang added a commit
that referenced
this pull request
Jul 4, 2026
…ase 4) (#2583) * feat(spec,lint): reject userFilters on object list views (ADR-0053 phase 4) ADR-0053 reserves userFilters/quickFilters for page lists ("filters" mode); on an object list view ("views" mode, where the ViewTabBar is the only nav control) they are silently dropped. Land the phase-4 guardrail as a layered defence so the wrong-context authoring mistake is caught without breaking existing metadata: - Type (author time): new ObjectListViewSchema = ListViewSchema minus userFilters. Object built-in listViews + defineView list/listViews use it, so userFilters on an object list view is a tsc error. Full ListViewSchema (page filters mode) untouched. - Runtime (back-compat): field STRIPPED at parse (default strip, no throw) — existing metadata keeps loading; ObjectSchema.parse never fails on a stray userFilters. - Author/CI (actionable): new @objectstack/lint validateListViewMode, wired into `os validate`, reports the wrong-context field PRE-parse (before the schema strips it) with a fix hint. Verified: spec 6673 tests + lint 122 tests green, turbo build (54 pkgs, tsc typecheck) green, e2e normalize+lint chain catches both array/map forms. Closes the schema half of objectui #2219; supersedes the interim runtime warn in objectui #2220. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(spec): approve ObjectListViewSchema in public API surface New export from the ADR-0053 phase-4 guardrail; the api-surface check flagged it as 1 added / 0 breaking. Regenerated the snapshot via `pnpm --filter @objectstack/spec gen:api-surface`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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 freeto 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.
Why
CronJobAdapterfired every scheduled tick on every replica → a cron job runs N times in an N-node cluster (duplicate invoices / emails / charges). This is the core multi-node correctness gap for scheduled work (cloud ADR-0018).What
runScheduled(name), which acquires a per-job cluster lock (job:<name>,waitMs:0fail-fast) before running; only the winner runs the handler, peers skip.trigger()bypasses the gate (explicit, node-local invocation).JobServicePlugininjects the cluster service (getService('cluster'), undefined → single-node).@objectstack/service-jobtests green (36).Scope / notes
Open-core mechanism; EE remote driver activates cross-node election. Best-effort per-fire leader election suited to timer-scheduled jobs; for strict exactly-once use the DB job queue (
sys_job_queue, transactional CAS). Tightens cluster-semantics.mdx §7.4 (the deferredleader-electedscheduler).🤖 Generated with Claude Code