mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-22 05:19:54 +08:00
fix(webhooks): run inactive deployment-version cleanup inline on deploy (#5250)
When a deploy activates a new version, superseded versions' webhooks are removed by a separate, best-effort CLEANUP_INACTIVE outbox event. When that event is lost/dead-letters, old-version webhooks linger as is_active orphans that fetchActiveWebhooks skips (version mismatch), so they silently stop polling (~515 webhooks across ~130 workflows in prod). Run the existing cleanupInactiveDeploymentVersions synchronously in the SYNC_ACTIVE handler, right after the active version's webhooks/schedules are registered, falling back to the deferred outbox event only if the inline pass throws. This reuses the existing guarded cleanup, which re-checks each version is still inactive before tearing anything down (so it never touches the active version) and runs strictly after registration (so a teardown failure can't block it).
This commit is contained in:
@@ -207,9 +207,10 @@ const syncActiveSideEffects = async (rawPayload: unknown): Promise<void> => {
|
||||
return
|
||||
}
|
||||
|
||||
await enqueueWorkflowInactiveDeploymentCleanup(db, {
|
||||
await syncInactiveDeploymentCleanup({
|
||||
workflowId: payload.workflowId,
|
||||
activeDeploymentVersionId: payload.deploymentVersionId,
|
||||
workflow: workflowData,
|
||||
userId: payload.userId,
|
||||
requestId,
|
||||
})
|
||||
@@ -278,6 +279,40 @@ const cleanupUndeployedSideEffects = async (rawPayload: unknown): Promise<void>
|
||||
await removeMcpToolsIfStillUndeployed(payload.workflowId, requestId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Run inactive-version cleanup synchronously as part of the active-version sync, right
|
||||
* after the active version's webhooks/schedules are registered.
|
||||
*
|
||||
* {@link cleanupInactiveDeploymentVersions} re-checks that each version is still inactive
|
||||
* before tearing anything down, so it can never touch the now-active version. Running it
|
||||
* inline — rather than only enqueueing it — closes the window where a lost
|
||||
* `CLEANUP_INACTIVE` outbox event leaves superseded webhooks behind as live-but-never-polled
|
||||
* `is_active` orphans. The deferred event is kept as a fallback so cleanup still retries if
|
||||
* the inline pass throws, without failing the already-succeeded registration.
|
||||
*/
|
||||
async function syncInactiveDeploymentCleanup(params: {
|
||||
workflowId: string
|
||||
activeDeploymentVersionId: string
|
||||
workflow: Record<string, unknown>
|
||||
userId: string
|
||||
requestId: string
|
||||
}): Promise<void> {
|
||||
try {
|
||||
await cleanupInactiveDeploymentVersions(params)
|
||||
} catch (cleanupError) {
|
||||
logger.warn(
|
||||
`[${params.requestId}] Inline inactive-deployment cleanup failed; deferring to outbox retry`,
|
||||
cleanupError
|
||||
)
|
||||
await enqueueWorkflowInactiveDeploymentCleanup(db, {
|
||||
workflowId: params.workflowId,
|
||||
activeDeploymentVersionId: params.activeDeploymentVersionId,
|
||||
userId: params.userId,
|
||||
requestId: params.requestId,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanupInactiveDeploymentVersions(params: {
|
||||
workflowId: string
|
||||
activeDeploymentVersionId: string
|
||||
|
||||
Reference in New Issue
Block a user