elsa-workflows / elsa-core

A .NET workflows library
https://v3.elsaworkflows.io/
MIT License
6.27k stars 1.15k forks source link

While and for loops cause massive delay in elsa 3 #4948

Open LukasBenner opened 7 months ago

LukasBenner commented 7 months ago

I noticed some delays in workflows that use a for or a while loop and http endpoints. A simple example is a workflow, that is triggered by an http endpoint and a for loop that is followed by another http endpoint.

The delay isn't in the execution of the workflow but in the request to the http endpoint.

I trigger the workflow by calling the endpoint, the for loop starts and prints to the console. After 1000 iterations the the loop ends and the output in the console stops. Another 2 seconds later, the request to the endpoint returns with status 200. Why does the request return AFTER the for loop finished and why does it take another 1-2 seconds? A real problem occurs if there is another http endpoint after the for loop. I can't call the second endpoint until the first endpoint returned.

I don't knwo if this problem is caused by a poor configuration so I'll post the code of Program.cs

// <copyright file="Program.cs" company="Mitutoyo Europe GmbH">
// Copyright (c) Mitutoyo Europe GmbH, All rights reserved
// Reproduction or transmission in whole or in part, in
// any form or by any means, electronic, mechanical or otherwise, is prohibited
// without the prior written consent of the copyright owner.
// </copyright>

using Elsa.DropIns.Extensions;
using Elsa.EntityFrameworkCore.Extensions;
using Elsa.EntityFrameworkCore.Modules.Management;
using Elsa.EntityFrameworkCore.Modules.Runtime;
using Elsa.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddElsa(elsa =>
{
    // Configure Management layer to use EF Core.
    elsa.UseWorkflowManagement(management
        => management.UseEntityFrameworkCore(options
            => options.UseSqlite(builder.Configuration.GetConnectionString("WorkflowManagementDb")!)));

    // Configure Runtime layer to use EF Core.
    elsa.UseWorkflowRuntime(runtime
        => runtime.UseEntityFrameworkCore(options
            => options.UseSqlite(builder.Configuration.GetConnectionString("WorkflowRuntimesDb")!)));

    // Default Identity features for authentication/authorization.
    elsa.UseIdentity(identity =>
    {
        identity.TokenOptions =
            options => options.SigningKey =
                "sufficiently-large-secret-signing-key"; // This key needs to be at least 256 bits long.
        identity.UseAdminUserProvider();
    });

    // Configure ASP.NET authentication/authorization.
    elsa.UseDefaultAuthentication(auth => auth.UseAdminApiKey());

    // Expose Elsa API endpoints.
    elsa.UseWorkflowsApi();

    // Setup a SignalR hub for real-time updates from the server.
    elsa.UseRealTimeWorkflows();

    elsa.UseCSharp();
    elsa.UseJavaScript();

    // Enable HTTP activities.
    elsa.UseHttp(http => http.ConfigureHttpOptions = options =>
    {
        options.BaseUrl = new Uri("https://localhost:5001");
        options.BasePath = "/workflows";
    });

    // Use timer activities.
    elsa.UseScheduling();

    // Register custom activities from the application, if any.
    elsa.AddActivitiesFrom<Program>();

    // Register custom workflows from the application, if any.
    elsa.AddWorkflowsFrom<Program>();

    elsa.InstallDropIns(options =>
        options.DropInRootDirectory = Path.Combine(Directory.GetCurrentDirectory(), "App_Data", "Plugins"));
});

// Configure CORS to allow designer app hosted on a different origin to invoke the APIs.
builder.Services.AddCors(cors => cors
    .AddDefaultPolicy(policy => policy
        .AllowAnyOrigin() // For demo purposes only. Use a specific origin instead.
        .AllowAnyHeader()
        .AllowAnyMethod()
        .WithExposedHeaders(
            "x-elsa-workflow-instance-id"))); // Required for Elsa Studio in order to support running workflows from the designer. Alternatively, you can use the `*` wildcard to expose all headers.

// Add Health Checks.
builder.Services.AddHealthChecks();
builder.Services.AddControllers();

// Build the web application.
var app = builder.Build();

// Configure web application's middleware pipeline.
app.UseCors();
app.UseRouting(); // Required for SignalR.
app.UseAuthentication();
app.UseAuthorization();
app.UseWorkflowsApi(); // Use Elsa API endpoints.
app.UseWorkflows(); // Use Elsa middleware to handle HTTP requests mapped to HTTP Endpoint activities.
app.UseWorkflowsSignalRHubs(); // Optional SignalR integration. Elsa Studio uses SignalR to receive real-time updates from the server. 

app.Run();

The workflow is rather simple:

{
    "id": "7bdf9cebfa6b048f",
    "definitionId": "a66ad0a9fcf34611",
    "name": "ForTest",
    "createdAt": "2024-02-15T12:28:09.7170621+00:00",
    "version": 6,
    "toolVersion": "3.0.0.0",
    "variables": [
        {
            "id": "8d9e79d501dddb4c",
            "name": "LoopCounter",
            "typeName": "Long",
            "isArray": false,
            "storageDriverTypeName": "Elsa.Workflows.Services.MemoryStorageDriver, Elsa.Workflows.Core"
        }
    ],
    "inputs": [],
    "outputs": [],
    "outcomes": [],
    "customProperties": {},
    "isReadonly": false,
    "isLatest": true,
    "isPublished": true,
    "options": {
        "autoUpdateConsumingWorkflows": false
    },
    "root": {
        "type": "Elsa.Flowchart",
        "version": 1,
        "id": "b80ff2b34c27143",
        "nodeId": "Workflow1:b80ff2b34c27143",
        "metadata": {},
        "customProperties": {
            "source": "FlowchartJsonConverter.cs:45",
            "notFoundConnections": [],
            "canStartWorkflow": false,
            "runAsynchronously": false
        },
        "activities": [
            {
                "start": {
                    "typeName": "Int32",
                    "expression": {
                        "type": "Literal",
                        "value": "0"
                    }
                },
                "end": {
                    "typeName": "Int32",
                    "expression": {
                        "type": "Literal",
                        "value": "1000"
                    }
                },
                "step": {
                    "typeName": "Int32",
                    "expression": {
                        "type": "Literal",
                        "value": "1"
                    }
                },
                "outerBoundInclusive": {
                    "typeName": "Boolean",
                    "expression": {
                        "type": "Literal",
                        "value": true
                    }
                },
                "body": {
                    "type": "Elsa.Flowchart",
                    "version": 1,
                    "id": "c2e36e18be05d880",
                    "nodeId": "Workflow1:b80ff2b34c27143:7b97d8dfa69bf6e8:c2e36e18be05d880",
                    "metadata": {},
                    "customProperties": {
                        "source": "FlowchartJsonConverter.cs:45",
                        "notFoundConnections": [],
                        "canStartWorkflow": false,
                        "runAsynchronously": false
                    },
                    "activities": [
                        {
                            "id": "afa9d9f22a06e95",
                            "nodeId": "Workflow1:b80ff2b34c27143:7b97d8dfa69bf6e8:c2e36e18be05d880:afa9d9f22a06e95",
                            "name": "End1",
                            "type": "Elsa.End",
                            "version": 1,
                            "customProperties": {
                                "canStartWorkflow": false,
                                "runAsynchronously": false
                            },
                            "metadata": {
                                "designer": {
                                    "position": {
                                        "x": 241.6667022705078,
                                        "y": -120
                                    },
                                    "size": {
                                        "width": 94.39999389648438,
                                        "height": 50
                                    }
                                }
                            }
                        },
                        {
                            "id": "d5ff8da030077f",
                            "nodeId": "Workflow1:b80ff2b34c27143:7b97d8dfa69bf6e8:c2e36e18be05d880:d5ff8da030077f",
                            "name": "Start1",
                            "type": "Elsa.Start",
                            "version": 1,
                            "customProperties": {
                                "canStartWorkflow": false,
                                "runAsynchronously": false
                            },
                            "metadata": {
                                "designer": {
                                    "position": {
                                        "x": -300,
                                        "y": -120
                                    },
                                    "size": {
                                        "width": 102.21665954589844,
                                        "height": 50
                                    }
                                }
                            }
                        },
                        {
                            "text": {
                                "typeName": "String",
                                "expression": {
                                    "type": "JavaScript",
                                    "value": "return getLoopCounter();"
                                }
                            },
                            "id": "1ec541701dd33bec",
                            "nodeId": "Workflow1:b80ff2b34c27143:7b97d8dfa69bf6e8:c2e36e18be05d880:1ec541701dd33bec",
                            "name": "WriteLine1",
                            "type": "Elsa.WriteLine",
                            "version": 1,
                            "customProperties": {
                                "canStartWorkflow": false,
                                "runAsynchronously": false
                            },
                            "metadata": {
                                "designer": {
                                    "position": {
                                        "x": -67.22219276428223,
                                        "y": -120
                                    },
                                    "size": {
                                        "width": 139.3333282470703,
                                        "height": 50
                                    }
                                }
                            }
                        }
                    ],
                    "connections": [
                        {
                            "source": {
                                "activity": "d5ff8da030077f",
                                "port": "Done"
                            },
                            "target": {
                                "activity": "1ec541701dd33bec",
                                "port": "In"
                            }
                        },
                        {
                            "source": {
                                "activity": "1ec541701dd33bec",
                                "port": "Done"
                            },
                            "target": {
                                "activity": "afa9d9f22a06e95",
                                "port": "In"
                            }
                        }
                    ]
                },
                "currentValue": {
                    "typeName": "Object",
                    "memoryReference": {
                        "id": "8d9e79d501dddb4c"
                    }
                },
                "id": "7b97d8dfa69bf6e8",
                "nodeId": "Workflow1:b80ff2b34c27143:7b97d8dfa69bf6e8",
                "name": "For1",
                "type": "Elsa.For",
                "version": 1,
                "customProperties": {
                    "canStartWorkflow": false,
                    "runAsynchronously": false
                },
                "metadata": {
                    "designer": {
                        "position": {
                            "x": 100,
                            "y": 165
                        },
                        "size": {
                            "width": 211.1666717529297,
                            "height": 120
                        }
                    }
                }
            },
            {
                "path": {
                    "typeName": "String",
                    "expression": {
                        "type": "Literal",
                        "value": "for"
                    }
                },
                "supportedMethods": {
                    "typeName": "String[]",
                    "expression": {
                        "type": "Object",
                        "value": "[\u0022GET\u0022]"
                    }
                },
                "authorize": {
                    "typeName": "Boolean",
                    "expression": {
                        "type": "Literal",
                        "value": false
                    }
                },
                "policy": {
                    "typeName": "String",
                    "expression": {
                        "type": "Literal"
                    }
                },
                "requestTimeout": null,
                "requestSizeLimit": null,
                "fileSizeLimit": null,
                "allowedFileExtensions": null,
                "blockedFileExtensions": null,
                "allowedMimeTypes": null,
                "exposeRequestTooLargeOutcome": false,
                "exposeFileTooLargeOutcome": false,
                "exposeInvalidFileExtensionOutcome": false,
                "exposeInvalidFileMimeTypeOutcome": false,
                "parsedContent": null,
                "files": null,
                "routeData": null,
                "queryStringData": null,
                "headers": null,
                "result": null,
                "id": "a0a0dce3a6d4e604",
                "nodeId": "Workflow1:b80ff2b34c27143:a0a0dce3a6d4e604",
                "name": "HttpEndpoint1",
                "type": "Elsa.HttpEndpoint",
                "version": 1,
                "customProperties": {
                    "canStartWorkflow": true,
                    "runAsynchronously": false
                },
                "metadata": {
                    "designer": {
                        "position": {
                            "x": -200,
                            "y": 200
                        },
                        "size": {
                            "width": 176.43333435058594,
                            "height": 50
                        }
                    }
                }
            },
            {
                "path": {
                    "typeName": "String",
                    "expression": {
                        "type": "Literal",
                        "value": "continue"
                    }
                },
                "supportedMethods": {
                    "typeName": "String[]",
                    "expression": {
                        "type": "Object",
                        "value": "[\u0022GET\u0022]"
                    }
                },
                "authorize": {
                    "typeName": "Boolean",
                    "expression": {
                        "type": "Literal",
                        "value": false
                    }
                },
                "policy": {
                    "typeName": "String",
                    "expression": {
                        "type": "Literal"
                    }
                },
                "requestTimeout": null,
                "requestSizeLimit": null,
                "fileSizeLimit": null,
                "allowedFileExtensions": null,
                "blockedFileExtensions": null,
                "allowedMimeTypes": null,
                "exposeRequestTooLargeOutcome": false,
                "exposeFileTooLargeOutcome": false,
                "exposeInvalidFileExtensionOutcome": false,
                "exposeInvalidFileMimeTypeOutcome": false,
                "parsedContent": null,
                "files": null,
                "routeData": null,
                "queryStringData": null,
                "headers": null,
                "result": null,
                "id": "47ffe973d32e500",
                "nodeId": "Workflow1:b80ff2b34c27143:47ffe973d32e500",
                "name": "HttpEndpoint2",
                "type": "Elsa.HttpEndpoint",
                "version": 1,
                "customProperties": {
                    "canStartWorkflow": false,
                    "runAsynchronously": false
                },
                "metadata": {
                    "designer": {
                        "position": {
                            "x": 400,
                            "y": 200
                        },
                        "size": {
                            "width": 176.43333435058594,
                            "height": 50
                        }
                    }
                }
            }
        ],
        "connections": [
            {
                "source": {
                    "activity": "7b97d8dfa69bf6e8",
                    "port": "Done"
                },
                "target": {
                    "activity": "47ffe973d32e500",
                    "port": "In"
                }
            },
            {
                "source": {
                    "activity": "a0a0dce3a6d4e604",
                    "port": "Done"
                },
                "target": {
                    "activity": "7b97d8dfa69bf6e8",
                    "port": "In"
                }
            }
        ]
    }
}
glime-ai[bot] commented 7 months ago

Based on the provided context and the workflow JSON, it seems that the delay you are experiencing after the for loop finishes and before the HTTP response is returned could be related to the way the workflow is being executed and how the HTTP response is being handled within the Elsa workflow engine.

The Elsa.HttpEndpoint activity is designed to handle incoming HTTP requests and can be configured to start a workflow. When the workflow is triggered by an HTTP request, the workflow will execute until it reaches a point where it needs to suspend and wait for another event (like another HTTP request) or until it completes. If there is a delay after the for loop and before the HTTP response is returned, it could be due to the workflow execution still being in progress or some post-execution processing that is happening within the Elsa engine.

The additional 1-2 seconds delay after the for loop could be due to the workflow engine finalizing the workflow execution and preparing the HTTP response. This could involve internal processing like logging, persistence, or other housekeeping tasks that the workflow engine needs to perform.

Regarding the inability to call the second HTTP endpoint until the first one returns, this could be due to the workflow instance being in a suspended state waiting for the first HTTP request to complete. If the workflow is designed to wait for an HTTP request at a certain point, it will not be able to process another request until the current one is completed and the workflow has been resumed.

To address these issues, you may want to review the workflow design and ensure that the activities are configured correctly. Additionally, you can look into the Elsa documentation and community resources for best practices on handling HTTP requests and responses within workflows.

If you believe there is a configuration issue or a bug within the Elsa workflow engine, you can reach out to the Elsa community on GitHub or Stack Overflow for further assistance.

Since the delay is not expected behavior and could be specific to your setup or a potential issue with the Elsa workflow engine, I would recommend checking the following:

  1. Elsa version: Ensure you are using the latest stable version of Elsa, as updates may contain performance improvements or bug fixes.
  2. Server performance: Check if the server running the workflow has sufficient resources and is not experiencing high load or latency issues.
  3. Workflow design: Review the workflow design to ensure there are no unnecessary activities or configurations that could introduce delays.
  4. Elsa logs: Check the logs generated by the Elsa workflow engine for any warnings or errors that could indicate a problem.
  5. Elsa community: Reach out to the Elsa community for support, as other users may have encountered similar issues and could provide insights or solutions.

If you provide more specific details about the delay or the behavior of the HTTP endpoints, I may be able to offer more targeted advice.

LukasBenner commented 7 months ago

Ok so here is some additional information:

jdevillard commented 7 months ago

I think this is related to #4701

for my comprehension, return of HTTPEndpoint is done when the workflow is suspended or finished. And at this point, some log & process are flush. The end of the response seems to in sync with some of theses processes.

This issue is identified.

thx

sfmskywalker commented 7 months ago

I think @jdevillard is correct; the description of this issue made me think of #4701 as well.