I am trying to insert a record in azure table on some specific condition. What I found that the records are not getting saved all the time. Few times it works and few times its not. Here is the code which I have written to execute the insert. In addition to that, the code is written to insert a single record for each call and its never going to save multiple records at a time. Also I don't want to wait for the insert operation nor I want to do it synchronously. I want to do it asynchronously and that is without wait.
why the code sometimes fails to insert the records?
Here is my code snippet
try
{
AuditUtils utils = new AuditUtils((int)this.User.Id);
String tableName = "ServiceHeaderInfo", partitionKey = this.Header.PrimaryAccount;
TableAuditEntity tableAuditEntity = utils.GetAuditEntity(tableName, partitionKey, Guid.NewGuid().ToString(), TableAuditTypes.Insert, refId, refType);
tableAuditEntity["DeviceIdentifier"] = this.Header.DeviceId;
tableAuditEntity["DeviceModel"] = this.Header.DeviceModel;
tableAuditEntity["OSVersion"] = this.Header.OSVersion;
utils.AddAsync(tableAuditEntity).ContinueWith(t =>
{
var ex = t.Exception?.GetBaseException();
if (ex != null)
{
this.HandleError(ex);
}
}, System.Threading.Tasks.TaskContinuationOptions.OnlyOnFaulted);
}
catch (Exception ex)
{
this.HandleError(ex);
}
public async Task<TableResult> AddAsync(string tableName, SampleTableEntity entity, CancellationToken cancellationToken = default(CancellationToken))
{
var table = await GetTableAsync(tableName);
return await table.ExecuteAsync(TableOperation.InsertOrReplace(entity), cancellationToken);
}
It is not possible Async without Wait.
You need to use
ExecuteAsync(TableOperation)`
or
ExecuteAsync(TableOperation, TableRequestOptions, OperationContext) methods is used to achieve the requirement.
Instead of above methods you have
ExecuteAsync(TableOperation, CancellationToken)
&
ExecuteAsync(TableOperation, TableRequestOptions, OperationContext, CancellationToken)
When you use CancellationToken it must have a TaskFactory.ContinueWhenAll method.
In a cancellation of the operation, it instantiates a CancellationTokenSource object that generates a cancellation token that's passed to a TaskFactory object.
In turn, the TaskFactory object passes the cancellation token to each of the tasks responsible for collecting readings for a particular instrument.
The TaskFactory.ContinueWhenAll<TAntecedentResult, TResult>(Task<TAntecedentResult>[], Func<Task<TAntecedentResult>[],TResult>, CancellationToken) method is called to ensure that computed only after all readings have been gathered successfully.
If a task has not completed because it was cancelled, the TaskFactory.ContinueWhenAll method throws an exception.
Please refer: click here
Related
I'm trying to cancel a cosmos query using the cancellation token using the NuGet package Microsoft.Azure.Cosmos (3.13.0).
Can anyone help explain why it takes so long to cancel?
This test shows that it takes 2000+ Milliseconds to cancel. I was expecting it to fail milliseconds after I cancelled it.
00158: Reading next
00160: Read next
00188: Cancelling
02492: The operation was canceled.
public class CosmosCancelationTests
{
private readonly ITestOutputHelper testOutputHelper;
public CosmosCancelationTests(ITestOutputHelper testOutputHelper)
{
this.testOutputHelper = testOutputHelper;
}
[Fact]
public async Task TestCancelationTime()
{
Stopwatch stopwatch = Stopwatch.StartNew();
try
{
var client = new CosmosClient(
"https://localhost:8081/",
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
);
var database = client.GetDatabase("CosmosDBPackage"); // Make sure this database is created in the cosmos instance
var container = database.GetContainer("SampleEntity"); // Make sure this container is created in the database
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
using var feedIterator = container.GetItemLinqQueryable<SampleEntity>()
.Where(x => false)
.ToFeedIterator();
if (feedIterator.HasMoreResults)
{
testOutputHelper.WriteLine($"{stopwatch.ElapsedMilliseconds:D5}: Reading next");
var task = feedIterator.ReadNextAsync(cancellationToken);
testOutputHelper.WriteLine($"{stopwatch.ElapsedMilliseconds:D5}: Read next");
await Task.Delay(20);
cancellationTokenSource.Cancel();
testOutputHelper.WriteLine($"{stopwatch.ElapsedMilliseconds:D5}: Cancelling");
await task;
}
}
catch (CosmosOperationCanceledException e)
{
testOutputHelper.WriteLine($"{stopwatch.ElapsedMilliseconds:D5}: {e.Message}");
}
}
}
CancellationTokens in .NET is a mechanism that by definition, won't cause any library to stop what it's doing immediately, it is a cooperative cancellation. The library will cancel the operation when it's safe and won't cause a corrupt or invalid state. For example, HttpClient, when you call SendAsync and the token cancels, if the client already started buffering the response, it won't cancel, it will wait until it completes.
In the case of the query, it will check the token, when it is safe and won't cause an invalid state, maybe the cancellation is happening while requests are on the wire, or responses are being aggregated.
Reference: https://learn.microsoft.com/en-us/dotnet/standard/threading/cancellation-in-managed-threads
I have a .NET Core worker service that fetches a list of procedures from DB that needs to execute on a regular time interval.
For every procedure Task is created with a delay after procedure executed. Different procedures have different delays.
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
var proceduresToExec = await _sqlRepository.GetProceduresToExecute(cancellationToken);
var workers = new List<Task>();
foreach (var proc in proceduresToExec)
{
workers.Add(ExecuteProcedure(proc, cancellationToken));
}
// IMPORTANT to wait for all tasks
await Task.WhenAll(workers);
}
Method that executes procedure is:
private async Task ExecuteProcedure(ProcToExec proc, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
// This line throws multiple exceptions per 1ms when SQL is unavailable
await _sqlRepository.ExecuteStoredProcedureAsync(proc, cancellationToken);
await Task.Delay(proc.Delay, cancellationToken);
}
catch (Exception ex)
{
// exceptions are logged here
_logger.LogError(ex);
}
}
}
SQL access is:
public async Task ExecuteStoredProcedureAsync(ProcToExec proc, CancellationToken cancellationToken)
{
var connStr = _configuration["DB_Conn_string"];
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand($"dbo.{proc.ProcedureName}", conn))
{
await conn.OpenAsync(cancellationToken);
cmd.CommandType = CommandType.StoredProcedure;
await cmd.ExecuteNonQueryAsync(cancellationToken);
}
return;
}
Problem begins when SQL instance becomes unavailable.
Expected behaviour: Program tries to execute stored procedure, instance is unavailable. When timeout expires, throw error and then wait for delay (2000ms). After delay is done, retry.
Reality: When SQL instance becomes unavailable, logger logs multiple messages per 1ms. Average is ~8k logged errors per 1s.
Can anyone point me in direction what am I doing wrong?
See code comments:
private async Task ExecuteProcedure(ProcToExec proc, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
// This line throws multiple exceptions per 1ms when SQL is unavailable
await _sqlRepository.ExecuteStoredProcedureAsync(proc, cancellationToken);
// If above line throws, this following line
// WILL NOT BE EXECUTED!
// Control flow will jump into the `catch` and go
// immediately into the next iteration.
await Task.Delay(proc.Delay, cancellationToken);
}
catch (Exception ex)
{
// exceptions are logged here
_logger.LogError(ex);
}
}
}
Also mind that this will only ever leave the loop IF cancellation is requested.
If you want to return on successful execution, you have to actually make it do that.
According to your requirement "Expected behaviour: Program tries to execute stored procedure, instance is unavailable. When timeout expires, throw error and then wait for delay (2000ms). After delay is done, retry." - I'd have expected to see something like this:
private async Task ExecuteProcedure(ProcToExec proc, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
await _sqlRepository.ExecuteStoredProcedureAsync(proc, cancellationToken);
return;
}
catch (Exception ex)
{
// exceptions are logged here
_logger.LogError(ex);
await Task.Delay(proc.Delay, cancellationToken);
}
}
}
Maybe also consider Polly to introduce error handling policies (not affiliated to the project).
Another after-thought:
Even in the last snippet, that should somewhat do what's expected, you do not have a means to report to the caller if the procedure has been executed successfully or if the operation has been cancelled. Maybe think about whether you need that.
I've the following chunk of code :
using (var cancelSource = new CancellationTokenSource())
{
Task[] tasks = null;
var cancelToken = cancelSource.Token;
tasks = new[]
{
Task.Run(async () => await ThrowAfterAsync("C", cancelToken, 1000)) //<---
};
await Task.Delay(howLongSecs * 1000); // <---
cancelSource.Cancel();
await Task.WhenAll(tasks);
}
Where ThrowAfterAsync have this :
private async Task ThrowAfterAsync(string taskId, CancellationToken cancelToken, int afterMs)
{
await Task.Delay(afterMs, cancelToken);
var msg = $"{taskId} throwing after {afterMs}ms";
Console.WriteLine(msg);
throw new ApplicationException(msg);
}
Resharper is suggesting me that I can use the overload of Task.Run() with cancellation token like this :
Task.Run(async () => await ThrowAfterAsync("C", cancelToken, 1000), cancelToken)
But why ? What is the benefit of doing this over the first version, without the cancellation token as parameter ?
In this specific case, there is no point. In general, you'd want to do as it suggests because, by passing the token to Task.Run it can avoid even scheduling the operation in the first place if the token is cancelled before the operation even has a chance to start, but in your case you're creating the token and you know it won't be cancelled when you start the operation.
But the reason you don't need to pass the token to Task.Run is because the code starting that task is the operation responsible for cancelling the token, and so it knows that the token isn't cancelled yet. Normally you'd be accepting a token from elsewhere, and you wouldn't know if/when it was cancelled.
All that said, there's no reason to even use Task.Run at all. You can just write:
tasks = new[] { ThrowAfterAsync("C", cancelToken, 1000) };
It will have the same behavior but without needlessly starting a new thread just to start the asynchronous operation.
Next, your code will never return in less than howLongSecs seconds, even if the operation finishes before then, because of how you've structured your code. You should simply provide the timeout to the cancellation token source and let it take care of canceling the token at the right time, it won't delay the rest of your method if the operation finishes before the cancellation should happen, so your whole method can just be written as:
using (var cancelSource = new CancellationTokenSource(Timespan.FromSeconds(howLongSecs)))
{
await ThrowAfterAsync("C", cancelToken, 1000)
}
Resharper sees that you are using a method (Task.Run) which has overload which accepts CancellationToken, you have instance of CancellationToken in scope, but you do not use that overload which accepts a token. It does not perform any extensive analysys of your code - it's as simple as that. You can easily verify this with this code:
class Program {
static void Main() {
CancellationToken ct;
Test("msg"); // will suggest to pass CancellationToken here
}
private static void Test(string msg) {
}
private static void Test(string msg, CancellationToken ct) {
}
}
Yes the code itself is strange and you don't need to wrap your async in Task.Run at all, but I won't touch that since you asked just why Resharper suggests that.
If I make a CancellationTokenSource and pass it down to a place where I perform a query like this:
await command.Connection.OpenAsync();
dataReader = await command.ExecuteReaderAsync(_cancellationToken);
If immediately below this I add the following:
_cancellationToken.ThrowIfCancellationRequested();
resultTable.Load(dataReader);
If on a different thread _cancellationTokenSource.Cancel() is called, will the query be cancelled appropriately?
Was able to find a decent solution to the problem. Given the cancellation token and a SQLCommand, wrapping the loading to a data table as follows successfully cancels the db query:
using (CancellationTokenRegistration ctr = cancellationToken.Register(() => cmd.Cancel()))
{
using (var reader = cmd.ExecuteReaderAsync(cancellationToken))
{
dataTable.Load(reader.Result);
}
}
So when the cancellationToken is cancelled in a different thread, the cmd.Cancel is called automatically. This will throw a SqlException that I had to handle and perform my cleanup.
I have the function that I call asynchronously using the await keyword:
public Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName)
{
var tcs = new TaskCompletionSource<StateInfo>();
try
{
var propInstance = BuildCacheKey(entity, propName);
StateCacheItem cacheItem;
if (_stateCache.TryGetValue(propInstance, out cacheItem))
{
tcs.SetResult( new StateInfo (cacheItem.State.Name, cacheItem.State.Value) );
return tcs.Task;
}
//state not found in local cache so save the tcs for later and request the state
var cacheKey = BuildCacheKey(entity, propName);
_stateRequestItemList.TryAdd(cacheKey, new StateRequestItem(entity, propName, tcs));
_evtClient.SubmitStateRequest(entity, propName);
return tcs.Task;
}
catch (Exception ex)
{
tcs.SetException(ex);
return tcs.Task;
}
}
The function has a look to see if it has the information it needs and if it does it returns it. If it doesn’t have the details it sends a request out which should eventually come in as an event. At that point my code (not shown) finds the stored TaskCompletionSource item, sets the result and returns it. This all works fine but I have now been asked to consider a situation where a reply may never be returned when I request state via the “_evtClient.SubmitStateRequest(entity, propName);” line. I need to implement some sort of timeout mechanism so I can cancel the TCS task so the function caller can fail gracefully. I’ve been looking on SO and the internet and can’t find anything that looks right. I’m now not sure if I need to restructure the above code in a different way. Can anyone advise or point me to a similar scenario?
The code that calls the above function can call it in a single hit like this:
var stateProperty = await RequestStateForEntity(key, stateName);
or in a batch, like this:
await
Task.WhenAll(
stateDefinitions.Select(stateDefinition => stateDefinition.Name)
.Select(
stateName =>
Task.Factory.StartNew(
async () => results.Add(await RequestStateForEntity(key, stateName)))
.Unwrap())
.ToArray());
First off, what you really want to enable is cancellation. The fact that the cancellation comes from a timeout is just a footnote.
.NET has some great built-in support for cancellation, and the Task-based Asynchronous Pattern prescribes how to use it.
Essentially, what you want to do is take a CancellationToken:
Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName,
CancellationToken cancellationToken);
Next, you want to respond when that token is signaled. Ideally, you would want to just pass the CancellationToken down to the _evtClient so that the request is truly cancelled:
_evtClient.SubmitStateRequest(entity, propName, cancellationToken);
This is the normal way of implementing cancellation, and it works great if SubmitStateRequest already understands cancellation. Often the event arguments have a flag indicating cancellation (e.g., AsyncCompletedEventArgs.Cancelled). If at all possible, use this approach (i.e., change _evtClient as necessary to support cancellation).
But sometimes this just isn't possible. In this case, you can choose to pretend to support cancellation. What you're actually doing is just ignoring the request if it completes after it was cancelled. This is not the most ideal situation but sometimes you have no choice.
Personally, I don't really like this kind of approach since it makes the API "lie": the method signature claims to support cancellation but it really is just faking it. So first, I recommend documenting this. Put in a code comment apology explaining that _evtClient doesn't support cancellation, and the "cancellation" is actually just pretend cancellation.
Then, you'll need to hook into the CancellationToken yourself, after the state request item is in the list but before the actual request is sent:
var item = new StateRequestItem(entity, propName, tcs);
_stateRequestItemList.TryAdd(cacheKey, item);
item.CancellationRegistration = cancellationToken.Register(() =>
{
StateRequestItem cancelledItem;
if (!_stateRequestItemList.TryRemove(cacheKey, out cancelledItem))
return;
cancelledItem.TaskCompletionSource.TrySetCanceled();
});
_evtClient.SubmitStateRequest(entity, propName);
Finally, you'll need to update your event handler completion code (not shown) to ignore the situation where the state request item has already been removed, and to dispose the CancellationRegistration if the state request item is found.
Once your method supports cancellation, then it's easy to cancel via a timer:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
CancellationToken token = cts.Token;
or from any other kind of situation. Say, if the user cancels whatever (s)he's doing. Or if another part of the system decides it doesn't need that data anymore. Once your code supports cancellation, it can handle cancellation for any reason.
You may pass a CancellationToken to your method, which can internally implement the cancellation logic:
public Task<StatePropertyEx> RequestStateForEntity(
EntityKey entity, string propName, CancellationToken token)
{
var tcs = new TaskCompletionSource<StateInfo>();
try
{
// Cache checking
_evtClient.SubmitStateRequest(entity, propName, token);
return tcs.Task;
}
catch (Exception ex)
{
tcs.SetException(ex);
return tcs.Task;
}
}
And inside SubmitStateRequest:
token.ThrowIfCancellationRequest();
Note ThrowIfCancellationRequest will throw a OperationCanceledException which you will need to handle. If you are making a blocking call, you can internally set the CancellationTokenSource with a TimeSpan:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));