Repository navigation
Implement asynchronous support in ODataJsonLightBatchWriter - #2111
Conversation
c52bf6e to
fca52e2
Compare
fca52e2 to
c45de75
Compare
c45de75 to
653a8f7
Compare
| await this.WriteEndBatchImplementationAsync() | ||
| // Note that we intentionally go through the public API so that if the Flush fails the writer moves to the Error state. | ||
| .FollowOnSuccessWithTask(task => this.FlushAsync()); | ||
| .FollowOnSuccessWithTask(task => this.FlushAsync()) |
There was a problem hiding this comment.
Out of curiosity, do you know why ContinueWith is not used here instead?
There was a problem hiding this comment.
Or just using await on the second statement, i.e.
await this.WriteEndBatchImplementationAsync();
await this.FlushAsync();What else is FollowOnSuccessWithTask doing behind the scenes?
There was a problem hiding this comment.
You can think of FollowOnSuccessWithTask as a "syntactic" sugar for ContinueWith, although it's a little more than that. We actually use ContinueWith in the method. The method is designed to propagate failures if the antecedent task fails. If WriteEndBatchImplementationAsync throws an ODataException, we propagate that to the caller - not an AggregateException like ContinueWith would do.
The delegate passed to FollowOnSuccessWithTask will not be executed if the antecedent task fails. Specifically in this case, we do not wish to execute FlushAsync if WriteEndBatchImplementationAsync fails.
The method is defined in TaskUtils class just in case you'd like to study the implementation further.
| return TaskUtils.GetTaskForSynchronousOperation(() => this.WriteStartChangesetImplementation(changesetId)) | ||
| .FollowOnSuccessWith(t => this.FinishWriteStartChangeset()); | ||
| await this.WriteStartChangesetImplementationAsync(changesetId) | ||
| .FollowOnSuccessWith(t => this.FinishWriteStartChangeset()) |
There was a problem hiding this comment.
Out of curiosity, what's the difference between FollowOnSuccessWithTask and FollowOnSuccessWith
There was a problem hiding this comment.
Are there async versions of this.FinishWriteStartChangeSet()?
There was a problem hiding this comment.
FollowOnSuccessWithTask supports delegate that return another Task instance while FollowOnSuccessWith doesn't. As an illustration consider the following two usages:
Task.Factory.StartNew(() =>
{
// Do work
return new Random().Next(1, 100);
}).FollowOnSuccessWith((antecedentTask) =>
{
Console.WriteLine("Result: {0}", antecedentTask.Result);
});
// vs.
Task.Factory.StartNew(() =>
{
// Do work
// return new Random().Next(1, 100);
}).FollowOnSuccessWithTask((antecedentTask) =>
{
return Task.Factory.StartNew(() => Console.WriteLine("Result: {0}", antecedentTask.Result));
});There was a problem hiding this comment.
There's no async version of FinishWriteStartChangeSet. The private method currently doesn't do any writing. It's an implementation detail so it'd be easy to introduce an async equivalent if the synchronous version evolved to doing any writing.
| Debug.Assert(operationListener != null, "operationListener != null"); | ||
|
|
||
| return new ODataWriteStream(outputStream, operationListener); | ||
| return new ODataWriteStream(outputStream, operationListener, synchronous); |
There was a problem hiding this comment.
Could you remind me what the synchronous argument does again? I remember it from a previous PR but don't remember what it was used for. I think it was used in the dispose methods, but I'm not quite sure.
There was a problem hiding this comment.
Reference to #2109, ODataWriteStream previously had no way of telling whether we're writing synchronously or asynchronously. We address that gap by introducing this argument such that when the Dispose method is invoked, either of StreamDisposed or StreamDisposedAsync is called depending on the value passed for the argument.
This approach is necessitated by the fact that we support NET45 and NETSTANDARD1_1 both of which do not support IAsyncDisposable that would make it feasible to use DisposeAsync in asynchronous scenarios.
| await this.FlushAsynchronously() | ||
| .FollowOnFaultWith(t => this.SetState(BatchWriterState.Error)) | ||
| .ConfigureAwait(false); |
There was a problem hiding this comment.
What does FollowOnFaultWith do? Could this be safely transformed to something like:
try {
await this.FlushAsynchronously();
} catch {
this.SetState(BatchWriterState.Error);
}There was a problem hiding this comment.
FollowOnFaultWith is designed such that the Action delegate is invoked only when the antecedent task fails (i.e. task is in a Faulted state)
There was a problem hiding this comment.
It could be transformed the way you demonstrated. But instead of littering the code with such try...catch I think it's neater to encapsulate that in a helper method
653a8f7 to
83c24dd
Compare
|
This PR has Quantification details
Why proper sizing of changes matters
Optimal pull request sizes drive a better predictable PR flow as they strike a
What can I do to optimize my changes
How to interpret the change counts in git diff output
Was this comment helpful? 👍 :ok_hand: :thumbsdown: (Email) |
Issues
This pull request is in partial fulfilment of issue #2019.
Description
Implement asynchronous support in
ODataJsonLightBatchWriterTo prevent a breaking change, I added the following 6
protected virtualasynchronous methods to thepublic abstract ODataBatchWriterclass as asynchronous wrappers over the synchronous methods.WriteStartBatchImplementationAsyncWriteEndBatchImplementationAsyncWriteStartChangesetImplementationAsyncWriteEndChangesetImplementationAsyncCreateOperationRequestMessageImplementationAsyncCreateOperationResponseMessageImplementationAsyncThese virtual asynchronous methods are then invoked from the public asynchronous methods. For example;
becomes
This I believe should guarantee asynchronous methods in the abstract base class will work as before unless overridden in a derived class.
It also allows asynchronous support to be implemented by overriding the protected methods in the derived class(es).
Checklist (Uncheck if it is not completed)
Additional work necessary
If documentation update is needed, please add "Docs Needed" label to the issue and provide details about the required document change in the issue.