I’m trying to implement a producer/consumer queue using Dataflow for HTTP requests towards a web service. I found an excellent post from Stephen Cleary, which is covering exactly this scenario. However, in contrast to Stephen’s post, I cannot mark the producer queue as complete since clients shall be able to enqueue requests throughout the entire lifetime of the application. The idea behind this approach that the client can constantly produce requests and the consumer is able to handle requests differently if more than 1 request is pending (which is required).
This requirement leads also to the fact that the consumption of the requests cannot be started after the production was finished, but have to be started the first request was enqueued. This also requires me to start the consumption in a non-blocking way (otherwise it would lead to a deadlock). I’ve done this via an async-call which is not awaited, which unfortunately hampers the exception handling. Exceptions occurring during the consumption (implementing the HTTP requests) cannot bubble up since the call of the consume-function is not awaited. I’ve introduced and event to deal with this kind of problem, but this leads me to the following questions:
Is it a good idea to use an event to forward exceptions from the consumer to the client of the producer?
Is this a good idea to implement the producer/consumer pattern in that fashion for my use case?
Are there potentially other approaches, which are more beneficial under the given circumstances?
To make it a more explicit, I’ve prepared a code example illustrating the problem I described above:
public class HttpConnector
{
private BufferBlock<RequestPayload> queue;
public delegate void ConnectorExceptionHandler(object sender, Exception e);
public event ConnectorExceptionHandler ConnectorExceptionOccured;
public Task<bool> ProduceRequest(RequestPayload payload)
{
if(this.queue == null)
{
this.queue = new BufferBlock<RequestPayload>();
this.ConsumeRequestsAsync(queue); //this call cannot be awaited since it would lead to a deadlock
//however, by not awaiting this call all exceptions raised in
//ConsumeRequestsAsync will be lost
}
return await queue.SendAsync(payload)
}
public Task ConsumeRequestsAsync(BufferBlock<RequestPayload> queue)
{
while(await queue.OutputAvailableAsync())
{
try
{
var payload = await queue.ReceiveAsync();
//do the HTTP request...
}
catch (Exception e)
{
ConnectorExceptionOccured(this, e); //fire event to forward the exception to the client
}
}
}
}
public class Client
{
private HttpConnector connector = new HttpConnector();
public Task<bool> UpdateDataAsync()
{
connector += (object sender, Exception e ) //register an event handler to receive exceptions occur
//during the consumption of the requests
{
//handle exception or re-throw
};
connector.ProduceRequest(new RequestPayload()); //produce a request
}
}
Forwarding exceptions via an event has some severe drawbacks:
Natural exception handling is not possible. If developers are aware of this mechanism, they won't catch any exception.
You cannot use AppDomain#UnhandledException for unhandled exceptions during the application runtime. In fact, if you don't have a subscription to the 'Exception'-event, the exception is completely lost.
If you have only one event to subscribe to, your exception object needs a lot of context information in order to figure out which operation caused the exception.
For our problem it turned out that it is better to use TaskCompletionSource, which is a standard technique to synchronize different threads. An instance of TaskCompletionSource class is provided by each RequestPayload object. After the consumption the TaskCompletionSource.Task is completed (either with the result or with an exception). The producer doesn't return the Task for queue.SendAsync(payload) but payload.CompletionSource.Task:
public class RequestPayload
{
public IModelBase Payload { get; set; }
public TaskCompletionSource<IResultBase> CompletionSource { get; private set; }
}
public class HttpConnector
{
private BufferBlock<RequestPayload> queue;
public Task ProduceRequest(RequestPayload payload)
{
if(this.queue == null)
{
this.queue = new BufferBlock<RequestPayload>();
this.ConsumeRequestsAsync(queue);
}
await queue.SendAsync(payload);
return await payload.CompletionSource.Task;
}
public Task ConsumeRequestsAsync(BufferBlock<RequestPayload> queue)
{
while(await queue.OutputAvailableAsync())
{
try
{
var payload = await queue.ReceiveAsync();
//do the HTTP request...
payload.CompletionSource.TrySetResult(null);
}
catch (Exception e)
{
payload.CompletionSource.TrySetException(e)
}
}
}
}
public class Client
{
private HttpConnector connector = new HttpConnector();
public Task UpdateDataAsync()
{
try
{
await connector.ProduceRequest(new RequestPayload());
}
catch(Exception e) { /*handle exception*/ }
}
}
Related
I have a service method that does so many things.
public Result DoSomething(){
var queryResult = service.GetResult();
SaveResultToRedis(queryResult);
logger.Log($"this data saved in redis successfully {queryResult.Id}");
AddSomethingToKafka(queryResult);
logger.Log($"this data saved in kafka successfully {queryResult.Id}");
logger.Log($"this data response is success {queryResult.Id}");
}
In this stuation,
if redis or kafka fails, the request response will fail.
if logger service fails, the request response will fail.
if I put all logics in try catch blocks, code will appear so bad.
Which way may apply in this stuations? Is there any design pattern approaches or else?
If you want to try to make your method thinner, then try to apply SOLID rules.
If DoSomething() method just saves data to some database or event system, then we can separate them by database or event systems. However, code example just saves in two places and it would not be great choice separate by storage.
As an alterantive, it is possible to hide logger.log methods by creating a private helper method and call it from DoSomething():
private void ExecuteAndLog(Action action, logger, string message)
{
action();
logger.log(message);
}
and the full code looks like this:
public void SaveToKafka(string str)
{
}
public void SaveToRedis(string str)
{
}
public void DoSomething()
{
try
{
string s1 = "s1";
ExecuteAndLog(() => SaveToKafka(s1), logger, "someMessage");
ExecuteAndLog(() => SaveToRedis(s1), logger, "someMessage");
logger.log("this data response is success");
}
catch (Exception)
{
throw;
}
}
private void ExecuteAndLog(Action action, logger, string message)
{
action();
logger.log(message);
}
I am using log4net (.Net) to write kafka appender and I am running into an issue where I cannot use await ProduceAsync.
Error
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%# Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it. ,
StackTrace : at
System.Web.AspNetSynchronizationContext.OperationStarted(at System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create()
Code
public class CustomAppender: AppenderSkeleton
{
private IProducer<Null, string> p;
public override void ActivateOptions()
{
// Setup kafka producer
}
protected override void Append(LoggingEvent loggingEvent)
{
// Get JSON from application
// Add additional data to the json
callBroker(json, topic);
}
private async void callBroker(string json, string topic)
{
var result = await p.ProduceAsync(Topic, new Message<Null, string>{Value=json});
}
}
I can return Task in my callBroker method but then there is no async override for Append method.
So my question is, Can I use Producer.Produce instead of ProduceAsync in a high volume environment? this program will be logging >500 messages/sec, is there a preference on which works better? I also need to handle some exceptions and take some action if it fails for specific error codes.
Sync version
protected override void Append(LoggingEvent loggingEvent)
{
CallBroker(topic, json);
}
private void CallBroker(string topic, string message)
{
producer.Produce(topic, new Message<Null, string> { Value = message });
}
Semi-async version
If you can't change the signature of the Append method
then you can call an async method in blocking mode via the following way:
protected override void Append(LoggingEvent loggingEvent)
{
CallBrokerAsync(topic, json).GetAwaiter().GetResult();
}
private async Task CallBrokerAsync(string topic, string message)
{
await producer.ProduceAsync(topic, new Message<Null, string> { Value = message });
}
Async shines when it is used all the way down (from the top most entry-point through all the layers till the lowest component which calls the async I/O operation)
As always measure, measure and measure to understand how does this change affect your application.
It is possible to retry task till condition is not completed ? e.g.
internal class Program
{
private static void Main(string[] args)
{
var user = new User();
var jobs = new Jobs();
Hangfire.BackgroundJob.Enqueue(() => jobs.SendNotification(user));
}
}
public class Jobs
{
Rules rules = new Rules();
public void SendNotification(User user)
{
if (rules.Rule1() && rules.Rule2())
{
// send notification
return;
}
// somehow retry the execution of this method (throw exception does not seem right)
}
}
public class Rules
{
public bool Rule1() { return true; }
public bool Rule2() { return true; }
}
public class User { }
I know that it is possible to retry execution of method by throwing exception but that does not seem right, since I know that recovering from exception is rather costly, and it will mark job as failed in hangfire admin interface which is not true.
I could write retry pattern myself, but I like the way hangfire is saving all the information related to background job processing to the persistent storage (SQL in my case), no data is kept in a process’ memory. So I assume it can recover the queue from storage even after server was shut down and continue processing.
Note: i would like to use hangfire because we already using it for the jobs but if it is not suitable I have free hand. Could you recommend some library that can do what I want and you have good experience with it ?
I've been banging my head against a wall for two days now, and frankly I'm annoyed with myself because I just can't seem to get it.
I'm in a webapi context. During this request I need to send some data to one of our other systems, this system is slow to return, due to heavy calculations and multiple database saves etc etc. I need to log the result of this operation, regardless of whether it is successful or not. But I don't want to wait around for it to finish.
I've read that I should be async await all the way from top to bottom. I would have to convert numerous methods if I decided to do this, as I'm already 3 or 4 methods deep, which I fear would branch out even more.
What are my options here? If I go async await all the way down, what do I do with the methods higher up the stack, like my WebApi controllers?
Here is my code, I've tried to thin it down as much as I can. Right now I'm using Task.Result() in the method PushResult(). Which to my understanding is blocking the async? This code works in that the request gets sent. But the TestLog is always last, not first. Therefore not async.
//I'm in a public service and referenced twice
private void MyEndProcess()
{
// other stuff
_vendorPushService.PushResult(); // This could take a while and I have to wait for it!
_logService.PostLog(LogType.TestLog, "Test");
}
//I'm referenced above and somewhere else in the code base
public void PushResult()
{
ExternalResultModel externalResultModel = _resultService.GetExternalResultModel();
PushedResultModel pushedResult = new PushedResultModel();
try
{
pushedResult = _vendorRequestService.PushResultAsync(externalResultModel).Result;
}
catch (Exception ex)
{
pushedResult.Success = false;
}
if (pushedResult.Success)
{
_logService.PostLog(LogType.SuccessLog, pushedResult.Message);
}
else
{
_logService.PostLog(LogType.FailedLog, pushedResult.Message);
}
}
public async Task<PushedResultModel> PushResultAsync(ExternalResultModel externalResultModel)
{
// setup the requestMessage
HttpResponseMessage responseMessage = await _httpRequestService
.SendRequest(requestMessage)
.ConfigureAwait(false);
return new PushedResultModel
{
Success = responseMessage.IsSuccessStatusCode,
Message = await responseMessage.Content.ReadAsStringAsync()
};
}
public class HttpRequestService : IHttpRequestService
{
private readonly HttpClient _httpClient;
public HttpRequestService(IHttpClientAccessor httpClientAccessor)
{
_httpClient = httpClientAccessor.HttpClient;
}
public async Task<HttpResponseMessage> SendRequest(HttpRequestMessage requestMessage)
{
HttpResponseMessage httpResponseMessage = await _httpClient.SendAsync(requestMessage).ConfigureAwait(false);
return httpResponseMessage;
}
}
You should implement async await all the way from top to bottom.
If I go async await all the way down, what do I do with the methods higher up the stack, like my WebApi controllers?
Just make your controller actions async like this:
[RoutePrefix("api")]
public class PresidentsController : ApiController
{
[Route("presidents")]
public async Task<IHttpActionResult> GetPresidents()
{
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
return Ok();
}
}
It's easiest way to implement async methods. Even if it will add some work to change everything to async it will benefit in future, because You will avoid many problem with async code.
If you absolutly HAVE to use async method in synchronous methods make it block in ONE place, like this:
public void MySyncMethod()
{
try
{
this.MyAsyncMethod().Wait();
}
catch (Exception exception)
{
//omited
}
}
private async Task MyAsyncMethod()
{
await AsyncLogic().ConfigureAwait(false);
}
But i don't recommend it. You should just use async await all the way to controller action.
In your comment you said you want to process a task in the background and not make the client calling your API wait. To do that, you don't really need to use async/await.
Try this:
private void MyEndProcess()
{
// other stuff
Task.Run(_vendorPushService.PushResult()).ConfigureAwait(false); //fire and forget
_logService.PostLog(LogType.TestLog, "Test");
}
The Task.Run will start the task, and the ConfigureAwait(false) tells it that it does not need to resume on the same context that we're currently on (meaning that the context can close before the task is finished - i.e. the response can be sent back without waiting for the task to finish).
You will get a compiler warning that you're not awaiting Task.Run, but that's what you want.
Keep in mind that when you do this, HttpContext.Current will not be available inside PushResult.
I'm trying to wait the end of function to perform some task. HEre is my architecture
A class for the windows service
A class for communication with a device, instanciated as "ilon". This class have access to another class, who permit me to use a webservice
From the windows service, i'm doing it :
Item_DataColl resultSet = ilon.read("Net/LON/10/LampDali1/nviRunHours");
Here is the definition of the read function of the "ilon" class:
internal Item_DataColl read(string UCPTName)
{
return ilonBind.invoke_command_READ("Net/LON/10/LampDali1/nviRunHours").Result;
}
Ilonbind variable is associated to a class who permit me to create a connection with the webservice. So he got a function named "invoke_command_read", defined as :
public async Task<Item_DataColl> invoke_command_READ(string UCPTName)
{
return await Task.Run(() => thread_command_READ_result(UCPTName));
}
On the same class, i finally have this function :
private Item_DataColl thread_command_READ_result(string UCPTName)
{
Item_DataColl resultSet = null;
if (UCPTName != null)
{
try
{
OnProgressBarUpdate(progressBar.UnknownEnd);
resultSet = connector.command_READ(UCPTName);
readOperationDone(resultSet);
OnConsoleWriting(string.Format("[READING] Lecture réussie : {0} = {1}", ((Dp_Data)resultSet.Item[0]).UCPTname, ((Dp_Data)resultSet.Item[0]).UCPTvalue[0].Value), ILonConnectorConsoleResultType.RESULT);
}
catch (Exception e)
{
OnConsoleWriting(e.ToString(), ILonConnectorConsoleResultType.ERROR);
}
finally
{
OnProgressBarUpdate(progressBar.Invisible);
}
}
return resultSet;
}
Instruction "resultSet = connector.command_READ(UCPTName)" work well, and no result will be return until the result of the webservice request. But i am not able to get any result of the webservice.
Are my Task used well ?
Are my Task used well?
No.
Here's what's going on:
The actual operation is a network call, so it's a perfect fit for async.
But the proxy gives you synchronous APIs, so you're blocking a thread. (not good)
So invoke_command_READ wraps the synchronous API in a Task.Run, so it blocks a thread pool thread. (not good)
Then read blocks on the task using Result, blocking two threads per request and causing a deadlock. (really bad)
Your code is sync-over-async-over-sync, which is notable for employing two anti-patterns (sync-over-async and async-over-sync) simultaneously.
To fix this, either go async all the way, or sync all the way. Async all the way is more efficient but requires async APIs on your proxy:
public async Task<Item_DataColl> invoke_command_READ(string UCPTName)
{
Item_DataColl resultSet = null;
if (UCPTName != null)
{
try
{
OnProgressBarUpdate(progressBar.UnknownEnd);
resultSet = await connector.command_READAsync(UCPTName);
readOperationDone(resultSet);
OnConsoleWriting(string.Format("[READING] Lecture réussie : {0} = {1}", ((Dp_Data)resultSet.Item[0]).UCPTname, ((Dp_Data)resultSet.Item[0]).UCPTvalue[0].Value), ILonConnectorConsoleResultType.RESULT);
}
catch (Exception e)
{
OnConsoleWriting(e.ToString(), ILonConnectorConsoleResultType.ERROR);
}
finally
{
OnProgressBarUpdate(progressBar.Invisible);
}
}
return resultSet;
}
internal Task<Item_DataColl> readAsync(string UCPTName)
{
return ilonBind.invoke_command_READ("Net/LON/10/LampDali1/nviRunHours");
}
Sync all the way would probably be easier, since your proxy is sync and your consuming code is sync:
internal Item_DataColl read(string UCPTName)
{
return ilonBind.invoke_command_READ("Net/LON/10/LampDali1/nviRunHours");
}
public Item_DataColl invoke_command_READ(string UCPTName)
{
return thread_command_READ_result(UCPTName);
}