44import pytest
55
66from databricks .sdk .errors import NotFound , ResourceDoesNotExist
7- from databricks .sdk .retries import poll , retried , RetryError
7+ from databricks .sdk .retries import RetryError , poll , retried
88from tests .clock import FakeClock
99
1010
@@ -79,38 +79,156 @@ def foo():
7979@pytest .mark .parametrize (
8080 "scenario,attempts,result_value,exception_type,exception_msg,timeout,min_time,max_time" ,
8181 [
82- pytest .param ("success" , 1 , "immediate" , None , None , 60 , 0.0 , 0.0 ,
83- id = "returns string immediately on first attempt with no sleep" ),
84- pytest .param ("success" , 2 , 42 , None , None , 60 , 1.05 , 1.75 ,
85- id = "returns integer after 1 retry with ~1s backoff" ),
86- pytest .param ("success" , 3 , {"key" : "val" }, None , None , 60 , 3.10 , 3.90 ,
87- id = "returns dict after 2 retries with linear backoff (1s+2s)" ),
88- pytest .param ("success" , 5 , [1 , 2 ], None , None , 60 , 10.25 , 11.75 ,
89- id = "returns list after 4 retries with linear backoff (1s+2s+3s+4s)" ),
90- pytest .param ("success" , 1 , None , None , None , 60 , 0.0 , 0.0 ,
91- id = "returns None as valid result immediately (None is acceptable)" ),
92- pytest .param ("success" , 5 , "ok" , None , None , 200 , 10.2 , 13.0 ,
93- id = "verifies linear backoff increase over 4 retries" ),
94- pytest .param ("success" , 11 , "ok" , None , None , 200 , 55.5 , 62.5 ,
95- id = "verifies linear backoff approaching 10s cap over 10 retries" ),
96- pytest .param ("success" , 15 , "ok" , None , None , 200 , 95.7 , 105.5 ,
97- id = "verifies backoff is capped at 10s after 10th retry" ),
98- pytest .param ("timeout" , None , None , TimeoutError , "Timed out after" , 1 , 1 , None ,
99- id = "raises TimeoutError after 1 second of continuous retries" ),
100- pytest .param ("timeout" , None , None , TimeoutError , "Timed out after" , 5 , 5 , None ,
101- id = "raises TimeoutError after 5 seconds of continuous retries" ),
102- pytest .param ("timeout" , None , None , TimeoutError , "Timed out after" , 15 , 15 , None ,
103- id = "raises TimeoutError after 15 seconds of continuous retries" ),
104- pytest .param ("halt" , 1 , None , ValueError , "halt error" , 60 , None , None ,
105- id = "raises ValueError immediately when halt error on first attempt" ),
106- pytest .param ("halt" , 2 , None , ValueError , "halt error" , 60 , None , None ,
107- id = "raises ValueError after 1 retry when halt error on second attempt" ),
108- pytest .param ("halt" , 3 , None , ValueError , "halt error" , 60 , None , None ,
109- id = "raises ValueError after 2 retries when halt error on third attempt" ),
110- pytest .param ("unexpected" , 1 , None , RuntimeError , "unexpected" , 60 , None , None ,
111- id = "raises RuntimeError immediately on unexpected exception" ),
112- pytest .param ("unexpected" , 3 , None , RuntimeError , "unexpected" , 60 , None , None ,
113- id = "raises RuntimeError after 2 retries on unexpected exception" ),
82+ pytest .param (
83+ "success" ,
84+ 1 ,
85+ "immediate" ,
86+ None ,
87+ None ,
88+ 60 ,
89+ 0.0 ,
90+ 0.0 ,
91+ id = "returns string immediately on first attempt with no sleep" ,
92+ ),
93+ pytest .param ("success" , 2 , 42 , None , None , 60 , 1.05 , 1.75 , id = "returns integer after 1 retry with ~1s backoff" ),
94+ pytest .param (
95+ "success" ,
96+ 3 ,
97+ {"key" : "val" },
98+ None ,
99+ None ,
100+ 60 ,
101+ 3.10 ,
102+ 3.90 ,
103+ id = "returns dict after 2 retries with linear backoff (1s+2s)" ,
104+ ),
105+ pytest .param (
106+ "success" ,
107+ 5 ,
108+ [1 , 2 ],
109+ None ,
110+ None ,
111+ 60 ,
112+ 10.25 ,
113+ 11.75 ,
114+ id = "returns list after 4 retries with linear backoff (1s+2s+3s+4s)" ,
115+ ),
116+ pytest .param (
117+ "success" ,
118+ 1 ,
119+ None ,
120+ None ,
121+ None ,
122+ 60 ,
123+ 0.0 ,
124+ 0.0 ,
125+ id = "returns None as valid result immediately (None is acceptable)" ,
126+ ),
127+ pytest .param (
128+ "success" , 5 , "ok" , None , None , 200 , 10.2 , 13.0 , id = "verifies linear backoff increase over 4 retries"
129+ ),
130+ pytest .param (
131+ "success" ,
132+ 11 ,
133+ "ok" ,
134+ None ,
135+ None ,
136+ 200 ,
137+ 55.5 ,
138+ 62.5 ,
139+ id = "verifies linear backoff approaching 10s cap over 10 retries" ,
140+ ),
141+ pytest .param (
142+ "success" , 15 , "ok" , None , None , 200 , 95.7 , 105.5 , id = "verifies backoff is capped at 10s after 10th retry"
143+ ),
144+ pytest .param (
145+ "timeout" ,
146+ None ,
147+ None ,
148+ TimeoutError ,
149+ "Timed out after" ,
150+ 1 ,
151+ 1 ,
152+ None ,
153+ id = "raises TimeoutError after 1 second of continuous retries" ,
154+ ),
155+ pytest .param (
156+ "timeout" ,
157+ None ,
158+ None ,
159+ TimeoutError ,
160+ "Timed out after" ,
161+ 5 ,
162+ 5 ,
163+ None ,
164+ id = "raises TimeoutError after 5 seconds of continuous retries" ,
165+ ),
166+ pytest .param (
167+ "timeout" ,
168+ None ,
169+ None ,
170+ TimeoutError ,
171+ "Timed out after" ,
172+ 15 ,
173+ 15 ,
174+ None ,
175+ id = "raises TimeoutError after 15 seconds of continuous retries" ,
176+ ),
177+ pytest .param (
178+ "halt" ,
179+ 1 ,
180+ None ,
181+ ValueError ,
182+ "halt error" ,
183+ 60 ,
184+ None ,
185+ None ,
186+ id = "raises ValueError immediately when halt error on first attempt" ,
187+ ),
188+ pytest .param (
189+ "halt" ,
190+ 2 ,
191+ None ,
192+ ValueError ,
193+ "halt error" ,
194+ 60 ,
195+ None ,
196+ None ,
197+ id = "raises ValueError after 1 retry when halt error on second attempt" ,
198+ ),
199+ pytest .param (
200+ "halt" ,
201+ 3 ,
202+ None ,
203+ ValueError ,
204+ "halt error" ,
205+ 60 ,
206+ None ,
207+ None ,
208+ id = "raises ValueError after 2 retries when halt error on third attempt" ,
209+ ),
210+ pytest .param (
211+ "unexpected" ,
212+ 1 ,
213+ None ,
214+ RuntimeError ,
215+ "unexpected" ,
216+ 60 ,
217+ None ,
218+ None ,
219+ id = "raises RuntimeError immediately on unexpected exception" ,
220+ ),
221+ pytest .param (
222+ "unexpected" ,
223+ 3 ,
224+ None ,
225+ RuntimeError ,
226+ "unexpected" ,
227+ 60 ,
228+ None ,
229+ None ,
230+ id = "raises RuntimeError after 2 retries on unexpected exception" ,
231+ ),
114232 ],
115233)
116234def test_poll_behavior (
@@ -137,20 +255,20 @@ def test_poll_behavior(
137255 def fn () -> tuple [Any , Optional [RetryError ]]:
138256 nonlocal call_count
139257 call_count += 1
140-
258+
141259 if scenario == "success" :
142260 if call_count < attempts :
143261 return None , RetryError .continues (f"attempt { call_count } " )
144262 return result_value , None
145-
263+
146264 elif scenario == "timeout" :
147265 return None , RetryError .continues ("retrying" )
148-
266+
149267 elif scenario == "halt" :
150- if call_count < attempts :
268+ if call_count < attempts :
151269 return None , RetryError .continues ("retrying" )
152270 return None , RetryError .halt (ValueError (exception_msg ))
153-
271+
154272 elif scenario == "unexpected" :
155273 if call_count < attempts :
156274 return None , RetryError .continues ("retrying" )
@@ -167,10 +285,10 @@ def fn() -> tuple[Any, Optional[RetryError]]:
167285 else :
168286 with pytest .raises (exception_type ) as exc_info :
169287 poll (fn , timeout = timedelta (seconds = timeout ), clock = clock )
170-
288+
171289 assert exception_msg in str (exc_info .value )
172290 assert call_count >= 1
173-
291+
174292 if scenario == "timeout" :
175293 assert clock .time () >= min_time - 1
176294 elif scenario in ("halt" , "unexpected" ):
0 commit comments