ProtectedconstructorOptional observer notified of every step and run boundary. Set once at process startup (see the RSU progress bridge in MJServer) — a single observer is sufficient because RSU runs are serialized by the pipeline lock, so events can never interleave between runs.
Never awaited, never allowed to fail the pipeline. See RSUPipelineObserver.
Whether RSU is enabled based on ALLOW_RUNTIME_SCHEMA_UPDATE=1.
Whether the local API is out of sync with the git repo.
Whether a pipeline is currently running (concurrency mutex).
When the last pipeline run completed.
Result of the last pipeline run.
When the out-of-sync state was first detected.
StaticInstanceClear the out-of-sync flag (called when CI/CD deployment lands).
Mark a pending work row Completed. Call ONLY after the work actually succeeded.
Optionalprovider: IMetadataProviderOptionalprovider: IMetadataProviderReturns the complete set of schema names this instance will block in DDL.
Always includes __mj (the MemberJunction system schema, never modifiable).
Also includes any additional schemas configured via the RSU_PROTECTED_SCHEMAS
environment variable (comma-separated list, e.g. RSU_PROTECTED_SCHEMAS=dbo,sys).
Callers such as the Database Designer Validator use this for early-exit UX
feedback before DDL is even generated — the same set that
ValidateMigrationSQL() enforces at the SQL level.
A readonly lowercase set of all protected schema names.
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.
Current status snapshot.
Mark the API as running code that is out-of-sync with the git repo.
Dry-run: validate the SQL and return what would happen.
Use ReadPendingWork instead, and mark each item terminal with CompletePendingWork / FailPendingWork once you have processed it.
Retained with its original signature so consumers taking this minor upgrade still
compile. It is NO LONGER FUNCTIONAL: pending work lives in MJ: RSU Pending Works
rather than in .rsu_pending files, and reading it requires a context user this
signature has no way to supply. Returns an empty array — the value that previously
meant "no pending work" — rather than pretending to have drained a queue.
Read every still-Pending work row. Never deletes or mutates — the caller marks each row Completed/Failed once it has actually finished processing it. Rows whose payload fails to parse are reported as Failed so they stop being retried forever.
OptionalstaleAfterMinutes: numberwhen set, rows created more than this many minutes ago are logged as stale so an operator can see work that a previous process abandoned.
Optionalprovider: IMetadataProviderRe-queues failed work for one more attempt, narrowed to what has NOT yet been done.
RSU is a long chain — migrations, CodeGen, a git commit, a compile, a restart — and a failure partway through it is frequently transient: the process was restarted mid-consumption, or a provider call failed once. Marking such an item Failed terminally means the objects it would have mapped are silently never mapped, and the only recovery is for someone to notice and re-apply the connector by hand.
The remainder matters as much as the retry. Re-running an item whole would redo the objects that already succeeded; carrying only the outstanding ones makes each attempt strictly smaller and keeps a single poison object from blocking its siblings forever.
Returns false when the budget is spent, in which case the caller should fail the item terminally — the message it writes is the operator's only signal, so it should name what was left undone.
Optionalprovider: IMetadataProviderExecute the RSU pipeline for a single input. Convenience wrapper around RunPipelineBatch — a batch of 1.
Execute the RSU pipeline for a batch of inputs.
Correctness guarantee: migrations and CodeGen never overlap. All migrations execute under one lock, then CodeGen runs once against the fully-committed schema.
Flow:
Batch equivalent of RunPipelineWithRetry, for the ApplyAll / ApplyAllBatch
install path.
Same transient-step whitelist, same backoff, same safety rule: a retry re-runs every input's migration, so it is only attempted when NOTHING was applied on the failed pass. Once even one migration has committed, the batch is returned as-is rather than replayed.
Run the RSU pipeline with automatic retry for transient failures. Only retries steps that are known to be transient (SQL execution, CodeGen, compilation, restart). Validation and git steps are not retried.
Pipeline input
Maximum number of retries (default: 2, so 3 total attempts)
Base delay for exponential backoff (default: 5000ms)
Set the additionalSchemaInfo file path that CodeGen reads (from mj.config.cjs
additionalSchemaInfo). RSU writes its soft PK/FK config to THIS path so the
subsequent CodeGen run actually finds it. Injected by the server at startup,
mirroring SetCodeGenOutputPaths.
Without injection, RSU falls back to RSU_ADDITIONAL_SCHEMA_INFO_PATH / 'additionalSchemaInfo.json' — a path that need not match CodeGen's configured one. When they diverge, RSU writes soft PKs to a file CodeGen never reads, and every integration table is skipped with "No primary key found". Keeping the two in lockstep is the whole point of this setter.
Set the CodeGen output directory paths (from mj.config.cjs output[].directory). Used by the git step to stage only CodeGen-produced source files.
Set the in-process CodeGen runner. Injected by the server at startup so RSU can run CodeGen without shelling out.
Set a dedicated provider for DDL operations (CREATE TABLE, CREATE SCHEMA, etc.). Should be configured with elevated credentials (e.g. MJ_CodeGen with db_owner). If not set, falls back to the default Metadata.Provider.
Register pending work durably in MJ: RSU Pending Works so it survives the
post-migration restart. Returns the new row's ID.
Unlike the .rsu_pending files this replaces, the row is NOT consumed on read —
it stays Pending until the consumer explicitly calls CompletePendingWork
or FailPendingWork, so a crash mid-consumption leaves visible, resumable work.
OptionalcontextUser: UserInfoOptionalprovider: IMetadataProviderStaticBuildMaps a finished batch onto its terminal run.end event.
result is undefined when a throw escaped the pipeline before a result existed — that case
must still produce a FAILED run.end, otherwise an observer's run would hang in-flight forever.
Public + static so the mapping is unit-testable without a live pipeline.
OptionalErrorMessage?: stringPresent when the run failed — the first failing step's message.
OptionalErrorStep?: stringPresent when the run failed — the name of the step that failed.
Protected StaticgetReturns 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.
OptionalclassName: string
Singleton pipeline orchestrator for runtime schema changes.
Usage: const rsm = RuntimeSchemaManager.Instance; if (!rsm.IsEnabled) throw new Error('RSU is disabled'); const result = await rsm.RunPipeline(input);