Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LLC] For Discussion: Add constructor that takes Response to RequestFailedException #24248

Closed
wants to merge 5 commits into from

Conversation

annelo-msft
Copy link
Member

Opening this PR as draft, so we can have a discussion about the points raised in the implementation of the new constructor for RequestFailedException.

Our overarching goal (per #23372) is to implement this API:

        public static implicit operator Pet(Response response)
        {
            if (response.IsError())
            {
                throw new RequestFailedException(response);
            }

            return DeserializePet(JsonDocument.Parse(response.Content.ToMemory()));
        }

// ??
// (that would be so much tidier!)
// QUESTION: What happens in the Async case, do we still need it?
// Because we can't have an async constructor.
Copy link
Member Author

Choose a reason for hiding this comment

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

Open question here on what we would do in async APIs if we wanted to replace the _clientDiagnostics call with new RequestFailedException(response) instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

Per @pakrym:
It's not a huge problem
most client calls are buffered
so all reads are sync
but for the last 1% I wonder if we should auto buffer error responses

// _clientDiagnostics.CreateRFException() -- this happens via different constructors (Note: does it have to? Could we refactor this?)
//
// 2) In the ResponseStatusOptions.NoThrow case, we're going through code paths in
// ResponseClassifier, which will become the only path after resolution of #24031
Copy link
Member Author

Choose a reason for hiding this comment

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

Note: we'll reconcile these two code paths in #24031

{
// NOTE: is it weird that we're saying NoThrow here and it throws?
// This looks confusing to me as someone reading this code.
Pet pet = await client.GetPetAsync("pet1", ResponseStatusOption.NoThrow);
Copy link
Member Author

Choose a reason for hiding this comment

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

Question: do we like the look of this API, with the exception thrown when we pass NoThrow?

return _pipeline.Span[0].ProcessAsync(message, _pipeline.Slice(1));

// TODO: We need to discuss possible performance implications of this
await _pipeline.Span[0].ProcessAsync(message, _pipeline.Slice(1)).ConfigureAwait(false);
Copy link
Member Author

@annelo-msft annelo-msft Sep 27, 2021

Choose a reason for hiding this comment

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

Question: what are the performance implications of making this change -- it would affect all libraries.

Copy link
Member

Choose a reason for hiding this comment

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

I would propose that we don't do this change now. After LLC RC, we can measure the impact and either do this change or leave the code as-is.

Copy link
Member Author

Choose a reason for hiding this comment

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

That makes sense. Given that this approach relies on Response.RequestClassifier being internal, the approach to putting this functionality in Azure.Core.Experimental may end up looking quite different. I can do that implementation, but I wanted to make sure we discussed what it would look like in Core in the long term so we didn't end up with unpleasant surprises when we made that transition.

@@ -2,15 +2,51 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
Copy link
Member Author

Choose a reason for hiding this comment

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

Note, only some of the ClientDiagnostics functionality has been moved over to ResponseClassifier here; this will be addressed in #24031.


// Note: we can ignore additionalInfo for now (for the purposes of the discussion of this design)
// because it's only passed in by exception creation methods called by the service libraries,
// and isn't useful when we are creating the exception from the response directly.
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 points to an opportunity to make some changes to the ClientDiagnostics API when we make it public in ResponseClassifier. We can clarify what's strictly required (i.e. the response), and what available input properties are there to allow service libraries to customize the exception.

@@ -144,5 +144,8 @@ internal static void DisposeStreamIfNotBuffered(ref Stream? stream)
stream = null;
}
}

// TODO: Are we happy with this internal property on Response?
Copy link
Member

Choose a reason for hiding this comment

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

It's reasonable, but why would we not add a property pointing to the whole message, i.e. the classifier is already in the message.

Copy link
Contributor

Choose a reason for hiding this comment

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

Classifier is the only thing that survives the message lifetime.

Copy link
Member Author

Choose a reason for hiding this comment

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

My concern about adding the whole message is that it contains Request which may be disposed before the Response lifetime is over, so we would have to take extra care in Response to only access parts of message at the right time. This approach side-steps that issue.

Copy link
Contributor

Choose a reason for hiding this comment

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

But if we are careful we can reference the entire thing.

Copy link
Member Author

Choose a reason for hiding this comment

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

But if we are careful we can reference the entire thing.

Do you mean that we can include message as an internal property as long as we make sure we don't access Request after it is disposed? Just out of curiosity, what is the benefit of having the whole message over just the ResponseClassifier?

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you mean that we can include message as an internal property as long as we make sure we don't access Request after it is disposed?

Yes

Just out of curiosity, what is the benefit of having the whole message over just the ResponseClassifier?

Next time we need something from the message we don't need to add another property. But it's a pretty weak reason for me.

return _pipeline.Span[0].ProcessAsync(message, _pipeline.Slice(1));

// TODO: We need to discuss possible performance implications of this
await _pipeline.Span[0].ProcessAsync(message, _pipeline.Slice(1)).ConfigureAwait(false);
Copy link
Member

Choose a reason for hiding this comment

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

I would propose that we don't do this change now. After LLC RC, we can measure the impact and either do this change or leave the code as-is.

/// Initializes a new instance of <see cref="ResponseClassifier"/>.
/// </summary>
/// <param name="options"></param>
public ResponseClassifier(ClientOptions options)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is interesting. We will need to enforce everyone using this ctor and ban the parameterless one.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't know that they always can -- sometimes ResponseClassifier is newed in Core and would take DefaultClientOptions then. We should think this through and see if there's another way.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know that they always can

We have to. Otherwise, exception formatting would sanitize all the headers even if they were marked as safe by the client author.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok :)

@annelo-msft
Copy link
Member Author

Had the discussion I was interested in, closing this in favor of an approach that sets the Reponse property in Core.Experimental.

azure-sdk pushed a commit to azure-sdk/azure-sdk-for-net that referenced this pull request Jun 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants