MCPcopy Index your code
hub / github.com/simstudioai/sim / rollbackFork

Function rollbackFork

apps/sim/lib/workspaces/fork/promote/rollback.ts:63–292  ·  view source on GitHub ↗
(params: RollbackForkParams)

Source from the content-addressed store, hash-verified

61 * deployed version is the source of truth.
62 */
63export async function rollbackFork(params: RollbackForkParams): Promise<RollbackForkResult> {
64 const { targetWorkspaceId, otherWorkspaceId, userId } = params
65 const requestId = params.requestId ?? 'unknown'
66
67 const edge = await resolveForkEdge(targetWorkspaceId, otherWorkspaceId)
68 if (!edge) {
69 throw new ForkError('These workspaces are not a direct fork edge', 400)
70 }
71
72 // Only the most recent sync into the target is undoable. Undoing an older sibling's
73 // sync while a newer one stands would partially revert the target and strand the
74 // newer sync's changes.
75 const run = await getLatestPromoteRunForTarget(db, targetWorkspaceId)
76 if (!run) {
77 throw new ForkError('There is no promote to undo for this workspace', 404)
78 }
79 if (run.childWorkspaceId !== edge.childWorkspaceId) {
80 throw new ForkError(
81 'A newer sync into this workspace exists; reopen and undo the most recent sync.',
82 409
83 )
84 }
85
86 const { updated, created, archived } = run.snapshot
87
88 // Build the restore ops: reactivate a prior version, or undeploy (created targets +
89 // updated targets that had no prior deployment). Sort by workflow id so the locked
90 // transaction acquires workflow row locks in a deterministic order, avoiding
91 // deadlocks with the (unlocked) promote deploy loop, which locks the same rows.
92 const undeployIds = [
93 ...created,
94 ...updated.filter((i) => i.priorVersion == null).map((i) => i.workflowId),
95 ]
96 const toReactivateOps = (
97 list: Array<{ workflowId: string; priorVersion: number | null }>
98 ): RollbackOp[] =>
99 list
100 .filter((item) => item.priorVersion != null)
101 .map((item) => ({
102 workflowId: item.workflowId,
103 kind: 'reactivate' as const,
104 version: item.priorVersion as number,
105 }))
106 const ops: RollbackOp[] = [
107 ...toReactivateOps(updated),
108 ...toReactivateOps(archived),
109 ...undeployIds.map((workflowId) => ({ workflowId, kind: 'undeploy' as const })),
110 ].sort((a, b) => a.workflowId.localeCompare(b.workflowId))
111
112 const skipped = new Set<string>()
113 const outboxEventIds: string[] = []
114
115 await db.transaction(async (tx) => {
116 await setForkLockTimeout(tx)
117 await acquireForkTargetLock(tx, targetWorkspaceId)
118 await acquireForkEdgeLock(tx, edge.childWorkspaceId)
119
120 // Re-confirm our run is still the newest sync, now under the lock. If a promote

Callers 2

rollback.test.tsFile · 0.90
route.tsFile · 0.90

Calls 15

resolveForkEdgeFunction · 0.90
setForkLockTimeoutFunction · 0.90
acquireForkTargetLockFunction · 0.90
acquireForkEdgeLockFunction · 0.90
undeployWorkflowFunction · 0.90

Tested by

no test coverage detected