mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
fix(deploy-check): race condition fixes (#2710)
This commit is contained in:
committed by
GitHub
parent
261becd129
commit
833825f04a
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { Button, Tooltip } from '@/components/emcn'
|
||||
import { DeployModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/deploy-modal'
|
||||
@@ -62,26 +62,13 @@ export function Deploy({ activeWorkflowId, userPermissions, className }: DeployP
|
||||
const canDeploy = userPermissions.canAdmin
|
||||
const isDisabled = isDeploying || !canDeploy || isEmpty
|
||||
|
||||
/**
|
||||
* Handle deploy button click
|
||||
*/
|
||||
const onDeployClick = useCallback(async () => {
|
||||
const onDeployClick = async () => {
|
||||
if (!canDeploy || !activeWorkflowId) return
|
||||
|
||||
const result = await handleDeployClick()
|
||||
if (result.shouldOpenModal) {
|
||||
setIsModalOpen(true)
|
||||
}
|
||||
}, [canDeploy, activeWorkflowId, handleDeployClick])
|
||||
|
||||
const refetchWithErrorHandling = async () => {
|
||||
if (!activeWorkflowId) return
|
||||
|
||||
try {
|
||||
await refetchDeployedState()
|
||||
} catch (error) {
|
||||
// Error already logged in hook
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +122,7 @@ export function Deploy({ activeWorkflowId, userPermissions, className }: DeployP
|
||||
needsRedeployment={changeDetected}
|
||||
deployedState={deployedState!}
|
||||
isLoadingDeployedState={isLoadingDeployedState}
|
||||
refetchDeployedState={refetchWithErrorHandling}
|
||||
refetchDeployedState={refetchDeployedState}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useMemo } from 'react'
|
||||
import { hasWorkflowChanged } from '@/lib/workflows/comparison'
|
||||
import { useDebounce } from '@/hooks/use-debounce'
|
||||
import { useVariablesStore } from '@/stores/panel/variables/store'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
@@ -48,16 +47,12 @@ export function useChangeDetection({
|
||||
const blockSubValues = subBlockValues?.[blockId] || {}
|
||||
const subBlocks: Record<string, any> = {}
|
||||
|
||||
for (const [subId, value] of Object.entries(blockSubValues)) {
|
||||
subBlocks[subId] = { value }
|
||||
}
|
||||
|
||||
if (block.subBlocks) {
|
||||
for (const [subId, subBlock] of Object.entries(block.subBlocks)) {
|
||||
if (!subBlocks[subId]) {
|
||||
subBlocks[subId] = subBlock
|
||||
} else {
|
||||
subBlocks[subId] = { ...subBlock, value: subBlocks[subId].value }
|
||||
const storedValue = blockSubValues[subId]
|
||||
subBlocks[subId] = {
|
||||
...subBlock,
|
||||
value: storedValue !== undefined ? storedValue : subBlock.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,14 +72,12 @@ export function useChangeDetection({
|
||||
} as WorkflowState & { variables: Record<string, any> }
|
||||
}, [workflowId, blocks, edges, loops, parallels, subBlockValues, workflowVariables])
|
||||
|
||||
const rawChangeDetected = useMemo(() => {
|
||||
const changeDetected = useMemo(() => {
|
||||
if (!currentState || !deployedState || isLoadingDeployedState) {
|
||||
return false
|
||||
}
|
||||
return hasWorkflowChanged(currentState, deployedState)
|
||||
}, [currentState, deployedState, isLoadingDeployedState])
|
||||
|
||||
const changeDetected = useDebounce(rawChangeDetected, 300)
|
||||
|
||||
return { changeDetected }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import type { WorkflowState } from '@/stores/workflows/workflow/types'
|
||||
@@ -27,29 +27,27 @@ export function useDeployedState({
|
||||
(state) => state.setWorkflowNeedsRedeployment
|
||||
)
|
||||
|
||||
/**
|
||||
* Fetches the deployed state of the workflow from the server
|
||||
* This is the single source of truth for deployed workflow state
|
||||
*/
|
||||
const fetchDeployedState = async () => {
|
||||
if (!workflowId || !isDeployed) {
|
||||
const fetchDeployedState = useCallback(async () => {
|
||||
const registry = useWorkflowRegistry.getState()
|
||||
const currentWorkflowId = registry.activeWorkflowId
|
||||
const deploymentStatus = currentWorkflowId
|
||||
? registry.getWorkflowDeploymentStatus(currentWorkflowId)
|
||||
: null
|
||||
const currentIsDeployed = deploymentStatus?.isDeployed ?? false
|
||||
|
||||
if (!currentWorkflowId || !currentIsDeployed) {
|
||||
setDeployedState(null)
|
||||
return
|
||||
}
|
||||
|
||||
// Store the workflow ID at the start of the request to prevent race conditions
|
||||
const requestWorkflowId = workflowId
|
||||
|
||||
// Helper to get current active workflow ID for race condition checks
|
||||
const getCurrentActiveWorkflowId = () => useWorkflowRegistry.getState().activeWorkflowId
|
||||
const requestWorkflowId = currentWorkflowId
|
||||
|
||||
try {
|
||||
setIsLoadingDeployedState(true)
|
||||
|
||||
const response = await fetch(`/api/workflows/${requestWorkflowId}/deployed`)
|
||||
|
||||
// Check if the workflow ID changed during the request (user navigated away)
|
||||
if (requestWorkflowId !== getCurrentActiveWorkflowId()) {
|
||||
if (requestWorkflowId !== useWorkflowRegistry.getState().activeWorkflowId) {
|
||||
logger.debug('Workflow changed during deployed state fetch, ignoring response')
|
||||
return
|
||||
}
|
||||
@@ -64,22 +62,22 @@ export function useDeployedState({
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
if (requestWorkflowId === getCurrentActiveWorkflowId()) {
|
||||
if (requestWorkflowId === useWorkflowRegistry.getState().activeWorkflowId) {
|
||||
setDeployedState(data.deployedState || null)
|
||||
} else {
|
||||
logger.debug('Workflow changed after deployed state response, ignoring result')
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error fetching deployed state:', { error })
|
||||
if (requestWorkflowId === getCurrentActiveWorkflowId()) {
|
||||
if (requestWorkflowId === useWorkflowRegistry.getState().activeWorkflowId) {
|
||||
setDeployedState(null)
|
||||
}
|
||||
} finally {
|
||||
if (requestWorkflowId === getCurrentActiveWorkflowId()) {
|
||||
if (requestWorkflowId === useWorkflowRegistry.getState().activeWorkflowId) {
|
||||
setIsLoadingDeployedState(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!workflowId) {
|
||||
|
||||
Reference in New Issue
Block a user