[PR #31230] fix (workflow): Fixed workflow execution status update logic and added support for secure storage mode. #33145

Closed
opened 2026-02-21 20:52:44 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/langgenius/dify/pull/31230

State: closed
Merged: No


Handled status update logic for workflow and node execution, ensuring terminal state is not overwritten by non-terminal state.

Added support for secure storage mode parameters.

Optimized database operations and added integrity error handling.

Important

  1. Make sure you have read our contribution guidelines
  2. Ensure there is an associated issue and you have been assigned to it
  3. Use the correct syntax to link this PR: Fixes #<issue number>.

Summary

I have implemented a fix that addresses both issues:

  1. Atomic "Insert or Update" Strategy:
    Wrap the insert operation in a try...except IntegrityError block. If an insertion fails due to a duplicate key, strictly rollback and fallback to the update logic.

  2. State Machine Guard:
    Enforce a unidirectional state transition policy in the update logic.

Code Snippet (Fix Implementation Pattern):

# Applied to both save_workflow_node_execution_task and save_workflow_execution_task

try:
    session.add(new_record)
    session.commit()
    return True
except IntegrityError:
    # Handle race condition: another worker inserted the record concurrently
    session.rollback()
    existing_record = session.scalar(select(Model).where(Model.id == record_id))
    if existing_record:
        _update_from_domain(existing_record, execution_data)
        session.commit()
        return True
    # Retry insert if somehow still missing
    session.add(new_record)
    session.commit()
    return True

And the state guard:

def _update_from_domain(db_record, domain_data):
    terminal = {"succeeded", "failed", "stopped", "exception", "partial-succeeded"}
    if db_record.status in terminal and domain_data.status.value not in terminal:
        # Ignore status update to prevent regression
        # Update other fields (outputs, etc.) if necessary
        db_record.finished_at = db_record.finished_at or domain_data.finished_at
        return
    # ... normal update

Screenshots

Before After
... ...

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I've updated the documentation accordingly.
  • I ran make lint and make type-check (backend) and cd web && npx lint-staged (frontend) to appease the lint gods

#31222

**Original Pull Request:** https://github.com/langgenius/dify/pull/31230 **State:** closed **Merged:** No --- Handled status update logic for workflow and node execution, ensuring terminal state is not overwritten by non-terminal state. Added support for secure storage mode parameters. Optimized database operations and added integrity error handling. > [!IMPORTANT] > > 1. Make sure you have read our [contribution guidelines](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) > 1. Ensure there is an associated issue and you have been assigned to it > 1. Use the correct syntax to link this PR: `Fixes #<issue number>`. ## Summary I have implemented a fix that addresses both issues: 1. **Atomic "Insert or Update" Strategy**: Wrap the `insert` operation in a `try...except IntegrityError` block. If an insertion fails due to a duplicate key, strictly rollback and fallback to the `update` logic. 2. **State Machine Guard**: Enforce a unidirectional state transition policy in the update logic. **Code Snippet (Fix Implementation Pattern):** ```python # Applied to both save_workflow_node_execution_task and save_workflow_execution_task try: session.add(new_record) session.commit() return True except IntegrityError: # Handle race condition: another worker inserted the record concurrently session.rollback() existing_record = session.scalar(select(Model).where(Model.id == record_id)) if existing_record: _update_from_domain(existing_record, execution_data) session.commit() return True # Retry insert if somehow still missing session.add(new_record) session.commit() return True ``` And the state guard: ```python def _update_from_domain(db_record, domain_data): terminal = {"succeeded", "failed", "stopped", "exception", "partial-succeeded"} if db_record.status in terminal and domain_data.status.value not in terminal: # Ignore status update to prevent regression # Update other fields (outputs, etc.) if necessary db_record.finished_at = db_record.finished_at or domain_data.finished_at return # ... normal update ``` ## Screenshots | Before | After | |--------|-------| | ... | ... | ## Checklist - [ ] This change requires a documentation update, included: [Dify Document](https://github.com/langgenius/dify-docs) - [x] I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!) - [x] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change. - [x] I've updated the documentation accordingly. - [x] I ran `make lint` and `make type-check` (backend) and `cd web && npx lint-staged` (frontend) to appease the lint gods #31222
yindo added the pull-request label 2026-02-21 20:52:44 -05:00
yindo closed this issue 2026-02-21 20:52:44 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#33145