Member Junction
    Preparing search index...

    Engine for managing scheduled job execution

    This engine uses composition to delegate metadata operations to SchedulingEngineBase while adding execution capabilities:

    • Evaluates cron expressions to determine which jobs are due
    • Instantiates the appropriate plugin for each job type
    • Executes jobs and tracks results in ScheduledJobRun
    • Sends notifications based on job configuration
    • Updates job statistics (RunCount, SuccessCount, FailureCount)
    • Manages distributed locking for multi-server environments

    ONLY USE ON SERVER-SIDE. For metadata only, use SchedulingEngineBase which can be used anywhere.

    const engine = SchedulingEngine.Instance;
    await engine.Config(false, contextUser);
    const runs = await engine.ExecuteScheduledJobs(contextUser);
    console.log(`Executed ${runs.length} scheduled jobs`);

    Hierarchy (View Summary)

    Index

    Constructors

    Accessors

    • get GlobalKey(): string

      Returns string

    • get LeaseTimeoutMinutes(): number

      Convenience accessor — lease duration as integer minutes. Production code may use either this or LeaseTimeoutMs. The setter validates positive integer minutes (no fractional minutes via this path; use LeaseTimeoutMs for sub-minute precision, including tests).

      Returns number

    • set LeaseTimeoutMinutes(value: number): void

      Parameters

      • value: number

      Returns void

    Methods

    • Configures the engine by loading scheduling metadata from the database. Delegates to SchedulingEngineBase.

      Parameters

      • OptionalforceRefresh: boolean
      • OptionalcontextUser: UserInfo
      • Optionalprovider: IMetadataProvider
      • includeRuns: boolean = false
      • includeAllJobs: boolean = false

      Returns Promise<boolean>

    • Dispatch all currently-due scheduled jobs WITHOUT awaiting their completion.

      This is the polling-path entry point introduced in v5.39 as part of the scheduler decoupling fix (GH #2736). The poll loop calls this and re-arms its timer based on the synchronous-portion return; jobs run in the background.

      Two phases:

      PHASE 1 — Stale-inflight sweep (decoupled from isJobDue AND from the cap): Walks inflightJobPromises looking for jobs whose DB lease has expired. Untracks each, frees its cap slot, and fire-and-forget marks any orphaned Status='Running' run records as abandoned. Runs first so it can free slots BEFORE the cap check throttles dispatch.

      PHASE 2 — Cap-bounded dispatch loop: For each due job, atomically acquire its lock via spAcquireScheduledJobLock. Only jobs whose lock was acquired count against MaxConcurrentJobs. Lock-failed jobs are reported via lockedOut counter. If at-cap, remaining due jobs counted via skippedAtCapacity and picked up by subsequent polls as slots free (no in-memory queueing).

      Same-instance double-dispatch is structurally prevented by the atomic lock sproc — its WHERE clause filters held-and-not-stale locks, so any second attempt against the same job ID returns Acquired=0.

      In-flight dispatched promises are tracked in inflightJobPromises so StopPolling({ waitForInflight: true }) can perform graceful shutdown.

      Parameters

      • contextUser: UserInfo
      • evalTime: Date = ...

      Returns Promise<
          {
              dispatched: number;
              lockedOut: number;
              skippedAtCapacity: number;
              swept: number;
          },
      >

      Counters for observability.

    • Execute all scheduled jobs that are currently due

      Evaluates each active job's cron expression against the current time. Jobs that are due are executed via their plugin's Execute method.

      Parameters

      • contextUser: UserInfo

        User context for execution

      • evalTime: Date = ...

        Optional time to evaluate against (defaults to now)

      Returns Promise<MJScheduledJobRunEntity[]>

      Array of scheduled job run records

    • The Global Object Store is a place to store global objects that need to be shared across the application. Depending on the execution environment, this could be the window object in a browser, or the global object in a node environment, or something else in other contexts. The key here is that in some cases static variables are not truly shared because it is possible that a given class might have copies of its code in multiple paths in a deployed application. This approach ensures that no matter how many code copies might exist, there is only one instance of the object in question by using the Global Object Store.

      Returns GlobalObjectStore

    • Handle job changes (create, update, delete) Reloads job metadata and restarts polling if needed

      This method is called automatically by MJScheduledJobEntityExtended.Save() and Delete()

      Parameters

      • contextUser: UserInfo

        User context for reloading metadata

      Returns Promise<void>

    • Start continuous polling for scheduled jobs.

      Async (changed in v5.39) because upfront work — Config, initial-NextRunAt seeding, stale-lock cleanup, permission probe — runs ONCE before the first poll fires. Subsequent polls assume that work is complete.

      The poll callback re-arms its timer FIRST, before any awaited work, so that any hang downstream (Config, DispatchScheduledJobs, etc.) cannot prevent the next poll from firing on schedule. This is the load-bearing invariant of the decoupling fix (see plans/scheduled-job-engine-decoupling.md).

      Parameters

      • contextUser: UserInfo

        User context for execution

      Returns Promise<void>

    • Stop continuous polling.

      Async (changed in v5.39). With opts.waitForInflight=true, awaits all currently-dispatched jobs to settle before returning. With opts.maxWaitMs, bounds that wait so a zombie can't make shutdown hang indefinitely.

      Order matters: sets acceptingDispatches=false FIRST so no new entries can be added to inflightJobPromises during the snapshot for allSettled.

      Parameters

      • Optionalopts: { maxWaitMs?: number; waitForInflight?: boolean }
        • OptionalmaxWaitMs?: number

          Bound the wait (only meaningful with waitForInflight)

        • OptionalwaitForInflight?: boolean

          Await dispatched jobs before returning

      Returns Promise<void>

    • Returns the singleton instance of the class. If the instance does not exist, it is created and stored in the Global Object Store. If className is provided it will be used as part of the key in the Global Object Store, otherwise the actual class name will be used. NOTE: the class name used by default is the lowest level of the object hierarchy, so if you have a class that extends another class, the lowest level class name will be used.

      Type Parameters

      Parameters

      • this: new () => T
      • OptionalclassName: string

      Returns T