fix(persistence): chunk block and edge inserts to prevent sql variabl…#3428
fix(persistence): chunk block and edge inserts to prevent sql variabl…#3428Siddhartha-singh01 wants to merge 1 commit intosimstudioai:mainfrom
Conversation
PR SummaryLow Risk Overview Instead of single bulk inserts, the transaction now writes records in fixed-size chunks ( Written by Cursor Bugbot for commit 160dc1c. This will update automatically on new commits. Configure here. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Greptile SummaryThis PR fixes a critical data-loss bug in self-hosted (SQLite) deployments where workflows would disappear after a page refresh. The root cause was that The fix introduces a Key points:
Style feedback:
Confidence Score: 4/5
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[saveWorkflowToNormalizedTables called] --> B[Begin db.transaction]
B --> C[DELETE existing blocks / edges / subflows]
C --> D{blocks.length > 0?}
D -- Yes --> E[Build blockInserts array]
E --> F[Loop: slice chunks of 50\ninsert each chunk into workflowBlocks]
D -- No --> G{edges.length > 0?}
F --> G
G -- Yes --> H[Build edgeInserts array]
H --> I[Loop: slice chunks of 50\ninsert each chunk into workflowEdges]
G -- No --> J[Build subflowInserts array]
I --> J
J --> K{subflowInserts.length > 0?}
K -- Yes --> L[Loop: slice chunks of 50\ninsert each chunk into workflowSubflows]
K -- No --> M[Commit transaction]
L --> M
M --> N[Return success: true]
style F fill:#d4edda,stroke:#28a745
style I fill:#d4edda,stroke:#28a745
style L fill:#d4edda,stroke:#28a745
Last reviewed commit: 160dc1c |
| tx.delete(workflowSubflows).where(eq(workflowSubflows.workflowId, workflowId)), | ||
| ]) | ||
|
|
||
| const CHUNK_SIZE = 50 |
There was a problem hiding this comment.
CHUNK_SIZE is defined inside the db.transaction async callback, which means it's re-created on every call to saveWorkflowToNormalizedTables. Since it's a fixed, never-changing value, consider hoisting it to module scope. This makes the intent clearer and avoids unnecessary re-allocation.
| const CHUNK_SIZE = 50 | |
| const CHUNK_SIZE = 50 |
Place this near the top of the file alongside other module-level constants (e.g., after the logger definition on line 22).
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| for (let i = 0; i < blockInserts.length; i += CHUNK_SIZE) { | ||
| await tx.insert(workflowBlocks).values(blockInserts.slice(i, i + CHUNK_SIZE)) | ||
| } |
There was a problem hiding this comment.
The SQLite hard limit of 999 bound parameters per statement is implicit in the choice of CHUNK_SIZE = 50, but this constraint is not documented. With 17 fields per block record, a chunk of 50 yields 850 parameters—safely under the limit. However, if new fields are ever added to workflowBlocks without updating CHUNK_SIZE, the code will silently fail. Consider documenting the derivation:
| for (let i = 0; i < blockInserts.length; i += CHUNK_SIZE) { | |
| await tx.insert(workflowBlocks).values(blockInserts.slice(i, i + CHUNK_SIZE)) | |
| } | |
| // SQLite limits bound parameters to 999 per statement. | |
| // workflowBlocks has 17 fields → max safe chunk = floor(999/17) = 58. | |
| // Using 50 for a conservative margin. | |
| for (let i = 0; i < blockInserts.length; i += CHUNK_SIZE) { | |
| await tx.insert(workflowBlocks).values(blockInserts.slice(i, i + CHUNK_SIZE)) | |
| } |
|
@Siddhartha-singh01 please point this to the staging branch not main |
Summary
Fixes the bug where workflows disappear or show up empty after a page refresh in self-hosted mode (SQLite).
The issue was that saveWorkflowToNormalizedTables was trying to bulk insert all blocks at once. SQLite has a hard limit of 999 parameters per query. Since every block has 17 fields, saving workflows with around 60 blocks was hitting the limit and silently failing the save transaction.
I just added a quick fix to chunk the array into smaller batches of 50 before inserting them.
Fixes #2424