Skip to content

Commit 90af6cf

Browse files
committed
instances.create: expose retry settings, add exponential backoff
1 parent 02cd7a3 commit 90af6cf

File tree

2 files changed

+28
-8
lines changed

2 files changed

+28
-8
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.15.0] - 2025-10-23
11+
12+
### Added
13+
14+
- Added `max_wait_time`, `initial_interval`, `max_interval`, `backoff_coefficient` keyword arguments to `instances.create()`
15+
16+
### Changed
17+
18+
- Cap retry interval to 5 seconds during `instances.create()` and add exponential backoff
19+
1020
## [1.14.0] - 2025-08-15
1121

1222
### Added

datacrunch/instances/instances.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,12 @@ def create(self,
123123
is_spot: bool = False,
124124
contract: Optional[Contract] = None,
125125
pricing: Optional[Pricing] = None,
126-
coupon: Optional[str] = None) -> Instance:
126+
coupon: Optional[str] = None,
127+
*,
128+
max_wait_time: float = 60,
129+
initial_interval: float = 0.5,
130+
max_interval: float = 5,
131+
backoff_coefficient: float = 2.0) -> Instance:
127132
"""Creates and deploys a new cloud instance.
128133
129134
Args:
@@ -141,6 +146,10 @@ def create(self,
141146
contract: Optional contract type for the instance.
142147
pricing: Optional pricing model for the instance.
143148
coupon: Optional coupon code for discounts.
149+
max_wait_time: Maximum total wait for the instance to start provisioning, in seconds (default: 60)
150+
initial_interval: Initial interval, in seconds (default: 0.5)
151+
max_interval: The longest single delay allowed between retries, in seconds (default: 5)
152+
backoff_coefficient: Coefficient to calculate the next retry interval (default 2.0)
144153
145154
Returns:
146155
The newly created instance object.
@@ -169,20 +178,21 @@ def create(self,
169178
id = self._http_client.post(INSTANCES_ENDPOINT, json=payload).text
170179

171180
# Wait for instance to enter provisioning state with timeout
172-
MAX_WAIT_TIME = 60 # Maximum wait time in seconds
173-
POLL_INTERVAL = 0.5 # Time between status checks
174-
175-
start_time = time.time()
181+
interval = min(initial_interval, max_interval)
182+
start = time.monotonic()
183+
deadline = start + max_wait_time
176184
while True:
177185
instance = self.get_by_id(id)
178186
if instance.status != InstanceStatus.ORDERED:
179187
return instance
180188

181-
if time.time() - start_time > MAX_WAIT_TIME:
189+
now = time.monotonic()
190+
if now >= deadline:
182191
raise TimeoutError(
183-
f"Instance {id} did not enter provisioning state within {MAX_WAIT_TIME} seconds")
192+
f"Instance {id} did not enter provisioning state within {max_wait_time:.1f} seconds")
184193

185-
time.sleep(POLL_INTERVAL)
194+
time.sleep(min(interval, deadline - now))
195+
interval = min(interval * backoff_coefficient, max_interval)
186196

187197
def action(self, id_list: Union[List[str], str], action: str, volume_ids: Optional[List[str]] = None) -> None:
188198
"""Performs an action on one or more instances.

0 commit comments

Comments
 (0)