Hi Azure SDK for GO team,
I have a request to create resources asynchronously. For example,
Retry-After in response may exceed 60 seconds.azure-asyncoperation in the header. Using REST API calls, I can check the completion of the creating request. How to do it in SDK?I've read https://github.com/Azure/azure-sdk-for-go/tree/master/arm#making-asynchronous-requests, but still have questions:
FooSender can cancel the polling. Does the polling refer to azure-asyncoperation?FooSender, how can I do the pooling in my codes to make sure the request is completed?Thanks in advance.
Howdy @bingosummer,
You've caught us somewhat! The documentation you are referring to is somewhat out of date, not wrong exactly, but not the state of the art.
For one, starting in v10, we updated the pattern of having methods return channels. The channel will return the response from the wire, and be closed once the operation is complete. This should make async requests super easy:
results, errs := client.CreateOrUpdate(...)
// Fire off other independent creations, etc
result := <-results
if err := <- errs; err != nil {
return err
}
However, you have some particular questions about how to create a storage account. Luckily, I have an example of doing exactly that with our new pattern here:
A function that creates a Storage Account Asynchronously
Calling that function
I know that doesn't exactly answer your questions about Senders and polling, but I hope it leads you in the right direction. If you're still having trouble, feel free to respond to this thread.
Hi @marstr
I didn't see any further question or comment on this, but I have I think a similar situation/question with the new APIs for the PostgreSQL service in v10.2.1-beta. When creating a new PostgreSQL server, we need to be able to return either an "error" or "request accepted" response quickly, and allow the client application to poll for completion.
In servers.go the generated CreateOrUpdateSender function is creating a azure.DoPollForAsynchronous decorator.
return autorest.SendWithSender(
client,
req,
azure.DoPollForAsynchronous(client.PollingDelay))
While the CreateOrUpdate function returns a server and error channel, I don't see the call returning until the polling has completed. i.e. the result on the server channel is the final result from the polling decorator, so effectively the method is always synchronous. If I update the code in servers.go to just use SendWithSender, I immediately get back the response with with the HTTP 201 and the Azure-AsyncOperation URL.
I saw in the examples that you posted the ability to process requests concurrently, but in this case the issue is that (as far as I can see) the generated code is resulting in synchronous behavior with no response until the server is completed (or fails).
If I create and pass in a cancel channel, its seems non-deterministic what gets cancelled. If I close the channel immediately after making the call to CreateOrUpdate, I get an error message that the HTTP connect operation was cancelled. So it seems to me that I should not be trying to use the cancel channel to get the desired behavior.
The docs referenced in the original question note that it should be possible to disable polling on the autorest.Client:
FooSender: This method sends the prepared http.Request. It handles the possible status codes and will, unless the disabled in the autorest.Client, handling polling.
However when I look at the generated code I don't see any place where it's possible to override the construction and decoration of the Sender.
When I look at the generated code for the SQL Server APIs , the sender is not setup for polling:
return autorest.SendWithSender(client, req))
But when I compare the swagger files for postgresql and sql I don't see any immediate difference (i.e. they both list 200,201, and 202 are responses and include x-ms-long-running-operation": true
So I'm not sure why the generated SQL Server and PostgreSQL clients are different.
Not sure if this makes sense. I'm happy to post the client code we've been testing with.
Thanks
Howdy @dave-read, I think I understand what you're saying, correct me if this isn't a good summary:
From a Go perspective, we have achieved a reasonable form of concurrency. However, using these language constructs suggests an ability to cancel resource creation, instead of cancelling the polling for creation.
Further more, you have noticed some inconsistency in what is generated for items marked as Long Running Operations.
To your first point, @jhendrixMSFT, @mcardosos, and I might have to chat about this a bit to see if we can tweak anything about the SDK's behavior, or if we just have to attempt to document this better.
For your second point, there may be a timing issue where our generator changed and we generated an additive package instead of reg-generating the whole darn SDK. However, that is sloppy and hopefully we'll be able to cut that sorta thing out a bit once we move to the code that I'm previewing here: experimental/allAPIVersions
edit: grammar
Hi @marstr I think the summary is good.
From an API behavior perspective, the python APIs for Azure databases seem intuitive to me. The long running operations are async by default and you get an object back that helps you to manage determining the final outcome of the request. If you want sync behavior, you explicitly delegate polling to the API by calling .result(timeout=n).
Thanks for providing the link to the experimental work. I'll have a look at that.
@marstr Sorry for late response. Thanks for the summary.
Thanks @dave-read. I think, my question is same with you. I also need a solution to get a ACCEPTED from Azure right after I send the request, and do the polling by myself.
Got it. What you're asking for, @bingosummer & @dave-read, makes sense. Until I can round up the crew and think through what we ought to be generating in the long term, you should be able to create your own wrapper function which calls DatabasesClient.CreateOrUpdatePreparer and DabasesClient.CreateOrUpdateResponder(...) with a custom version of DatabasesClient.CreateOrUpdateSender(...) that doesn't begin polling. Your wrapper should end up looking a lot like DatabasesClient.CreateOrUpdate(...) without the goroutine.
Thanks @marstr We'll take that path.
@marstr I'm having a similar issue with the Managed Disk Snapshot api.
The Managed Disk Snapshot Create REST API returns a 202 Accepted. This is the async behaviour I'd like to leverage. I don't want to wait around polling for the completion of the snapshot. The Go SDK does not allow for polling for completion to be bypassed. I went down the same cancel channel rabbit hole as @dave-read - that is not a workable option to cancel polling.
The azure cli provides a --no-wait flag for most long running operations that does not perform polling for completion after receiving a 202 Accepted. I would love to see a similar concept across all of the long running operations in the Azure Go SDK.
Here are some thoughts leveraging the snapshot code as an example:
https://github.com/Azure/azure-sdk-for-go/blob/master/arm/compute/snapshots.go#L47-L104
[47] func (client SnapshotsClient) CreateOrUpdate(resourceGroupName string, snapshotName string, snapshot Snapshot, cancel <-chan struct{}) (<-chan Snapshot, <-chan error) {
could be modified to the following:
[47] func (client SnapshotsClient) CreateOrUpdate(resourceGroupName string, snapshotName string, snapshot Snapshot, pollForCompletion bool, cancel <-chan struct{}) (<-chan Snapshot, <-chan error) {
...
[91] resp, err := client.CreateOrUpdateSender(req)
https://github.com/Azure/azure-sdk-for-go/blob/master/arm/compute/snapshots.go#L131-L136
[131] func (client SnapshotsClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) {
[132] return autorest.SendWithSender(client,
[133] req,
[134] azure.DoRetryWithRegistration(client.Client),
[135] azure.DoPollForAsynchronous(client.PollingDelay))
could be modified to the following:
[131] func (client SnapshotsClient) CreateOrUpdateSender(req *http.Request, pollForCompletion bool) (*http.Response, error) {
[132] if pollForCompletion {
[133] return autorest.SendWithSender(client,
[134] req,
[135] azure.DoRetryWithRegistration(client.Client)
[136] azure.DoPollForAsynchronous(client.PollingDelay))
[137] }
[138] return autorest.SendWithSender(client,
[139] req,
[140] azure.DoRetryWithRegistration(client.Client))
Thoughts? Seems like an easy enough addition for the generators?
In the v12 SDK (which will be released during the week of 11/27) long-running operations will return a future that can be used for polling status. The future type was added to go-autorest in https://github.com/Azure/go-autorest/commit/7e40f61402d694b6c0c5deb090fbee045700ce72. Sorry I don't have a sample yet, once the v12 SDK is released I'll get a comprehensive sample posted.
Most helpful comment
Thanks @marstr We'll take that path.