Skip to content

Conversation

@live1206
Copy link
Member

@live1206 live1206 commented Jan 9, 2024

  • Construct RehydrationToken from multiple fields
    • HeaderSource: Indicates where the NextRequestUri locates in the initial response header, it could be OperationLocation, AzureAsyncOperation, Location or None
    • NextRequestUri: The Uri to poll status of the operation
    • InitialUri: The initial Uri of the operation
    • RequestMethod: Http request method of the operation
    • LastKnowLocation: The last known Location value in the response header
    • FinalStateVia: Configuration from API spec, it could be AzureAsyncOperation, Location, OriginalUri or OperationLocation
  • For fake delete LRO with 404, just return empty response with 204 because the delete succeeded
  • To support LRO rehydration for both DPG and MPG, make Operation non-abstract

@live1206 live1206 added the Mgmt This issue is related to a management package. label Jan 9, 2024
@live1206 live1206 force-pushed the lro-rehydration-core branch from d06f4ab to 9518c51 Compare January 9, 2024 09:51
@KrzysztofCwalina
Copy link
Member

Can we eliminate some of the shared source files? We consider shared sources to be engineering debt.

public override string ToString() { throw null; }
}
public abstract partial class Operation
public partial class Operation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What motivates making this type non-abstract? This is a big change to a central Core type, to it would be good to be crisp on the reasons that motivate this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One suggestion is to add these APIs to an internal subtype of Operation and return that anywhere this instance is needed. I believe we would prefer to keep the Operation type itself internal, as that lets it evolve better in the future, helps us address performance issues, etc.

Copy link
Member Author

@live1206 live1206 Mar 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, did not add enough explanation for this.
The goal is to implement LRO rehydration for both DPG and MPG.
Had a discussion with @m-nash , @KrzysztofCwalina and @tg-msft, given the way we are implementing LRO for DPG nowadays, there is no instantiable type for DPG users, only Operation available.
That's why we want to make Operation non-abstract.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have two choices: 1) we could make the type non-abstract as in this PR, 2) we could add a static method Operation.Rehydrate that returns an internal subtype of Operation. I know that we initially said that we wanted a ctor, but now that I think about it the static method could be better. Rehydration is an advanced scenario and the static method would hide the API a bit. Also, it would allow us to not have to add fields to Operation

Copy link
Member Author

@live1206 live1206 Mar 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Static method is better to me as well. Given we can't add type constraint to constructor directly, we need to throw in constructor.
I will create another PR to implement it with static method to not mess with the existing change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #42686 for static methods

protected Operation() { }
public abstract bool HasCompleted { get; }
public abstract string Id { get; }
public Operation(Azure.Core.Pipeline.HttpPipeline pipeline, Azure.Core.RehydrationToken? rehydrationToken, Azure.Core.ClientOptions options = null) { }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be preferable not to add a dependency of an end-user type (Operation) on a client-internal type (HttpPipeline). Is there any way we could avoid this? If you don't know offhand, we might be able to help you find design-alternatives if you could share the scenario that motivates the addition of this API.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual implementation is hold in NextLinkOperationImplementation, so make Operation non-abstract, we need to implement the LRO rehydration based on NextLinkOperationImplementation.
To be able to construct NextLinkOperationImplementation, we need the user to input HttpPipeline and RehydrationToken, as construction here.

protected Operation() { }
public abstract bool HasValue { get; }
public abstract T Value { get; }
public Operation(Azure.Core.Pipeline.HttpPipeline pipeline, Azure.Core.RehydrationToken? rehydrationToken, Azure.Core.ClientOptions options = null) { }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment that we should work to avoid adding a dependency on HttpPipeline in this type's public API.


if (!lroDetails.TryGetValue("nextRequestUri", out var nextRequestUri))
{
throw new InvalidOperationException("Invalid next request URI from rehydration token");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the correct exception type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to ArgumentException

public OperationInternal(IOperation operation,
ClientDiagnostics clientDiagnostics,
Response rawResponse,
Response? rawResponse,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What scenario motivates passing a null raw response? It looks like Operation.GetRawResponse has a non-nullable return type. Are you planning to return null from this method? I believe that would violate the contract with the end user.

Copy link
Member Author

@live1206 live1206 Mar 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the case of LRO rehydration used here, the user doesn't have response in place since the operation is not complete.
And if we take a look at the documentation of rawResponse here, it says "When a user instantiates an directly using a public constructor, there's no previous service call. In this case, passing null is expected."
So, I think it should be nullable in the first place.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #42666 for this change.

}

public override Response RawResponse => _stateLock.TryGetValue(out var state) ? state.RawResponse : _rawResponse;
public override Response RawResponse => (_stateLock.TryGetValue(out var state) ? state.RawResponse : _rawResponse) ?? throw new InvalidOperationException("The operation does not have a response yet. Please call UpdateStatus or WaitForCompletion first.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who calls this API? If it's internal, would it make sense to provide a tester rather than throwing an exception? It is not idiomatic to .NET to throw exceptions from properties - we should avoid doing this if possible.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case is, the operation is not complete yet, there is no response for the user to consume.
The user need to update the status till complete firstly.

Copy link
Member Author

@live1206 live1206 Mar 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, we should make this nullable instead and check if null in the usage.

Copy link
Member Author

@live1206 live1206 Mar 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we throw on incomplete operation for OperationInternal.Value. To be consistent, we should throw on this as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get latest state during rehydration to avoid empty raw response, implemented in #42686

throw state.OperationFailedException!;
}

private class EmptyResponse : Response
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we would avoid adding new internal subtypes of Response. Is there one in the Azure.Core code base you can reuse? If not, it would be good to add a comment to the type to explain why it exists and why it is unique, when you would expect to return it to an end-user, and what they would expect from it, so that maintainers of this code no what they can and can't change going forward.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used for fake delete LRO, we just want to return an empty response with 204 to the user for this case.

Added the above comment to the class documentation. I can't find any existing internal implemented Response type for this usage.

Assert.Null(operation.GetRehydrationToken());
}

[Test]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we move forward with making the Operation type non-abstract, this test file should contain full coverage of all implementation details of the implementation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to add tests for some properties of Operation and OperationOfT.
We need to mock HttpPipeline methods to be able to test UpdateStatus.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be possible with the TestFramework in Azure.Core - let us know if you need any help with this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will dig around firstly, and get back if need any help.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented in #42686

@live1206
Copy link
Member Author

Based on #41088 (comment), we would like to expose the LRO rehydration functionality as static method, created #42686 for it.

@live1206 live1206 marked this pull request as draft March 14, 2024 08:54
@live1206
Copy link
Member Author

continue the work in #42686

@live1206 live1206 closed this Mar 29, 2024
@live1206 live1206 deleted the lro-rehydration-core branch September 2, 2024 05:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Mgmt This issue is related to a management package.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants