Skip to content

Commit 3e53bd3

Browse files
authored
Handle RELO LRO synchronous completion (Azure#18353)
If the initial response contains a provisioning state use it. This will prevent extra polling in the case the operation is in a terminal state.
1 parent 76388be commit 3e53bd3

File tree

5 files changed

+42
-2
lines changed

5 files changed

+42
-2
lines changed

sdk/azcore/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Breaking Changes
88

99
### Bugs Fixed
10+
* Avoid polling when a RELO LRO synchronously terminates.
1011

1112
### Other Changes
1213

sdk/azcore/internal/pollers/async/async.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ func New[T any](pl exported.Pipeline, resp *http.Response, finalState pollers.Fi
7171
if !pollers.IsValidURL(asyncURL) {
7272
return nil, fmt.Errorf("invalid polling URL %s", asyncURL)
7373
}
74+
// check for provisioning state. if the operation is a RELO
75+
// and terminates synchronously this will prevent extra polling.
76+
// it's ok if there's no provisioning state.
77+
state, _ := pollers.GetProvisioningState(resp)
78+
if state == "" {
79+
state = pollers.StatusInProgress
80+
}
7481
p := &Poller[T]{
7582
pl: pl,
7683
resp: resp,
@@ -79,7 +86,7 @@ func New[T any](pl exported.Pipeline, resp *http.Response, finalState pollers.Fi
7986
OrigURL: resp.Request.URL.String(),
8087
Method: resp.Request.Method,
8188
FinalState: finalState,
82-
CurState: pollers.StatusInProgress,
89+
CurState: state,
8390
}
8491
return p, nil
8592
}

sdk/azcore/internal/pollers/async/async_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,15 @@ func TestPollFailedError(t *testing.T) {
244244
require.Error(t, err)
245245
require.Nil(t, resp)
246246
}
247+
248+
func TestSynchronousCompletion(t *testing.T) {
249+
resp := initialResponse(http.MethodPut, io.NopCloser(strings.NewReader(`{ "properties": { "provisioningState": "Succeeded" } }`)))
250+
resp.Header.Set(shared.HeaderAzureAsync, fakePollingURL)
251+
resp.Header.Set(shared.HeaderLocation, fakeResourceURL)
252+
poller, err := New[struct{}](exported.Pipeline{}, resp, "")
253+
require.NoError(t, err)
254+
require.Equal(t, fakePollingURL, poller.AsyncURL)
255+
require.Equal(t, fakeResourceURL, poller.LocURL)
256+
require.Equal(t, pollers.StatusSucceeded, poller.CurState)
257+
require.True(t, poller.Done())
258+
}

sdk/azcore/internal/pollers/loc/loc.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,19 @@ func New[T any](pl exported.Pipeline, resp *http.Response) (*Poller[T], error) {
6464
if !pollers.IsValidURL(locURL) {
6565
return nil, fmt.Errorf("invalid polling URL %s", locURL)
6666
}
67+
// check for provisioning state. if the operation is a RELO
68+
// and terminates synchronously this will prevent extra polling.
69+
// it's ok if there's no provisioning state.
70+
state, _ := pollers.GetProvisioningState(resp)
71+
if state == "" {
72+
state = pollers.StatusInProgress
73+
}
6774
return &Poller[T]{
6875
pl: pl,
6976
resp: resp,
7077
Type: kind,
7178
PollURL: locURL,
72-
CurState: pollers.StatusInProgress,
79+
CurState: state,
7380
}, nil
7481
}
7582

sdk/azcore/internal/pollers/loc/loc_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
1818
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
19+
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
1920
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
2021
"github.com/stretchr/testify/require"
2122
)
@@ -29,6 +30,7 @@ func initialResponse() *http.Response {
2930
return &http.Response{
3031
Header: http.Header{},
3132
StatusCode: http.StatusAccepted,
33+
Body: http.NoBody,
3234
}
3335
}
3436

@@ -161,3 +163,14 @@ func TestUpdateFailedWithProvisioningState(t *testing.T) {
161163
var respErr *azcore.ResponseError
162164
require.ErrorAs(t, err, &respErr)
163165
}
166+
167+
func TestSynchronousCompletion(t *testing.T) {
168+
resp := initialResponse()
169+
resp.Body = io.NopCloser(strings.NewReader(`{ "properties": { "provisioningState": "Succeeded" } }`))
170+
resp.Header.Set(shared.HeaderLocation, fakeLocationURL)
171+
poller, err := New[struct{}](exported.Pipeline{}, resp)
172+
require.NoError(t, err)
173+
require.Equal(t, fakeLocationURL, poller.PollURL)
174+
require.Equal(t, pollers.StatusSucceeded, poller.CurState)
175+
require.True(t, poller.Done())
176+
}

0 commit comments

Comments
 (0)