Skip to content

Commit 304414a

Browse files
CopilotPallabPaul
andauthored
Restore original 404 retry logic (with counter) and add separate 406 handling
Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-python/sessions/dbd04bba-1b0d-4c00-92c0-5ee3d41d6653 Co-authored-by: PallabPaul <20746357+PallabPaul@users.noreply.github.com>
1 parent e05b180 commit 304414a

3 files changed

Lines changed: 49 additions & 45 deletions

File tree

sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/_operations/_patch.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def __init__(
5555
self._latest_response: JSON = {}
5656

5757
self._retry_not_found = retry_not_found
58+
self._not_found_count = 0
5859

5960
def initialize(self, client, initial_response, deserialization_callback): # pylint: disable=unused-argument
6061
self._evaluate_response(initial_response)
@@ -101,17 +102,22 @@ def run(self) -> None:
101102
try:
102103
response = self._operation()
103104
self._evaluate_response(response)
105+
except ResourceNotFoundError as not_found_exception:
106+
# We'll allow some instances of resource not found to account for replication
107+
# delay if session stickiness is lost.
108+
109+
self._not_found_count += 1
110+
111+
not_retryable = not self._retry_not_found or self._give_up_not_found_error(not_found_exception)
112+
113+
if not_retryable or self._not_found_count >= 3:
114+
raise
104115
except HttpResponseError as http_exception:
105-
# 404: node doesn't know about the transaction yet (replication lag)
106-
# 406: node knows the transaction but it hasn't committed yet
107-
# Both are transient during transaction-status polling — treat as
116+
# 406: node knows the transaction but it hasn't committed yet.
117+
# This is transient during transaction-status polling — treat as
108118
# pending so the poller retries.
109-
if http_exception.status_code in (404, 406) and self._retry_not_found:
110-
if (
111-
isinstance(http_exception, ResourceNotFoundError)
112-
and self._give_up_not_found_error(http_exception)
113-
):
114-
raise
119+
if http_exception.status_code == 406 and self._retry_not_found:
120+
pass # stay in polling loop
115121
else:
116122
raise
117123
if not self.finished():

sdk/confidentialledger/azure-confidentialledger/azure/confidentialledger/aio/_operations/_patch.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,22 @@ async def run(self) -> None:
5656
try:
5757
response = await self._operation()
5858
self._evaluate_response(response)
59+
except ResourceNotFoundError as not_found_exception:
60+
# We'll allow some instances of resource not found to account for replication
61+
# delay if session stickiness is lost.
62+
63+
self._not_found_count += 1
64+
65+
not_retryable = not self._retry_not_found or self._give_up_not_found_error(not_found_exception)
66+
67+
if not_retryable or self._not_found_count >= 3:
68+
raise
5969
except HttpResponseError as http_exception:
60-
# 404: node doesn't know about the transaction yet (replication lag)
61-
# 406: node knows the transaction but it hasn't committed yet
62-
# Both are transient during transaction-status polling — treat as
70+
# 406: node knows the transaction but it hasn't committed yet.
71+
# This is transient during transaction-status polling — treat as
6372
# pending so the poller retries.
64-
if http_exception.status_code in (404, 406) and self._retry_not_found:
65-
if (
66-
isinstance(http_exception, ResourceNotFoundError)
67-
and self._give_up_not_found_error(http_exception)
68-
):
69-
raise
73+
if http_exception.status_code == 406 and self._retry_not_found:
74+
pass # stay in polling loop
7075
else:
7176
raise
7277
if not self.finished():

sdk/confidentialledger/azure-confidentialledger/tests/test_polling.py

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,17 @@ def _make_http_response_error(status_code, message="Error"):
3838

3939
class TestStatePollingMethodTransient404:
4040
"""Verify that 404 responses during polling are treated as transient when
41-
retry_not_found is True.
41+
retry_not_found is True, up to 3 retries.
4242
"""
4343

4444
def test_404_retried_then_committed(self):
45-
"""404 should be retried and eventually succeed when the transaction commits."""
45+
"""404 should be retried (up to 3 times) and eventually succeed."""
4646
call_count = 0
4747

4848
def operation():
4949
nonlocal call_count
5050
call_count += 1
51-
if call_count <= 3:
51+
if call_count <= 2:
5252
raise _make_resource_not_found()
5353
return {"state": "Committed"}
5454

@@ -57,7 +57,7 @@ def operation():
5757
poller.run()
5858

5959
assert poller.status() == "finished"
60-
assert call_count == 4
60+
assert call_count == 3
6161

6262
def test_404_raises_when_retry_not_found_false(self):
6363
"""404 should raise immediately when retry_not_found is False."""
@@ -89,23 +89,19 @@ def operation():
8989

9090
assert poller.status() == "failed"
9191

92-
def test_many_404s_retried_without_limit(self):
93-
"""More than 3 consecutive 404s should still be retried (no hard limit)."""
94-
call_count = 0
92+
def test_404_raises_after_3_retries(self):
93+
"""After 3 consecutive 404s the poller should give up and raise."""
9594

9695
def operation():
97-
nonlocal call_count
98-
call_count += 1
99-
if call_count <= 10:
100-
raise _make_resource_not_found()
101-
return {"state": "Committed"}
96+
raise _make_resource_not_found()
10297

10398
poller = StatePollingMethod(operation, "Committed", 0, retry_not_found=True)
10499
poller._status = "polling"
105-
poller.run()
106100

107-
assert poller.status() == "finished"
108-
assert call_count == 11
101+
with pytest.raises(ResourceNotFoundError):
102+
poller.run()
103+
104+
assert poller.status() == "failed"
109105

110106

111107
class TestStatePollingMethodTransient406:
@@ -237,7 +233,7 @@ async def test_404_retried_then_committed(self):
237233
async def operation():
238234
nonlocal call_count
239235
call_count += 1
240-
if call_count <= 3:
236+
if call_count <= 2:
241237
raise _make_resource_not_found()
242238
return {"state": "Committed"}
243239

@@ -246,7 +242,7 @@ async def operation():
246242
await poller.run()
247243

248244
assert poller.status() == "finished"
249-
assert call_count == 4
245+
assert call_count == 3
250246

251247
@pytest.mark.asyncio
252248
async def test_404_raises_when_retry_not_found_false(self):
@@ -275,22 +271,19 @@ async def operation():
275271
assert poller.status() == "failed"
276272

277273
@pytest.mark.asyncio
278-
async def test_many_404s_retried_without_limit(self):
279-
call_count = 0
274+
async def test_404_raises_after_3_retries(self):
275+
"""After 3 consecutive 404s the async poller should give up and raise."""
280276

281277
async def operation():
282-
nonlocal call_count
283-
call_count += 1
284-
if call_count <= 10:
285-
raise _make_resource_not_found()
286-
return {"state": "Committed"}
278+
raise _make_resource_not_found()
287279

288280
poller = AsyncStatePollingMethod(operation, "Committed", 0, retry_not_found=True)
289281
poller._status = "polling"
290-
await poller.run()
291282

292-
assert poller.status() == "finished"
293-
assert call_count == 11
283+
with pytest.raises(ResourceNotFoundError):
284+
await poller.run()
285+
286+
assert poller.status() == "failed"
294287

295288

296289
class TestAsyncStatePollingMethodTransient406:

0 commit comments

Comments
 (0)