2019-04-24 16:04:30 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using Tapeti.Config;
|
2019-08-13 18:30:04 +00:00
|
|
|
|
using Tapeti.Default;
|
2019-04-24 16:04:30 +00:00
|
|
|
|
|
|
|
|
|
namespace Tapeti.Transient
|
|
|
|
|
{
|
2019-08-13 18:30:04 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Manages active requests and responses. For internal use.
|
|
|
|
|
/// </summary>
|
2019-08-15 09:26:55 +00:00
|
|
|
|
internal class TransientRouter
|
2019-04-24 16:04:30 +00:00
|
|
|
|
{
|
2019-04-25 13:19:51 +00:00
|
|
|
|
private readonly int defaultTimeoutMs;
|
2022-11-17 15:47:07 +00:00
|
|
|
|
private readonly ConcurrentDictionary<Guid, TaskCompletionSource<object>> map = new();
|
2019-04-24 16:04:30 +00:00
|
|
|
|
|
2019-08-13 18:30:04 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The generated name of the dynamic queue to which responses should be sent.
|
|
|
|
|
/// </summary>
|
2022-11-23 08:13:38 +00:00
|
|
|
|
public string? TransientResponseQueueName { get; set; }
|
2019-04-24 16:04:30 +00:00
|
|
|
|
|
2019-08-13 18:30:04 +00:00
|
|
|
|
|
2021-05-29 19:51:58 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// </summary>
|
2019-04-25 13:19:51 +00:00
|
|
|
|
public TransientRouter(TimeSpan defaultTimeout)
|
2019-04-24 16:04:30 +00:00
|
|
|
|
{
|
2019-04-25 13:19:51 +00:00
|
|
|
|
defaultTimeoutMs = (int)defaultTimeout.TotalMilliseconds;
|
2019-04-24 16:04:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-13 18:30:04 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Processes incoming messages to complete the corresponding request task.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="context"></param>
|
|
|
|
|
public void HandleMessage(IMessageContext context)
|
2019-04-24 16:04:30 +00:00
|
|
|
|
{
|
|
|
|
|
if (context.Properties.CorrelationId == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!Guid.TryParse(context.Properties.CorrelationId, out var continuationID))
|
|
|
|
|
return;
|
|
|
|
|
|
2022-11-23 08:13:38 +00:00
|
|
|
|
if (!map.TryRemove(continuationID, out var tcs))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (context.Message == null)
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
|
|
|
|
|
tcs.TrySetResult(context.Message);
|
2019-04-24 16:04:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-13 18:30:04 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sends a request and waits for the response. Do not call directly, instead use ITransientPublisher.RequestResponse.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="publisher"></param>
|
|
|
|
|
/// <param name="request"></param>
|
|
|
|
|
/// <returns></returns>
|
2019-04-25 13:19:51 +00:00
|
|
|
|
public async Task<object> RequestResponse(IPublisher publisher, object request)
|
2019-04-24 16:04:30 +00:00
|
|
|
|
{
|
|
|
|
|
var correlation = Guid.NewGuid();
|
2022-11-22 12:20:47 +00:00
|
|
|
|
var tcs = map.GetOrAdd(correlation, _ => new TaskCompletionSource<object>());
|
2019-04-24 16:04:30 +00:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2019-08-13 18:30:04 +00:00
|
|
|
|
var properties = new MessageProperties
|
2019-04-24 16:04:30 +00:00
|
|
|
|
{
|
|
|
|
|
CorrelationId = correlation.ToString(),
|
|
|
|
|
ReplyTo = TransientResponseQueueName,
|
|
|
|
|
Persistent = false
|
|
|
|
|
};
|
|
|
|
|
|
2024-04-08 12:20:15 +00:00
|
|
|
|
await ((IInternalPublisher)publisher).Publish(request, properties, false).ConfigureAwait(false);
|
2019-04-24 16:04:30 +00:00
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
// Simple cleanup of the task and map dictionary.
|
|
|
|
|
if (map.TryRemove(correlation, out tcs))
|
2022-11-23 08:13:38 +00:00
|
|
|
|
tcs.TrySetResult(null!);
|
2019-04-24 16:04:30 +00:00
|
|
|
|
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-22 12:20:47 +00:00
|
|
|
|
await using (new Timer(TimeoutResponse, tcs, defaultTimeoutMs, -1))
|
2019-04-24 16:04:30 +00:00
|
|
|
|
{
|
2024-04-08 12:20:15 +00:00
|
|
|
|
return await tcs.Task.ConfigureAwait(false);
|
2019-04-24 16:04:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-13 18:30:04 +00:00
|
|
|
|
|
2022-11-23 08:13:38 +00:00
|
|
|
|
private void TimeoutResponse(object? tcs)
|
2019-04-24 16:04:30 +00:00
|
|
|
|
{
|
2022-11-23 08:13:38 +00:00
|
|
|
|
ArgumentNullException.ThrowIfNull(tcs, nameof(tcs));
|
|
|
|
|
|
2021-09-09 13:11:44 +00:00
|
|
|
|
((TaskCompletionSource<object>)tcs).TrySetException(new TimeoutException("Transient RequestResponse timed out at (ms) " + defaultTimeoutMs));
|
2019-04-24 16:04:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|