9.1 KiB
DB-backed Object Consistency Refactor
Date: 2025-10-16
Owner: Workflow/Data Layer
Goal: Eliminate divergence between in-memory objects and database records by centralizing create/update/delete (CUD) logic in service/DB layer, and prohibiting direct list mutations in workflow objects.
Inputs / Constraints
- No caching subsystem: we use the in-memory workflow objects directly (no separate cache layer to reconcile).
- ChatDocuments must be able to be produced and exist in memory only (transient) when needed; this capability must be preserved.
Implication: Service methods must support both DB-backed and in-memory-only flows, with clear, explicit steps for when to persist.
1) Affected Pydantic Models (with DB representation)
Source: gateway/modules/interfaces/interfaceDbChatObjects.py
- ChatWorkflow (table: workflows)
- ChatMessage (table: messages)
- ChatDocument (table: documents)
- ChatLog (table: logs)
- ChatStat (table: stats)
Notes:
ChatWorkflowhas references to:messages,logs,stats(loaded via read operations only)ChatMessagehas references to:documents,stats
2) Where CUD is performed today (by module/function)
-
Workflow (ChatWorkflow)
- createWorkflow/updateWorkflow/deleteWorkflow:
interfaceDbChatObjects.ChatObjects - updateWorkflowStats:
serviceWorkflow.WorkflowService
- createWorkflow/updateWorkflow/deleteWorkflow:
-
Message (ChatMessage)
- createMessage/updateMessage/deleteMessage:
interfaceDbChatObjects.ChatObjects - createMessage (write-through + local append attempt):
serviceWorkflow.WorkflowService.createMessage - MessageCreator paths (indirect, see forbidden appends below):
workflows/processing/core/messageCreator.py - workflowManager paths (indirect, see forbidden appends below):
workflows/workflowManager.py
- createMessage/updateMessage/deleteMessage:
-
Document (ChatDocument)
- createDocument/getDocuments:
interfaceDbChatObjects.ChatObjects - deleteFileFromMessage:
interfaceDbChatObjects.ChatObjects
- createDocument/getDocuments:
-
Log (ChatLog)
- createLog/getLogs:
interfaceDbChatObjects.ChatObjects
- createLog/getLogs:
-
Stat (ChatStat)
- getMessageStats/getWorkflowStats:
interfaceDbChatObjects.ChatObjects - updateWorkflowStats:
serviceWorkflow.WorkflowService
- getMessageStats/getWorkflowStats:
3) Forbidden in-memory .append(...) sites (must be removed or guarded)
-
gateway/modules/services/serviceWorkflow/mainServiceWorkflow.py- createMessage:
workflow.messages.append(message)(has id-based guard; keep only this site to have calls to self.services.workflow.createMessage(...) in the code) - (Two occurrences)
workflow.logs.append(log_entry)
- createMessage:
-
gateway/modules/workflows/processing/core/messageCreator.py- Multiple occurrences of
workflow.messages.append(message)
- Multiple occurrences of
-
gateway/modules/workflows/workflowManager.py- Multiple occurrences of
workflow.messages.append(message)across handling steps
- Multiple occurrences of
-
gateway/modules/workflows/processing/modes/modeReact.pyworkflow.messages.append(message)
-
gateway/modules/workflows/processing/modes/modeActionplan.pyworkflow.messages.append(message)
Action: Replace all of the above with calls to serviceWorkflow.createMessage/updateMessage methods. No direct appends.
4) Cascading CUD that should NOT happen
Observed in: interfaceDbChatObjects.ChatObjects.updateWorkflow (lines updating logs, messages, stats inside workflow update).
Issue: Parent-level update attempts to write child tables (‘cascade write’) during a workflow update. This causes ordering issues and potential duplicates.
Action:
- Remove cascade CUD from
updateWorkflow. Workflow update should only modify workflow fields. - CUD for
messages,logs,stats,documentsmust be done via their own dedicated service methods. - Keep cascade on READ ONLY (e.g.,
getWorkflowloads logs/messages/stats/documents).
5) Proposed Operating Model (write-through, DB as source of truth)
-
For each DB-backed object (workflow, message, document, log, stat):
- All create/update/delete goes through
interfaceDbChatObjects.ChatObjectsorserviceWorkflow.WorkflowServicededicated methods. - After DB success, update in-memory cache (replace by id or remove by id). No direct
.append()other than in the single service function immediately after DB write (and guarded by id check). - All other modules must not mutate lists directly. Replace with service calls.
- All create/update/delete goes through
-
Reads: May hydrate workflow with referenced objects (messages/logs/stats/documents). No writes in read functions.
6) Tasklist: Interface changes in interfaceDbChatObjects.py
- Remove cascade writes in
updateWorkflowforlogs,messages,stats. - Ensure
createMessagereturns persistedChatMessageand does not assume localsequenceNrfrom in-memory list; optionally compute from DB if required, or drop if unused. - Ensure
updateMessageonly updates message table and related tables when explicitly provided via dedicated methods. - Ensure
createDocumentanddeleteFileFromMessageare the only paths to manage documents. - Add helper
syncWorkflowInMemory(workflowId)to refreshservices.currentWorkflowfrom DB when needed.
7) Tasklist: Module/function updates
-
services/serviceWorkflow/mainServiceWorkflow.py- createMessage: keep DB write-through; keep single guarded in-memory append (or drop append and rely on refresh); add optional
syncWorkflowInMemorycall. - Remove any direct
workflow.logs.append(...)writes; replace withcreateLogand in-memory refresh.
- createMessage: keep DB write-through; keep single guarded in-memory append (or drop append and rely on refresh); add optional
-
workflows/processing/core/messageCreator.py- Replace all
workflow.messages.append(message)withservices.workflow.createMessage(...)(orupdateMessage(...)if appropriate).
- Replace all
-
workflows/workflowManager.py- Replace all direct appends with service calls.
-
workflows/processing/modes/modeReact.py- Replace direct append with service calls.
-
workflows/processing/modes/modeActionplan.py- Replace direct append with service calls.
8) Diagnostics to add (temporary)
- Before any in-memory list mutation (temporary while refactoring):
- Log caller, object type, id, len(list) before/after.
- This will surface any remaining illegal appends.
10) Service-level Transactions (must update DB and in-memory references)
All transactions below are implemented at the service layer (not interface) and must perform both the database write and the in-memory reference updates to keep the live workflow object consistent.
A) Store message with documents to workflow
Function: services.workflow.storeMessageWithDocuments(workflow, messageData, documents)
Required behavior:
- Ensure ChatMessage exists in DB (create if not exists) with proper
workflowId(source of truth id coming from parameter). - Persist each ChatDocument in DB referencing the created message (set
messageId) and the sameworkflowIdwhen applicable. - Rehydrate/construct a
ChatMessageobject that contains the newly createdChatDocumentobjects. - Update in-memory workflow object:
- Append/replace the message in
services.currentWorkflow.messagesby id (replace if exists, else append). - Ensure the in-memory
ChatMessage.documentspoints to the same list of in-memoryChatDocumentinstances.
- Append/replace the message in
- Return the in-memory
ChatMessage(with documents) as confirmation.
Notes:
- If
documentsinclude in-memory-onlyChatDocumentinstances (no DB ids yet), step (2) must persist them and update their ids before binding them back to the message in-memory object. - No other modules are allowed to mutate
workflow.messagesdirectly; this transaction is the single entry point for this behavior.
Future transactions (to be specified similarly):
- Update message with added/removed documents
- Remove document from message
- Delete message (+ cascade delete of its documents in DB) and remove from in-memory workflow
B) Store log entry to workflow
Function: services.workflow.storeLog(workflow, logData)
Required behavior:
- Persist
ChatLogin DB withworkflowId. - Update in-memory workflow object: append/replace the
ChatLoginservices.currentWorkflow.logsby id (if logs are held in-memory), or trigger a lightweight refresh. - Return the in-memory
ChatLog.
Notes:
- No direct
workflow.logs.append(...)outside this service method.
C) Store stat entry to workflow (workflow-level stat)
Function: services.workflow.storeWorkflowStat(workflow, statData)
Required behavior:
- Persist
ChatStatin DB withworkflowId(nomessageId). - Update in-memory workflow object: set/replace workflow-level
statsonservices.currentWorkflowas applicable. - Return the in-memory
ChatStat.
Notes:
- No direct in-memory mutation outside this service method.
D) Store stat entry to message (message-level stat)
Function: services.workflow.storeMessageStat(workflow, messageId, statData)
Required behavior:
- Persist
ChatStatin DB with bothworkflowIdandmessageId. - Update in-memory workflow object: locate message by
messageIdinservices.currentWorkflow.messagesand set/replace itsstats. - Return the in-memory
ChatStat.
Notes:
- If the target message is not present in memory, optionally refresh the workflow messages or require caller to provide the message object; no direct appends outside service method.