timer lifetime for a IIS web app - c#

I need to add a retry logic to a web request if a write to db failed. I'd like to try two more times with a delay of 500ms before quitting. There is no requirement to block the user response during retry. My question is, once a response is made, is the timer still alive to complete the retries or is it killed immediately after the response?

Why not make use of async/await. It will probably make things a little easier:
public SomeResult HandleWebRequest()
{
StartThing(); //not awaited - fire-and-forget
//return a response
}
public async void StartThing()
{
//await Task.Yield(); //return control to call site and finish asynchronously
for(var numTries = 0; numTries < 3; numTries++)
{
if(trySomethingThatMightNotWork)break;
await Task.Delay(5000);
}
}

Related

WebAPI HTTP request not completing until queued work kicks off on background task

In my .Net 6 WebPI service, I am queueing work to a background task queue, very closely based on the example here, but I could post parts of my implementation if that would help:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio#queued-background-tasks
I am running into unexpected behavior where control is not returned to the caller, even after the return Ok(..) completes in the controller. Instead the request only completes after the await Task.Delay(1000); line is reached on the queued work item. The request returns to the client as soon as this line is reached, and does not need to wait for the Delay to finish.
I'm guessing this is because of the await either starting a new async context, or someone un-sticking the async context of the original request. My intention is for the request to complete immediately after queuing the work item.
Any insight into what is happening here would be greatly appreciated.
Controller:
public async Task<ActionResult> Test()
{
var resultMessage = await _testQueue.DoIt();
return Ok(new { Message = resultMessage });
}
Queueing Work:
public TestAdapter(ITaskQueue taskQueue)
{
_taskQueue = taskQueue;
}
public async Task<string> DoIt()
{
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) =>
{
await Task.Delay(1000);
var y = 12;
});
return "cool";
}
IoC:
services.AddHostedService<QueueHostedService>();
services.AddSingleton<ITTaskQueue>(ctx =>
{
return new TaskQueue(MaxQueueCount);
});
TaskQueue:
private readonly Channel<BackgroundTaskContext> _queue;
public TaskQueue(int capacity)
{
var options = new BoundedChannelOptions(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<BackgroundTaskContext>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
await _queue.Writer.WriteAsync(new BackgroundTaskContext(workItem, ...));
}
Not sure what you expect here. I'm assuming you want the async method to return the cool in the api response. That's fine but because your also awaiting the async call with in DoIt(), then it pauses until QueueBackgroundWorkItemAsync finishes. You could remove the await and it will run and return as you expect.
I can't say that I'm a big fan of that design as you lose contact with it with exception of the cancellation token. Another thought would be to Send that work off to a console job or function app using message bus or even another http call.
Additional Notes:
Async can be complicated to explain because in reality, it wraps up code and executes on a thread of it's choosing. The await simulates the synchronous behavior.
await Task.Delay(1000); // Runs on it's own thread but still halts code execution for 1 second.
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) // Waits for control to be returned from the code inside.
var resultMessage = await _testQueue.DoIt(); // Always waits for the code inside to complete.
If your wanting something to run without pausing code execution, you can either remove the await or add a Task.Run(() => { }); pattern. Is it a good idea is a whole other question. It also matters whether you need information back from the async method. If you don't await it then you'll get null back as it doesn't wait around for the answer to be computed.
This appears just to have been user error using the debugger. The debugger is switching to the background task thread and hitting breakpoints there before the response fully returns giving the appearance that control was not being returned to the client and being carried into the background task.
Even after adding some synchronous steps in QueueBackgroundWorkItemAsync and putting breakpoints on them, control was not immediately returned to the original http call. Only after I tried adding a long running task like await Task.Delay(1000); did enough time/ticks pass for the http response to return. I had conflated this with just the await somehow freeing up the original http context.

Proper way to implement my own async Worker Service(s) according to TAP pattern

I'm newbie in async. And making WPF app for scraping and API calls purposes. WPF's UI is needed only for service monitoring and settings control, so all services will run simultaneously in the background. And most of them is doing similar work.
For this one I need to implement strategy like this:
Start worker on threadpool
Worker must send request and process response from Website
2.1 If response processed and some new data appeared - raise an event
2.2 If request failed - handle an error
2.3 If there are many error percent for last x requests - stop worker
No matter the last request completed/failed/running we must send another request
Another request should be sent not earlier than the set delay but should not exceed the delay too much (as far as possible).
private _workTask;
private List<ScrapeParameters> _scrapeParams = new();
public event EventHandler<ScrapedEventArgs>? NewDataScraped;
//Can I run Worker like this?
public void RunWorker()
{
if(_workTask.IsCompleted)
_workTask = WorkAsync(_token)
}
private async Task WorkAsync(CancellationToken cancelToken)
{
List<Task> processTasks = new();
while(true)
{
if(cancelToken.IsCancellationRequested) return;
//Delay could be from 0.5 second to any value
var delayTask = Task.Delay(WorkerDelay);
var completedTasks = processTasks.Where(t => t.IsCompleted)
var setToHandle = new HashSet<Task>(completedTasks);
foreach(var task in setToHandle)
{
//Theoretical logic to handle errors and completion
if(task.IsFaulted)
HandleFaultedTask(task);
else
CountCompleted();
processTasks.Remove(task);
}
//Theoretical logic to obtain the desired parameters.
var currParameters = GetParameters();
processTasks.Add(ProcessAsync(currParameters, cancelToken));
await delayTask;
}
}
//This method usually takes around 2-4 seconds
private async Task ProcessAsync(ScrapeParameters parameters CancellationToken cancelToken)
{
//Some work with http requests
var response = await Client.GetAsync(parameters.ToUri());
...
//Processing response
...
if(newData != null)
NewDataScraped?.Invoke(new(newData));
}
Does my implementation matches the TAP pattern?
Especially I would like to focus on RunWorker() and setToHandle

.NET MVC, Task never goes to status Done

I try to implement long running operation handling on server without push notification.
My project methods all are Async. All methods out of the web project await with ConfigureAwait(false)
I have library referenced in my web that manages long running operation.
On my initial request I have fire-and-forget for what I think can continue longer:
// my fire and forget - the task is not awaited
longRunningOperation.RunAsync();
// add my delay
var result = await Task.WhenAny(longRunningOperation.Task, Task.Delay(LongRunningConfiguration.Instance.InitialRequestReleaseTime)).ConfigureAwait(false);
// if the task finishes the return on time, otherwise create long running handler
if (result == longRunningOperation.Task)
{
// it is OK
}
else
{
Task task = Task.Run(async () =>
{
await longRunningOperation.Task;
});
monitorTask = new ActiveMonitorTask(longRunningOperation, task)
{
Id = Guid.NewGuid()
};
_monitorStateSession.Add(monitorTask);
}
At the moment I have only one of my operations implemented to support long-running.
Myst of the time tasks for that operation goes to Done status. But from time to time they hang-out in WaitForActivation
Any suggestions how to track the problem or check what can causes it?
Regards,
Boris

Sending many parrallel WebRequests

I'm trying to simulate many concurrent users (>2000) to test a web service. Every user performs actions at a specific pre-defined time, for example:
User A: 09:10:02, 09:10:03, 09:10:08
User B: 09:10:03, 09:10:05, 09:10:07
User C: 09:10:03, 09:10:09, 09:10:15, 09:10:20
I now want to send web request in real time at each of those times. I can tolerate a delay of at most ~2 seconds. What I was already trying without success:
a) Aggregate all times in a single list, sort it by time then iterate it:
foreach (DateTime sendTime in times) {
while (DateTime.now < sendTime)
Thread.Sleep(1);
SendRequest();
}
b) Create a thread for each user, with each thread checking for the same condition as above but a longer sleep time
Both approaches kind of work, but the delay between the time that the request was supposed to be sent and the time that it has actually been sent is way too high. Is there any way to send the requests with higher precision?
Edit: The suggests approaches work really well. However, the delay is still extremely high for many requests. Apparantly, the reason for this is my SendRequest() method:
private static async Task SendRequest()
{
// Log time difference
string url = "http://www.request.url/newaction";
WebRequest webRequest = WebRequest.Create(url);
try
{
WebResponse webResponse = await webRequest.GetResponseAsync();
}
catch (Exception e) { }
}
Note that my web service does not return any response, maybe this is the reason for the slow down? Can I send the request without waiting for the response?
Why are you doing this with multiple thread? Threading requires slow sleep/wake context switching. You could just do this all with timers/async calls.
List<DateTime> scheduledTimes = ...;
List<Task> requests = scheduledTimes
.Select(t => t - DateTime.Now)
.Select(async delay =>
{
await Task.Delay(delay);
SendRequest();
})
.ToList();
await Task.WhenAll(requests);
The above code will in one thread schedule all the requests onto the SynchronizationContext and run them.
Simples.
I would suggest to use a timer object to trigger the requests:
// In Form_Load or another init method
Timer tRequest = new Timer();
tRequest.Interval = 500;
tRequest.Tick += TRequest_Tick;
private void TRequest_Tick(object sender, EventArgs e)
{
var sendTimes = times.Where(t => t.AddMilliseconds(-500) < DateTime.Now && t.AddMilliseconds(500) > DateTime.Now);
foreach(DateTime sendTime in sendTimes)
{
SendRequest();
}
}

How to cancel an asynchronous Task?

I have a method that loads data, eg.:
public async GetCustomers()
{
await Task.Run(() => {
for (int i = 0; i < 99; i++)
customers = customerRequest.GetCustomers();
});
}
customerRequest is a simple class that uses HttpClient to connect to a WebApi server. The call stack is as follows:
method->request->controller action->server layer->repository->db
Currently, the controller action returns IHttpActionResult, whilst the service and repository layers return IEnumerable<Customer>. All methods are called synchronously.
For testing purposes, I added the for loop to increase the task's delay and see the SQL statements being executed.
If the user decides to close the form, the task is still being executed in the background and the SQL statements are still sent to the db.
What is the correct way to cancel such a task?
You'd want to use CancellationTokenSource, and pass its CancellationToken into your method. When the form closes, call CancellationTokenSource.Cancel.
Note, however, that you want to pass the CancellationToken into the methods that actually use it. As I describe on my blog, Task.Run is not one of them.
What you really want to do is start at the lowest level (on the client side), and pass the CancellationToken into whatever HttpClient method that you're using (i.e., like this one).
Then work your way up. I'd also recommend making your code asynchronous. When you're done, you should end up with a GetCustomers that looks like this:
public async Task GetCustomersAsync(CancellationToken token)
{
for (int i = 0; i < 99; i++)
customers = await customerRequest.GetCustomersAsync(token);
}
If you want to Really Be Sure (tm) that no spurious requests go out, you can also explicitly check the token before doing the request:
public async Task GetCustomersAsync(CancellationToken token)
{
for (int i = 0; i < 99; i++)
{
token.ThrowIfCancellationRequested();
customers = await customerRequest.GetCustomersAsync(token);
}
}
You can also handle cancellation on the server side if that's important to you.
Checkout CancellationTokenSource. You can keep one of these long lived and a button or a close event call Cancel() on it. It just needs to be passed to all your tasks if you want them all to cancel. The task will throw an exception so make sure you wrap in a try catch. You can also check if a cancel request has been made within your task to try to break out in a graceful manor.
var cts = new CancellationTokenSource();
await Task.Run(() =>
{
//do work, will throw if cts.Cancel() is called
}, cts.Token);
//wait 2 seconds then cancel
await Task.Delay(2000);
cts.Cancel();

Categories