feat(engine): WebhookRouter (TCS + first-write-wins + timeout)
This commit is contained in:
35
src/VolcanicCore.Api/Engine/WebhookRouter.cs
Normal file
35
src/VolcanicCore.Api/Engine/WebhookRouter.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace VolcanicCore.Api.Engine;
|
||||
|
||||
public sealed record WebResponsePayload(int StatusCode, string Body, string ContentType);
|
||||
|
||||
public sealed class WebhookRouter
|
||||
{
|
||||
private sealed record Entry(
|
||||
TaskCompletionSource<WebResponsePayload?> Tcs,
|
||||
CancellationTokenSource Timeout);
|
||||
|
||||
private readonly ConcurrentDictionary<Guid, Entry> _pending = new();
|
||||
|
||||
public Task<WebResponsePayload?> Register(Guid id, TimeSpan timeout)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<WebResponsePayload?>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var cts = new CancellationTokenSource(timeout);
|
||||
cts.Token.Register(() =>
|
||||
{
|
||||
if (_pending.TryRemove(id, out _))
|
||||
tcs.TrySetResult(null); // timeout fires as null
|
||||
});
|
||||
_pending[id] = new Entry(tcs, cts);
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
// Returns true iff this call was the first to resolve the channel.
|
||||
public bool Resolve(Guid id, WebResponsePayload payload)
|
||||
{
|
||||
if (!_pending.TryRemove(id, out var entry)) return false;
|
||||
entry.Timeout.Dispose();
|
||||
return entry.Tcs.TrySetResult(payload);
|
||||
}
|
||||
}
|
||||
@@ -117,6 +117,8 @@ builder.Services.AddSingleton<ConnectionInstanceRegistry>();
|
||||
builder.Services.AddSingleton<IConnectionInstanceRegistry>(sp =>
|
||||
sp.GetRequiredService<ConnectionInstanceRegistry>());
|
||||
|
||||
builder.Services.AddSingleton<WebhookRouter>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Apply migrations at boot.
|
||||
|
||||
Reference in New Issue
Block a user