Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
Remove panics #73
Remove panics #73
Changes from 3 commits
b045242
ed02fe8
021a0d8
8efe419
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function already has an error on the return signature. Why not return an error here rather than a panic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great question! We perform a sanity check here because these parameters are not supported on the service end, and there could be potential data loss if the user relied on these parameters (and we let them).
Even if we returned this as an error instead, how could the user reasonably react to it? Would they call the API again without the offending access condition?
In my understanding, errors should be actionable. In this case, it is certainly not; so it might be better to panic and let the user catch this programmer error in their testing environment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree the error should be actionable just the same way that you would get an error from parsing something. Of course, you could retry parsing the same input, but you will still get the same error. The error is actually actionable. Most of all, it does not require special handling to ensure the application does not crash by the consumer of your API.
I find this usage of panic particularly subjective.
If you would like to indicate some level of severity with the error, then I suggest making a more informative error type. That way, you could provide some indication on the error type similar to how
net.Error
https://golang.org/pkg/net/#Error indicates some category of errors are temporary.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We keep going around and around on this. By the widely excepted convention documented in the golang wiki, panic/recover is just a way to shortcut control flow inside of your package and shouldn't be exposed through package boundaries. While calling it "sanityCheckFailed" it at least spares folks the number of hits they see when they
grep panic
on this library, the spirit of it doesn't change at all. Simply put, the community does not recognize any subtlety to when to return an error and when to panic: there's just a dangerous one and a safe one. Whether or not we agree with this assessment is not really relevant.In all cases where the function already ready returns an error, at the moment is is absolutely the correct thing to use that error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @devigned, I'm a bit confused by what you mean here:
To clarify, the receiver
SetMetadata
manipulates metadata on the container, but the Storage service ignores certain container access conditions(IfUnmodifiedSince, IfMatch, and IfNoneMatch). If the user relied on these access conditions, there could be data corruption and it's very hard to recover their data when they discover this problem. In other words, we are not doing some generic parsing here; we are trying to help the user not corrupt their data. Furthermore, the same problem exists with 2 other APIs, includingDelete
onContainerURL
, which ignores IfMatch and IfNoneMatch. We certainly do not want the user to mistakenly delete their entire container.If we take a step back, there are indeed other ways of helping the user with this problem. The two possibilities that I see are:
ContainerURL
take in individual parameters instead ofContainerAccessConditions
.Would you prefer option 1 or 2? And why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @marstr, thanks for the review. We indeed agreed to stop debating on whether panics are good/bad; instead we should be looking at our usage of panics on a case by case basis, and see if there's a way of removing it without impacting the quality/safety of the SDK. I certainly hope that you could see the progress we've made so far(there are only 13 calls to
sanityCheckFailed
). And with your help, I also hope to merge/publish this PR as soon as possible, to alleviate customer pain.Here are the 13 calls to
sanityCheckFailed
.ContainerURL
. They panic when the user relies on access conditions or parameters that are silently ignored by the Storage service. We are trying to protect the customers from data corruption/loss with the panics. We obviously do not expect the customers to write recover code for these APIs, we instead expect their code to fail immediately in the test environment when they attempt to use them.SharedKeyCredential
'sbuildCanonicalizedResource
. When failing to parse the query parameters, we panic instead of throwing an error because there must be a serious problem with the SDK itself if we are unable to parse the query parameters that own our code added. In other words, we can no longer proceed if we cannot trust the protocol layer.tokenCredential
's constructor. We panic if the user attempts to use HTTP instead of HTTPS. This is because the user is at the risk of exposing their bearer token with HTTP. Instead of letting the call go out, we want the user to catch this in their test environment.unmap
API of the memory-mapped-file types(Unix and Windows). We've already talked about this usage, since if we cannot unmap a memory-mapped-file, we cannot proceed as there could be memory corruption.The above is a brief explanation for each remaining usage of panic. Each case has some nuances and took us a good amount of time to consider their validity. Please feel free to reach out if you want to discuss them.
In short, the statement that panics means unsafe code is simply not true. At least the usage number 4 proves it. If we cannot unmap a memory-mapped-file, why shouldn't we crash with a clear message? The other two options in this case are:
This makes no sense, memory could be corrupted.
What are they supposed to do with it? Do you really expect them to write code to detect this?
Therefore, panics are in fact a necessity in some cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zezha-msft, let me try to clarify. You previously stated:
I was giving an example of where an error is returned, which provides information to the user about what went wrong (actionable), but should not be retried. For example, when parsing / unmarshalling JSON into a struct the error returned is actionable, but you will receive the same result if you call unmarshal a second time. By no means should the code below panic (though it does under the covers, I believe, but is handled before it hits the package boundary).
In the case of
SetMetadata
and similar methods, if you return an error to the consumer, you'd have the same API protecting effect as the panic, but without the undesirable possibility of crashing the consumers application. Since you'd return the error after you check if etags exist, you have ensured the user will not be able to call the API (c.client.SetAccessPolicy
) and possibly cause data loss.So the short answer is that I would select option 1. Return the error. Provide adequate information in the error for the consumer to understand the reason for the error and how they would change their code to avoid the error in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @devigned, thanks for the explanation! I'm ok with removing the panics in this case.
Could you please also take a look at the other cases that I've mentioned above? And see if you have any thoughts on them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, @zezha-msft.
ContainerURL
SetMetadata
and alike.buildCanonicalizedResource
andSharedKeyCredential
pipeline.PolicyFunc(func(ctx context.Context, request pipeline.Request) (pipeline.Response, error)
allows for an error return, so returning an error fromf.buildStringToSign
will not cause much pain for upward error propagation.tokenCredential
SetMetadata
and alike.unmap
ReadSeeker
SetMetadata
response == nil
func(ctx context.Context, request pipeline.Request) (response pipeline.Response, err error)
. If you'd like to have a stack for these error, consider using or building something like https://godoc.org/github.com/pkg/errors#WithStack.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, this seems like an acceptable place to panic.