fix: address #167 review — release Trakt lock on failed enqueue; mark busy after generatePlaylist save
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m2s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m30s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 15:26:01 +02:00
co-authored by Claude Fable 5
parent 1b9d85896c
commit 9c03184707
2 changed files with 47 additions and 23 deletions
+27 -21
View File
@@ -76,13 +76,7 @@ public partial class TraktController(
CreateProblemDetails(422, "Validation failed", "Invalid Trakt list url"));
}
if (!entityLocker.LockTrakt())
{
return ConflictProblem();
}
await workerChannel.WriteAsync(AddTraktList.FromUrl(request.Url), cancellationToken);
return new AcceptedResult();
return await EnqueueWithTraktLock(AddTraktList.FromUrl(request.Url), cancellationToken);
}
[HttpPost("/api/trakt/lists/{id:int}/match")]
@@ -100,13 +94,7 @@ public partial class TraktController(
return ApiResults.NotFoundProblem();
}
if (!entityLocker.LockTrakt())
{
return ConflictProblem();
}
await workerChannel.WriteAsync(new MatchTraktListItems(id), cancellationToken);
return new AcceptedResult();
return await EnqueueWithTraktLock(new MatchTraktListItems(id), cancellationToken);
}
[HttpDelete("/api/trakt/lists/{id:int}")]
@@ -124,13 +112,7 @@ public partial class TraktController(
return ApiResults.NotFoundProblem();
}
if (!entityLocker.LockTrakt())
{
return ConflictProblem();
}
await workerChannel.WriteAsync(new DeleteTraktList(id), cancellationToken);
return new AcceptedResult();
return await EnqueueWithTraktLock(new DeleteTraktList(id), cancellationToken);
}
[HttpPut("/api/trakt/lists/{id:int}")]
@@ -185,6 +167,30 @@ public partial class TraktController(
viewModel.AutoRefresh,
viewModel.GeneratePlaylist);
private async Task<IActionResult> EnqueueWithTraktLock(
IBackgroundServiceRequest request,
CancellationToken cancellationToken)
{
if (!entityLocker.LockTrakt())
{
return ConflictProblem();
}
try
{
await workerChannel.WriteAsync(request, cancellationToken);
}
catch
{
// the background handler only unlocks when it receives the message;
// if enqueueing fails (e.g. request aborted), release the lock here or it is held forever
entityLocker.UnlockTrakt();
throw;
}
return new AcceptedResult();
}
private static ConflictObjectResult ConflictProblem() =>
new ConflictObjectResult(
CreateProblemDetails(