So I have the problem that when many Reactions get added to a Message in discord I get the following error:
A ReactionAdded handler is blocking the gateway task.
As long as only a few reactions are added everything seems fine but when multiple are added at the same time (or in quick succession) I get the error.
It also seems in general that the ReactionHandler is taking time to realise there was a reaction added. Which should not be because the things I do in the handler are slow (because I don't do much in there)
Necessary Code (There's more but for this problem it's unnecessary I think):
class Program
{
//some variables
public static Task Main() => new Program().MainAsync();
public async Task MainAsync()
{
using IHost host = Host.CreateDefaultBuilder()
//some other code
.AddSingleton<ReactionHandler>())
.Build();
await.RunAsync(host);
}
public async Task RunAsync(IHost host)
{
using IServiceScope serviceScope = host.Services.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
//some other code
var reactions = provider.GetRequiredService<ReactionHandler>();
reactions.InitializeAsync();
//some other code
await _client.LoginAsync(TokenType.Bot, "Token");
await _client.StartAsync();
await Task.Delay(-1);
}
}
public class ReactionHandler
{
private readonly DiscordSocketClient _client;
public ReactionHandler(DiscordSocketClient client)
{
_client = client;
}
public async Task InitializeAsync()
{
_client.ReactionAdded += HandleReationAsync;
_client.ReactionRemoved += HandleReactionAsync;
}
private async Task HandleReactionAsync(Cacheable<IUserMessage, ulong> message, Cacheable<IMessageChannel, ulong> channel, SocketReaction reaction)
{
if (reaction.User.Value.IsBot) return;
Console.WriteLine("Reaction changed");
//some other code
}
}
So if there's some information missing (cause I left out some code) just tell me then I add it. Now in the interent I've read simular things happen with the MessageReceived Handler, but their code was so different from mine that I just didn't understand it. Also I've read that this can happen when the code / things I do are slow, but the code above is not that much that it is too slow right.
I hope everything's clear and thanks in advance :)
If I understand the problem correctly, seems like you're eating up all the threads in your app to handle reactions. It might be better to make the ReactionHandler a hosted service then queue reactions to that service so you're not waiting on those threads. This will essentially make ReactionHandler run in the background and process as they come.
Background tasks with hosted services in ASP.NET Core
Just do keep in mind that this will be on a completely separate thread so you need to make adjustments to make it a multithreaded app.
You would convert your handler to a hosted service, use some message queueing solution or roll your own to make requests to the service. Then the client (outside of the hosted service) would then add to the queue for when reactions are received.
Related
I´m currently working on a C# core 5.0 REST API with a RabbitMQ messagebus. I´ve created a class that derives from BackgroundService and the consumerEmployeeIncomming.Received event is triggered by messages on the messagebus. But how do I expose the received data to the rest of my application ?
The class is added thru StartUp.cs via: services.AddHostedService<MessageBusSubscriber>();
public class MessageBusSubscriber : BackgroundService
{
private IConfiguration _configuration;
public MessageBusSubscriber(IConfiguration configuration)
{
_configuration = configuration;
RabbitMqUtil.Initialize(_configuration);
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
stoppingToken.ThrowIfCancellationRequested();
if (RabbitMqUtil.IsInitialized)
{
var consumerEmployeeIncomming = new EventingBasicConsumer(RabbitMqUtil.EmployeeChannelIncomming);
consumerEmployeeIncomming.Received += (ModuleHandle, ea) =>
{
Logger.DoLog("CalendarService - MessageBusSubscribe/Employee - event received:");
var body = ea.Body;
var data = Encoding.UTF8.GetString(body.ToArray());
Logger.DoLog(data);
List<EmployeePublishDto> list = JsonSerializer.Deserialize<List<EmployeePublishDto>>(data);
Logger.DoLog("####################################");
Logger.DoLog("Nr of Recieved objects:" + list.Count);
foreach (EmployeePublishDto dto in list)
{
Logger.DoLog(dto.FirstName);
Logger.DoLog(dto.LastName);
Logger.DoLog(dto.Email);
}
};
RabbitMqUtil.EmployeeChannelIncomming.BasicConsume(queue: RabbitMqUtil.EmployeeQueueNameIncomming, autoAck: true, consumer: consumerEmployeeIncomming);
If it isn´t possible how do I proceed with using data from RabbitMQ in my application?
Suggestions are greatly appreciated
Regards
Kaare
I read through your comment and your code and your comments and think I can shed some light.
I'm still not 100% sure what you're trying to achieve, but I think you're trying to notify the client of changes once the changes have been completed in the queue. The challenge here is the background worker is essentially running in a separate thread. You can't simply "return" the list of employees from a separate thread.
There's a few ways to solve this but I will give you the top two in my opinion.
Use webhooks to notify the client of changes, you can use SignalR to accomplish this. It's pretty easy to set up.
The less pretty way, is you can simply move the queue to a "completed" status, stores the EmployeeList as json to the table, and have a separate endpoint on the client that polls the queue with a check to see if the status is complete, and if so, get the EmployeeList.
In my application I have a server which provides a REST-Api where my UI can communicate with.
Now I have to start a long running Process on the Server but I want the client not to wait for the response of the server.
I know I could just fire and forget the Post-Call and not await the response but I need to know at the client that the Process on the server was startet correctly.
So I thought about the following:
[HttpPost]
[Route("startscan")]
public HttpResponseMessage StartScan()
{
Task.Factory.StartNew( () =>
{
//Do long running things here
});
return Request.CreateResponse(HttpStatusCode.OK);
}
So my question now is: Will the task I started be executed to it's end? Background of the question is, that as far as I knwo a controller-instance is created for each call to it. So will the instance of the controller be terminated when the requested finished or will the Task I started run to it's end. The task can take up to 10 minutes or longer sometimes.
A simple approach would be to just use an asynchronous method without awaiting the Task result like so:
[HttpPost]
[Route("startscan")]
public async HttpResponseMessage StartScan()
{
DoLongRunningThings();
return Request.CreateResponse(HttpStatusCode.OK);
}
public async Task DoLongRunningThings()
{
// Do Stuff here
}
However, if you Processing is more complex and requires more resilience you should look into how to use background jobs. Here is a good collection of what you can use: https://stackoverflow.com/a/36098218/16154479
Will the task I started be executed to it's end?
Probably yes, but also possibly no. Sometimes it won't be executed completely. This is the result of misusing an ASP.NET app (which handles HTTP requests) as a background service (which runs outside of a request context).
There is a best practice that avoids possibly-partial execution: a distributed architecture (as I describe on my blog). The idea is that your controller enqueues a message describing the work to be done into a durable queue and then returns. Then there's a separate background service that reads from the queue and does the actual work.
I know that the new ASPNET Core 3.0 stack has a number of improvements around hosting processes.
I am curious about the best way to be able to define and execute a background process from a Razor PageModel? Meaning I have some logic that needs to start something in the background and then that Razor page doesn't need to monitor it's outcome, but I would like to be able to observe it too if that's not too hard.
Can someone show me a code sample or point me in the right direction?
Since this is probably a follow-up from your previous question about IHostedService, I am going to assume that you want to have some background service (as a hosted service) within your ASP.NET Core application that is able to perform background tasks. And now you want to trigger such a task through a controller or Razor page action and have it executed in the background?
A common pattern for this is to have some central storage that keeps track of the tasks which both the background service and the web application can access. A simple way to do this is to make it a (thread-safe) singleton service that both sides can access.
The docs actually show a simple example using a BackgroundTaskQueue which is exactly that shared service/state. If you have a worker for a specific kind of job though, you could also implement it like this:
public class JobQueue<T>
{
private readonly ConcurrentQueue<T> _jobs = new ConcurrentQueue<T>();
private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);
public void Enqueue(T job)
{
_jobs.Enqueue(job);
_signal.Release();
}
public async Task<T> DequeueAsync(CancellationToken cancellationToken = default)
{
await _signal.WaitAsync(cancellationToken);
_jobs.TryDequeue(out var job);
return job;
}
}
You can then register an implementation of this with the service collection along with a hosted background service that works on this queue:
services.AddSingleton<JobQueue<MyJob>>();
services.AddHostedService<MyJobBackgroundService>();
The implementation of that hosted service could then look like this:
public class MyJobBackgroundService : BackgroundService
{
private readonly ILogger<MyJobBackgroundService> _logger;
private readonly JobQueue<MyJob> _queue;
public MyJobBackgroundService(ILogger<MyJobBackgroundService> logger, JobQueue<MyJob> queue)
{
_logger = logger;
_queue = queue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var job = await _queue.DequeueAsync(stoppingToken);
// do stuff
_logger.LogInformation("Working on job {JobId}", job.Id);
await Task.Delay(2000);
}
}
}
In a controller action or a Razor page model, you then just need to inject the JobQueue<MyJob> and then call Enqueue on it to add a job to the list. Once the background service is ready to process it, it will then work on it.
Finally note that the queue is obviously in-memory, so if your application shuts down, the list of yet-to-do jobs is also gone. If you need, you could also persist this information within a database of course and set up the queue from the database.
I am having some trouble with while loops and events using background tasks in ASP.NET Core hosted services. I am trying to get my Progress_Changed event to fire from within the while (!cancellationToken.IsCancellationRequested) part so I can capture data continuously while the app is running. I have been following these guides:
Background tasks with hosted services in ASP.NET Core
Implement background tasks in microservices with IHostedService and the BackgroundService class
How can I capture events while keeping the BackgroundService in a continuous loop so I am always able to get data? I am hoping to use this with SignalR so I can feed out data to a hub.
public class MyBackgroundService : BackgroundService
{
private readonly ICustomService _service;
private IProgress<JSONMessage> _progress;
public MyBackgroundService(ICustomService service)
{
_service = service;
_progress = new Progress<JSONMessage>();
_progress.ProgressChagned += Progress_Changed;
{
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
await _service.StartService(_progress);
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
}
private void Progress_Changed(object s, JSONMessage e)
{
Console.WriteLine(e.Id);
}
}
Having long running tasks within IIS can be tricky for a variety of reasons. It's probably easier to delegate this kind of stuff to a library that just works like Hangfire. https://www.hangfire.io/.
I could be wrong but this seems like a possible deadlock to me. By default await captures the current SynchronizationContext and then posts back to it upon completion. This gets pretty dicey in IIS/ASP.NET for many reasons I won't get into. Try this code instead:
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
await _service.StartService(_progress).ConfigureAwait(false);
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
}
}
The only change made here is disabling the postback to the captured context I spoke about. Please be aware this means you're potentially on a different thread after the call to await. Because of this you could consider this a hack, but I see nothing in the method that requires a postback.
If that doesn't fix it I would attach a debugger to the release build. Or use a profiler to see what exactly is happening.
I am writing a WCF webservice that includes a method that accepts an array of objects and inserts them into the database. This could take a long time, so I can't just expect the client to wait.
My colleague thinks that I don't need to do anything, that it's the client's job to call my service asynchronously. I just write a normal method. This doesn't sound right to me, although I hope it's true because looking at WCF async tutorials and SO questions has just confused me so far.
Is he correct? If not, how do I actually write the method in a way that would allow the client to call the method asynchronously or otherwise avoid hanging?
If he is correct (as appears to be the case), then what is the point of defining an asynchronous method ([OperationContract (AsyncPattern=true)], Begin, End, etc.). Is it a way explicitly handling asynchronous calls, or allowing interactivity, or what?
It should fall on the client's side. They are the ones that have to prevent their app/UI from hanging.
Have your client call your method asynchronously. If they are using a service reference, all methods/events are generated automatically.
myWcfClient.myMethodCompleted
+= new EventHandler<myMethodCompletedEventArgs>(myCallBack);
myWcfClient.myMethodAsync(args);
public void myCallback(object sender, myMethodCompletedEventArgs e)
{
var myResult = e.Result;
}
If your client doesn't care what happens with the service call, you want a simple fire and forget operation and you can do this.
The AsyncPattern property tells the runtime that your operations implement the .NET Framework asynchronous method design pattern. See here. If you want your client application to know what has happened with your service call then you can use this pattern. There are other ways to get the results though.
This is only on the client side, I've skipped the old event driven async bleh pattern and replaced it with the async-await pattern. Not waiting for webmethod calls async, and blocking the UI... doesn't even belong in this century ;)
If you are using .net 4.5+ you are getting the async-await pattern for free (Unless wp8, where you still have to wrap it). The async methods should already be avaliable through the service. I recommend the AsyncBridge if you are using old frameworks, which allows you to use the async-await pattern for cases like this. The alternative is to stick to the old event driven async nightmare. The examples below is only possible if you are using C#5.0 or never.
Ensure to start in a new thread from a non async method.
Task.Factory.StartNew(client.DoSomethingAsync("blabla") ).ContinueWith(...);
The last part is run after your method has completed, check for exceptions to completion code etc.
Or in some async method
public async Task<string> DoSomethingAsync(String text) {
// Exception handling etc
return await client.DoSomethingAsync(text);
}
wrapping APM to async-await pattern:
public class ServiceWrapper : IServiceWrapper
{
readonly YourServiceClient client;
public ServiceWrapper(YourServiceClient client)
{
this.client = client;
}
public async Task<string> DoSomethingAsync(string someParameter)
{
return await Task<string>.Factory.FromAsync(client.BeginDoSomeStuff, client.EndDoSomeStuff, someParameter, new object());
}
}
EDIT
Opening and closing connections in a wrapped service. (I don't have my devbox avaliable right now but this should work).
public class ServiceWrapper : IServiceWrapper
{
EndpointAddress address;
public ServiceWrapper(EndpointAddress clientAddress)
{
address = clientAddress;
}
public async Task<string> DoSomethingAsync(string someParameter)
{
// handle exceptions etc here, can be done some cleaner..
var client = new YourServiceClient();
client.Endpoint.Address = address.Address; // can skip this..
await client.OpenAsync()
var res = await Task<string>.Factory.FromAsync(client.BeginDoSomeStuff, client.EndDoSomeStuff, someParameter, new object());
await client.CloseAsync();
return res;
}
}
One last thing I'm not sure how you generate your proxy, if you are using vs make sure to hook of the checkbox allowing async methods when configuring the service. If you are using svcutil add the appropriate flags.
Async-await pattern
Old event driven async pattern
Hope it helps,
Cheers,
Stian