From d896d32296eade30e122991843f2bd6e8df55985 Mon Sep 17 00:00:00 2001 From: Janis Joderi Shoferi Date: Thu, 25 Jun 2026 13:33:18 +0200 Subject: [PATCH 1/3] When a user deploys a process or removes a deployment the subsequent refresh might return previously cached data so the UI does not reflect the change --- .../executions/deployments-view.tsx | 1 + .../lib/data/deployment.ts | 50 +---------- .../lib/deployment-schema.ts | 16 ---- .../executions/deployment-server-actions.ts | 89 ++++++++++++++----- .../mcp-tools/startProcess.ts | 1 + 5 files changed, 72 insertions(+), 85 deletions(-) delete mode 100644 src/management-system-v2/lib/deployment-schema.ts diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/deployments-view.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/deployments-view.tsx index 8f6f35ea5..fe59fdc79 100644 --- a/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/deployments-view.tsx +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/deployments-view.tsx @@ -87,6 +87,7 @@ const DeploymentsView = ({ space.spaceId, 'dynamic', forceEngine, + true, ); queryClient.removeQueries({ queryKey: ['processDeployments', space.spaceId, process.id], diff --git a/src/management-system-v2/lib/data/deployment.ts b/src/management-system-v2/lib/data/deployment.ts index d9ba04a73..88e56e117 100644 --- a/src/management-system-v2/lib/data/deployment.ts +++ b/src/management-system-v2/lib/data/deployment.ts @@ -1,11 +1,10 @@ 'use server'; import db from '@/lib/data/db'; -import { DeploymentInput, DeploymentInputSchema } from '../deployment-schema'; import { getCurrentEnvironment } from '@/components/auth'; import { SuccessType, UserErrorType, userError } from '../user-error'; import Ability from '../ability/abilityHelper'; -import { cacheLife, cacheTag, revalidateTag } from 'next/cache'; +import { cacheLife, cacheTag } from 'next/cache'; export async function getDeployedProcesses(environmentId: string) { const { ability } = await getCurrentEnvironment(environmentId); @@ -112,50 +111,3 @@ export async function getProcessDeployments( export type StoredDeployment = SuccessType< Awaited> >[number]; - -export async function addDeployment( - spaceId: string, - processId: string, - input: DeploymentInput, - ability?: Ability, -) { - if (!ability) ({ ability } = await getCurrentEnvironment(spaceId)); - - if (!ability.can('create', 'Execution')) - return userError('Invalid Permissions', UserErrorType.PermissionError); - - const data = DeploymentInputSchema.parse(input); - - await db.processDeployment.createMany({ - data: data.engineIds.map((engineId) => ({ ...data, engineIds: undefined, engineId })), - }); - - revalidateTag(`space/${spaceId}/deployments`, 'max'); - revalidateTag(`deployments/process/${processId}`, 'max'); -} - -export async function updateDeployment( - spaceId: string, - processId: string, - deploymentId: string, - input: Partial, - ability?: Ability, -) { - if (!ability) ({ ability } = await getCurrentEnvironment(spaceId)); - - if (!ability.can('update', 'Execution')) { - return userError('Invalid Permissions', UserErrorType.PermissionError); - } - - const data = DeploymentInputSchema.partial().strict().parse(input); - - const result = await db.processDeployment.update({ - where: { id: deploymentId }, - data, - }); - - revalidateTag(`space/${spaceId}/deployments`, 'max'); - revalidateTag(`deployments/process/${processId}`, 'max'); - - return result; -} diff --git a/src/management-system-v2/lib/deployment-schema.ts b/src/management-system-v2/lib/deployment-schema.ts deleted file mode 100644 index 156a4f99d..000000000 --- a/src/management-system-v2/lib/deployment-schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { z } from 'zod'; - -export const DeploymentInputSchema = z.object({ - versionId: z.string(), - deployerId: z.string(), - deployTime: z.date(), - removeTime: z.date().nullable().default(null), - toRemove: z.boolean().default(false), - active: z.boolean().default(false), - engineIds: z.string().array(), -}); - -export type DeploymentInput = z.input; -export type Deployment = z.output & { - id: string; -}; diff --git a/src/management-system-v2/lib/executions/deployment-server-actions.ts b/src/management-system-v2/lib/executions/deployment-server-actions.ts index b48237ab5..877046882 100644 --- a/src/management-system-v2/lib/executions/deployment-server-actions.ts +++ b/src/management-system-v2/lib/executions/deployment-server-actions.ts @@ -31,14 +31,14 @@ import { InstanceInfo, } from '../engines/deployment'; import { getCurrentEnvironment, getCurrentUser } from '@/components/auth'; -import { addDeployment, getProcessDeployments, updateDeployment } from '../data/deployment'; +import { getProcessDeployments } from '../data/deployment'; import { getMSConfig } from '../ms-config/ms-config'; import { updateTaskInfo } from '../tasks/server-actions'; import { engineRequest } from '../engines/endpoints'; import { getSharedRefetch } from '../shared-refetch'; import Ability from '../ability/abilityHelper'; import { EngineConnection } from '@prisma/client'; -import { revalidateTag } from 'next/cache'; +import { revalidateTag, updateTag } from 'next/cache'; import { isActive } from '@/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/instance-helpers'; export async function deployProcess( @@ -47,6 +47,9 @@ export async function deployProcess( spaceId: string, method: 'static' | 'dynamic' = 'dynamic', _forceEngine?: SpaceEngine | 'PROCEED', + // defines if the request was made in the context of a browser session + // this allows us to use "updateTag" to instantly invalidate the cached deployment data + isBrowserRequest = false, ability?: Ability, userId?: string, ) { @@ -112,25 +115,41 @@ export async function deployProcess( ability, ); - await addDeployment( - spaceId, - definitionId, - { + await db.processDeployment.createMany({ + data: deployedTo.map((engine) => ({ versionId, - deployerId: userId, + deployerId: userId!, deployTime: new Date(), - engineIds: deployedTo.map((engine) => engine.id), + engineId: engine.id, active: true, - }, - ability, - ); + })), + }); + + if (isBrowserRequest) { + updateTag(`space/${spaceId}/deployments`); + updateTag(`deployments/process/${definitionId}`); + } else { + revalidateTag(`space/${spaceId}/deployments`, 'max'); + revalidateTag(`deployments/process/${definitionId}`, 'max'); + } // deactivate the process on all engines that have a deployment but which were not target of the // new deployment await Promise.allSettled( alreadyDeployed.map(async (deployment) => { if (deployment.active && !deployedTo.some((e) => e.id === deployment.engineId)) { - await updateDeployment(spaceId, definitionId, deployment.id, { active: false }, ability); + await db.processDeployment.update({ + where: { id: deployment.id }, + data: { active: false }, + }); + + if (isBrowserRequest) { + updateTag(`space/${spaceId}/deployments`); + updateTag(`deployments/process/${definitionId}`); + } else { + revalidateTag(`space/${spaceId}/deployments`, 'max'); + revalidateTag(`deployments/process/${definitionId}`, 'max'); + } const engine = allEngines.find((e) => e.id === deployment.engineId); if (engine) { @@ -184,19 +203,32 @@ export async function removeDeployment(definitionId: string, spaceId: string, fo // engine => we don't need to call this for every version but only once per engine per // process await removeDeploymentFromMachines([engine], deployment.processId); - await updateDeployment(spaceId, definitionId, deployment.id, { - removeTime: new Date(), - active: false, + await db.processDeployment.update({ + where: { id: deployment.id }, + data: { + removeTime: new Date(), + active: false, + }, }); + + updateTag(`space/${spaceId}/deployments`); + updateTag(`deployments/process/${definitionId}`); + return; } } catch (err) {} // if removing the deployment is currently not possible mark it for automatic removal - await updateDeployment(spaceId, definitionId, deployment.id, { - toRemove: true, - active: false, + await db.processDeployment.update({ + where: { id: deployment.id }, + data: { + toRemove: true, + active: false, + }, }); + + updateTag(`space/${spaceId}/deployments`); + updateTag(`deployments/process/${definitionId}`); }); } catch (e) { const message = getErrorMessage(e); @@ -241,7 +273,16 @@ export async function changeDeploymentActivation( try { // activate the process on a single machine so we do not spawn new instances on multiple machines at once await _changeDeploymentActivation(engine, definitionId, version, true); - await updateDeployment(spaceId, definitionId, deployment.id, { active: true }); + await db.processDeployment.update({ + where: { id: deployment.id }, + data: { + active: false, + }, + }); + + updateTag(`space/${spaceId}/deployments`); + updateTag(`deployments/process/${definitionId}`); + activated = true; break; } catch (err) {} @@ -257,7 +298,15 @@ export async function changeDeploymentActivation( const res = await asyncMap(deploymentsToChange, async (d) => { // mark the deployment as inactive locally - await updateDeployment(spaceId, definitionId, d.id, { active: false }); + await db.processDeployment.update({ + where: { id: d.id }, + data: { + active: false, + }, + }); + + updateTag(`space/${spaceId}/deployments`); + updateTag(`deployments/process/${definitionId}`); try { // if deactivating the deployment fails the refetch loop should deactivate it due to the diff --git a/src/management-system-v2/mcp-tools/startProcess.ts b/src/management-system-v2/mcp-tools/startProcess.ts index 072599c3d..1a7b00f57 100644 --- a/src/management-system-v2/mcp-tools/startProcess.ts +++ b/src/management-system-v2/mcp-tools/startProcess.ts @@ -76,6 +76,7 @@ export default async function startProcess({ environmentId, 'dynamic', undefined, + false, ability, userId, ); From d0360df2beb665925965e0d38886a652b08c10f9 Mon Sep 17 00:00:00 2001 From: Janis Joderi Shoferi Date: Thu, 25 Jun 2026 14:42:31 +0200 Subject: [PATCH 2/3] When a user start an instance the instance selection in the instance view might show its id because the newly fetched data is still from the cache --- .../[processId]/process-deployment-view.tsx | 17 +++++++---- src/management-system-v2/lib/data/instance.ts | 29 ------------------- .../lib/executions/instance-server-actions.ts | 29 ++++++++++++++----- .../mcp-tools/startProcess.ts | 1 + 4 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx index 416e762e1..c0e29485f 100644 --- a/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx @@ -274,13 +274,18 @@ export default function ProcessDeploymentView({ processId }: { processId: string