-
Notifications
You must be signed in to change notification settings - Fork 61
Description
Context
When multiple users apply an AI-generated workflow simultaneously, the Y.Array CRDT can create duplicate entries. This happens because Y.Array's push() operation is always additive - concurrent pushes from different users all get merged.
Current data structure:
ydoc.getArray('jobs') // Y.Array<Y.Map>
ydoc.getArray('triggers') // Y.Array<Y.Map>
ydoc.getArray('edges') // Y.Array<Y.Map>Current apply logic (in YAMLStateToYDoc.applyToYDoc):
const jobsArray = ydoc.getArray('jobs');
jobsArray.delete(0, jobsArray.length); // Clear
jobsArray.push(transformedJobs); // Add newWhen two users click Apply concurrently:
- Both delete operations target the same items
- Both push operations add the same items
- CRDT merge includes BOTH pushes → duplicate entries
Current Solution (Implemented)
We implemented server-coordinated locking via Phoenix Channel:
- When a user clicks Apply, backend broadcasts
workflow_applyingto all clients - All clients disable their Apply buttons
- When apply completes, backend broadcasts
workflow_applied - All clients re-enable Apply buttons
This is 100% effective because the server is the single source of truth.
Files changed:
lib/lightning_web/channels/workflow_channel.ex- Addedstart_applying_workflowanddone_applying_workflowhandlersassets/js/collaborative-editor/stores/createWorkflowStore.ts- Channel listeners and coordination methodsassets/js/collaborative-editor/components/AIAssistantPanelWrapper.tsx- Calls coordination methods
Proposed Future Improvement
Change the Y.Doc structure to use Y.Map keyed by ID:
ydoc.getMap('jobs') // Y.Map<id, Y.Map>
ydoc.getMap('triggers') // Y.Map<id, Y.Map>
ydoc.getMap('edges') // Y.Map<id, Y.Map>Why this would be better:
map.set(id, data)is idempotent - two users setting the same key = same result, no duplicates- More semantically correct (entities are keyed by ID)
- Simpler apply logic without needing coordination
What would need to change:
-
Frontend:
YAMLStateToYDoc.applyToYDoc- Usemap.set(id, data)instead ofarray.push()createWorkflowStore.ts- Update observers to read from maps- Convert map values to arrays for Immer state (React expects arrays)
-
Backend:
lib/lightning/collaboration/workflow_serializer.ex- ChangeYex.Doc.get_arraytoYex.Doc.get_map- Update initialization and extraction logic
Priority
Low - The server-coordinated solution works perfectly. This is an architectural improvement for cleaner CRDT semantics, not a bug fix.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status