Merge branch 'claudedo/5c80a0aba5d9468a96cf809fc7a6e7a1'
This commit is contained in:
@@ -432,6 +432,50 @@ public sealed class TaskStateService : ITaskStateService
|
||||
return new TransitionResult(true, null);
|
||||
}
|
||||
|
||||
public async Task<TransitionResult> SetDependsOnAsync(string taskId, string? dependsOnTaskId, CancellationToken ct)
|
||||
{
|
||||
await using var ctx = await _dbFactory.CreateDbContextAsync(ct);
|
||||
|
||||
if (dependsOnTaskId is not null)
|
||||
{
|
||||
if (dependsOnTaskId == taskId)
|
||||
return new TransitionResult(false, "A task cannot depend on itself.");
|
||||
|
||||
if (!await ctx.Tasks.AsNoTracking().AnyAsync(t => t.Id == dependsOnTaskId, ct))
|
||||
return new TransitionResult(false, $"Dependency task {dependsOnTaskId} not found.");
|
||||
|
||||
// Walk the proposed predecessor's own chain of dependencies; if it leads back to
|
||||
// taskId, linking here would create a cycle that starves both tasks forever (the
|
||||
// picker never claims either). `visited` also stops us looping forever on
|
||||
// pre-existing bad data unrelated to this write.
|
||||
var current = dependsOnTaskId;
|
||||
var visited = new HashSet<string>();
|
||||
while (current is not null)
|
||||
{
|
||||
if (current == taskId)
|
||||
return new TransitionResult(false, "Setting this dependency would create a cycle.");
|
||||
if (!visited.Add(current))
|
||||
break;
|
||||
current = await ctx.Tasks.AsNoTracking()
|
||||
.Where(t => t.Id == current)
|
||||
.Select(t => t.DependsOnTaskId)
|
||||
.FirstOrDefaultAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
var affected = await ctx.Tasks
|
||||
.Where(t => t.Id == taskId)
|
||||
.ExecuteUpdateAsync(s => s.SetProperty(t => t.DependsOnTaskId, dependsOnTaskId), ct);
|
||||
|
||||
if (affected == 0)
|
||||
return new TransitionResult(false, "Task not found.");
|
||||
|
||||
// Clearing a dependency may free up a Queued task the picker was skipping.
|
||||
if (dependsOnTaskId is null) _waker.Wake();
|
||||
await _broadcaster.TaskUpdated(taskId);
|
||||
return new TransitionResult(true, null);
|
||||
}
|
||||
|
||||
public async Task<int> RecoverStaleRunningAsync(string reason, CancellationToken ct)
|
||||
{
|
||||
var resultText = "[stale] " + reason;
|
||||
|
||||
Reference in New Issue
Block a user