Skip to content

Commit 6783e44

Browse files
committed
fix(projects): keep archive retries from re-stamping MCP servers and isolate post-commit notifications
1 parent 4905ae9 commit 6783e44

2 files changed

Lines changed: 31 additions & 9 deletions

File tree

‎apps/sim/lib/projects/application/authorization.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,11 @@ async function loadProjectAccess(
125125
await tx.execute<{ id: string }>(sql`
126126
SELECT DISTINCT denied.id FROM ${permissionGroup},
127127
jsonb_array_elements_text(
128-
COALESCE(${permissionGroup.config}->'deniedPartialAccessProjectIssues', '[]'::jsonb)
128+
CASE
129+
WHEN jsonb_typeof(${permissionGroup.config}->'deniedPartialAccessProjectIssues') = 'array'
130+
THEN ${permissionGroup.config}->'deniedPartialAccessProjectIssues'
131+
ELSE '[]'::jsonb
132+
END
129133
) AS denied(id)
130134
WHERE ${inArray(permissionGroup.organizationId, organizationIds)}
131135
AND denied.id = ANY(${textArrayLiteral(records.map((record) => record.id))})

‎apps/sim/lib/workspaces/lifecycle.ts‎

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,18 +134,32 @@ export async function archiveEnvironmentInTransaction(
134134
return { workspaceId, workflows, serverIds }
135135
}
136136

137-
/** Announces a committed environment archive; every step is best-effort. */
137+
/**
138+
* Announces a committed environment archive. Every step is best-effort and isolated, so
139+
* one failed notification never skips the rest.
140+
*/
138141
export async function finishEnvironmentArchive(
139142
effects: EnvironmentArchiveEffects,
140143
requestId: string
141144
): Promise<void> {
145+
const { workspaceId } = effects
142146
await mapWithConcurrency(effects.workflows, ARCHIVE_NOTIFICATION_CONCURRENCY, (row) =>
143-
finishWorkflowArchive(row.id, effects.workspaceId, row.serverIds, { requestId })
147+
finishWorkflowArchive(row.id, workspaceId, row.serverIds, { requestId }).catch((error) =>
148+
logger.warn(`[${requestId}] Post-archive notification failed for workflow ${row.id}`, {
149+
error,
150+
})
151+
)
144152
)
145-
await mcpService.clearCache(effects.workspaceId).catch(() => undefined)
146-
if (mcpPubSub) {
147-
for (const serverId of effects.serverIds)
148-
mcpPubSub.publishWorkflowToolsChanged({ serverId, workspaceId: effects.workspaceId })
153+
await mcpService.clearCache(workspaceId).catch(() => undefined)
154+
if (!mcpPubSub) return
155+
for (const serverId of effects.serverIds) {
156+
try {
157+
mcpPubSub.publishWorkflowToolsChanged({ serverId, workspaceId })
158+
} catch (error) {
159+
logger.warn(`[${requestId}] MCP tools-changed publish failed for server ${serverId}`, {
160+
error,
161+
})
162+
}
149163
}
150164
}
151165

@@ -234,15 +248,19 @@ async function archiveWorkspaceRecordsInTransaction(
234248
.delete(apiKey)
235249
.where(and(eq(apiKey.workspaceId, workspaceId), eq(apiKey.type, 'workspace')))
236250

251+
/** Every server is announced, so a retry still invalidates; only live ones are stamped. */
237252
const servers = await tx
253+
.select({ id: workflowMcpServer.id })
254+
.from(workflowMcpServer)
255+
.where(eq(workflowMcpServer.workspaceId, workspaceId))
256+
await tx
238257
.update(workflowMcpServer)
239258
.set({
240259
deletedAt: now,
241260
isPublic: false,
242261
updatedAt: now,
243262
})
244-
.where(eq(workflowMcpServer.workspaceId, workspaceId))
245-
.returning({ id: workflowMcpServer.id })
263+
.where(and(eq(workflowMcpServer.workspaceId, workspaceId), isNull(workflowMcpServer.deletedAt)))
246264

247265
await tx
248266
.update(mcpServers)

0 commit comments

Comments
 (0)