using Microsoft.EntityFrameworkCore;
using w4c_workflows.Data;
using w4c_workflows.Models;
namespace w4c_workflows.Services.Runs;
///
/// Persists durable/handler checkpoint state. The checkpoint
/// records the instance's position (TaskId = the next task to run) and
/// the input to feed it (StateJson), enabling resume after an external
/// event. The instance id is a run id for durable workflows and a stable
/// per-workflow id for handler workflows.
///
public class DurableStateStore
{
private readonly WorkflowsDbContext _db;
public DurableStateStore(WorkflowsDbContext db)
{
_db = db;
}
public async Task CheckpointAsync(
string instanceId,
Guid taskId,
string? stateJson,
string? correlationId,
CancellationToken ct)
{
var existing = await _db.DurableStates
.FirstOrDefaultAsync(d => d.InstanceId == instanceId, ct);
var now = DateTime.UtcNow;
if (existing == null)
{
_db.DurableStates.Add(new DurableState
{
InstanceId = instanceId,
TaskId = taskId,
StateJson = stateJson,
CheckpointAt = now,
CorrelationId = correlationId,
});
}
else
{
existing.TaskId = taskId;
existing.StateJson = stateJson;
existing.CheckpointAt = now;
if (!string.IsNullOrWhiteSpace(correlationId))
existing.CorrelationId = correlationId;
}
await _db.SaveChangesAsync(ct);
}
public Task GetAsync(string instanceId, CancellationToken ct)
=> _db.DurableStates.FirstOrDefaultAsync(d => d.InstanceId == instanceId, ct);
public async Task ClearAsync(string instanceId, CancellationToken ct)
{
var existing = await _db.DurableStates
.FirstOrDefaultAsync(d => d.InstanceId == instanceId, ct);
if (existing == null)
return;
_db.DurableStates.Remove(existing);
await _db.SaveChangesAsync(ct);
}
}