w4c-workflows-api/Services/Runs/RunLauncher.cs

75 lines
2.4 KiB
C#

using Microsoft.EntityFrameworkCore;
using w4c_workflows.Data;
using w4c_workflows.Models;
namespace w4c_workflows.Services.Runs;
/// <summary>Request to start a workflow run.</summary>
public sealed record LaunchRequest(
string TenantId,
Guid WorkflowId,
string? TriggerJson,
string? InputJson,
string? CorrelationId,
Guid? StartTaskId = null);
/// <summary>
/// Starts a run for a compiled workflow by creating a <c>pending</c>
/// <see cref="WorkflowRun"/> row. This is the seam between the trigger engine
/// (step 8 — decides WHEN to run) and the run lifecycle engine (step 9 — owns
/// dispatch via Redis, worker result consumption, chain advancement and durable
/// checkpoint/resume). The lifecycle engine picks up <c>pending</c> runs and
/// drives them to completion, so the trigger engine never touches the jobs
/// stream.
/// </summary>
public interface IRunLauncher
{
Task<Guid> LaunchAsync(LaunchRequest request, CancellationToken ct);
}
public class RunLauncher : IRunLauncher
{
private readonly WorkflowsDbContext _db;
private readonly ILogger<RunLauncher> _logger;
public RunLauncher(WorkflowsDbContext db, ILogger<RunLauncher> logger)
{
_db = db;
_logger = logger;
}
public async Task<Guid> LaunchAsync(LaunchRequest request, CancellationToken ct)
{
var workflow = await _db.Workflows
.FirstOrDefaultAsync(w =>
w.Id == request.WorkflowId
&& w.TenantId == request.TenantId
&& w.Status == WorkflowStatus.Compiled, ct);
if (workflow == null)
throw new InvalidOperationException(
$"workflow {request.WorkflowId} not found or not compiled for tenant {request.TenantId}");
var run = new WorkflowRun
{
Id = Guid.NewGuid(),
WorkflowId = workflow.Id,
TenantId = request.TenantId,
Status = RunStatus.Pending,
TriggerJson = request.TriggerJson,
InputJson = request.InputJson,
CorrelationId = request.CorrelationId,
StartTaskId = request.StartTaskId,
};
_db.WorkflowRuns.Add(run);
await _db.SaveChangesAsync(ct);
_logger.LogDebug(
"Launched pending run {RunId} for workflow {WorkflowId} (tenant {TenantId}, correlation {CorrelationId})",
run.Id, workflow.Id, request.TenantId, request.CorrelationId);
return run.Id;
}
}