mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 23:17:59 -05:00
fix(race-condition-workflow-switching): another race condition between registry and workflow stores (#1247)
* fix(race-condition-workflow-switching): another race condition between regitry and workflow stores" * fix initial load race cond + cleanup * fix initial load issue + simplify
This commit is contained in:
committed by
GitHub
parent
0f7dfe084a
commit
57c98d86ba
@@ -867,48 +867,41 @@ const WorkflowContent = React.memo(() => {
|
||||
[project, isPointInLoopNodeWrapper, getNodes]
|
||||
)
|
||||
|
||||
// Track when workflow is fully ready for rendering
|
||||
// Initialize workflow when it exists in registry and isn't active
|
||||
useEffect(() => {
|
||||
const currentId = params.workflowId as string
|
||||
if (!currentId || !workflows[currentId]) return
|
||||
|
||||
// Reset workflow ready state when workflow changes
|
||||
if (activeWorkflowId !== currentId) {
|
||||
setIsWorkflowReady(false)
|
||||
return
|
||||
// Clear diff and set as active
|
||||
const { clearDiff } = useWorkflowDiffStore.getState()
|
||||
clearDiff()
|
||||
setActiveWorkflow(currentId)
|
||||
}
|
||||
}, [params.workflowId, workflows, activeWorkflowId, setActiveWorkflow])
|
||||
|
||||
// Check if we have the necessary data to render the workflow
|
||||
const hasActiveWorkflow = activeWorkflowId === currentId
|
||||
const hasWorkflowInRegistry = Boolean(workflows[currentId])
|
||||
const isNotLoading = !isLoading
|
||||
// Track when workflow is ready for rendering
|
||||
useEffect(() => {
|
||||
const currentId = params.workflowId as string
|
||||
|
||||
// Workflow is ready when:
|
||||
// 1. We have an active workflow that matches the URL
|
||||
// 2. The workflow exists in the registry
|
||||
// 3. Workflows are not currently loading
|
||||
if (hasActiveWorkflow && hasWorkflowInRegistry && isNotLoading) {
|
||||
setIsWorkflowReady(true)
|
||||
} else {
|
||||
setIsWorkflowReady(false)
|
||||
}
|
||||
const shouldBeReady =
|
||||
activeWorkflowId === currentId && Boolean(workflows[currentId]) && !isLoading
|
||||
|
||||
setIsWorkflowReady(shouldBeReady)
|
||||
}, [activeWorkflowId, params.workflowId, workflows, isLoading])
|
||||
|
||||
// Init workflow
|
||||
// Handle navigation and validation
|
||||
useEffect(() => {
|
||||
const validateAndNavigate = async () => {
|
||||
const workflowIds = Object.keys(workflows)
|
||||
const currentId = params.workflowId as string
|
||||
|
||||
// Check if workflows have been initially loaded at least once
|
||||
// This prevents premature navigation decisions on page refresh
|
||||
if (!hasWorkflowsInitiallyLoaded()) {
|
||||
logger.info('Waiting for initial workflow load...')
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for both initialization and workflow loading to complete
|
||||
if (isLoading) {
|
||||
logger.info('Workflows still loading, waiting...')
|
||||
// Wait for initial load to complete before making navigation decisions
|
||||
if (!hasWorkflowsInitiallyLoaded() || isLoading) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -948,24 +941,10 @@ const WorkflowContent = React.memo(() => {
|
||||
router.replace(`/workspace/${currentWorkflow.workspaceId}/w/${currentId}`)
|
||||
return
|
||||
}
|
||||
|
||||
// Get current active workflow state
|
||||
const { activeWorkflowId } = useWorkflowRegistry.getState()
|
||||
|
||||
if (activeWorkflowId !== currentId) {
|
||||
// Clear workflow diff store when switching workflows
|
||||
const { clearDiff } = useWorkflowDiffStore.getState()
|
||||
clearDiff()
|
||||
|
||||
setActiveWorkflow(currentId)
|
||||
} else {
|
||||
// Don't reset variables cache if we're not actually switching workflows
|
||||
setActiveWorkflow(currentId)
|
||||
}
|
||||
}
|
||||
|
||||
validateAndNavigate()
|
||||
}, [params.workflowId, workflows, isLoading, setActiveWorkflow, createWorkflow, router])
|
||||
}, [params.workflowId, workflows, isLoading, workspaceId, router])
|
||||
|
||||
// Transform blocks and loops into ReactFlow nodes
|
||||
const nodes = useMemo(() => {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
export default function WorkflowsPage() {
|
||||
const router = useRouter()
|
||||
const { workflows, isLoading, loadWorkflows } = useWorkflowRegistry()
|
||||
const { workflows, isLoading, loadWorkflows, setActiveWorkflow } = useWorkflowRegistry()
|
||||
const [hasInitialized, setHasInitialized] = useState(false)
|
||||
|
||||
const params = useParams()
|
||||
@@ -45,9 +45,14 @@ export default function WorkflowsPage() {
|
||||
|
||||
// If we have valid workspace workflows, redirect to the first one
|
||||
if (workspaceWorkflows.length > 0) {
|
||||
router.replace(`/workspace/${workspaceId}/w/${workspaceWorkflows[0]}`)
|
||||
// Ensure the workflow is set as active before redirecting
|
||||
// This prevents the empty canvas issue on first login
|
||||
const firstWorkflowId = workspaceWorkflows[0]
|
||||
setActiveWorkflow(firstWorkflowId).then(() => {
|
||||
router.replace(`/workspace/${workspaceId}/w/${firstWorkflowId}`)
|
||||
})
|
||||
}
|
||||
}, [hasInitialized, isLoading, workflows, workspaceId, router])
|
||||
}, [hasInitialized, isLoading, workflows, workspaceId, router, setActiveWorkflow])
|
||||
|
||||
// Always show loading state until redirect happens
|
||||
// There should always be a default workflow, so we never show "no workflows found"
|
||||
|
||||
@@ -484,8 +484,6 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
|
||||
future: [],
|
||||
},
|
||||
}
|
||||
|
||||
// Subblock values will be initialized by initializeFromWorkflow below
|
||||
} else {
|
||||
// If no state in DB, use empty state - server should have created start block
|
||||
workflowState = {
|
||||
@@ -535,11 +533,12 @@ export const useWorkflowRegistry = create<WorkflowRegistry>()(
|
||||
}))
|
||||
}
|
||||
|
||||
// Update all stores atomically to prevent race conditions
|
||||
// Set activeWorkflowId and workflow state together
|
||||
set({ activeWorkflowId: id, error: null })
|
||||
useWorkflowStore.setState(workflowState)
|
||||
useSubBlockStore.getState().initializeFromWorkflow(id, (workflowState as any).blocks || {})
|
||||
|
||||
set({ activeWorkflowId: id, error: null })
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('active-workflow-changed', {
|
||||
detail: { workflowId: id },
|
||||
|
||||
Reference in New Issue
Block a user