Skip to content

Commit 0d775c6

Browse files
authored
Merge pull request #48 from DataCrunch-io/feature/instance-create-retry-params
instances.create: expose retry settings, add exponential backoff
2 parents 02cd7a3 + 8c3f23f commit 0d775c6

File tree

2 files changed

+26
-9
lines changed

2 files changed

+26
-9
lines changed

CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added `max_wait_time`, `initial_interval`, `max_interval`, `backoff_coefficient` keyword arguments to `instances.create()`
13+
14+
### Changed
15+
16+
- Cap `instances.create()` retry interval to 5 seconds; add exponential backoff; increase default `max_wait_time` from 60 to 180 seconds
17+
1018
## [1.14.0] - 2025-08-15
1119

1220
### Added

datacrunch/instances/instances.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import time
2+
import itertools
23
from typing import List, Union, Optional, Dict, Literal
34
from dataclasses import dataclass
45
from dataclasses_json import dataclass_json
@@ -123,7 +124,12 @@ def create(self,
123124
is_spot: bool = False,
124125
contract: Optional[Contract] = None,
125126
pricing: Optional[Pricing] = None,
126-
coupon: Optional[str] = None) -> Instance:
127+
coupon: Optional[str] = None,
128+
*,
129+
max_wait_time: float = 180,
130+
initial_interval: float = 0.5,
131+
max_interval: float = 5,
132+
backoff_coefficient: float = 2.0) -> Instance:
127133
"""Creates and deploys a new cloud instance.
128134
129135
Args:
@@ -141,6 +147,10 @@ def create(self,
141147
contract: Optional contract type for the instance.
142148
pricing: Optional pricing model for the instance.
143149
coupon: Optional coupon code for discounts.
150+
max_wait_time: Maximum total wait for the instance to start provisioning, in seconds (default: 180)
151+
initial_interval: Initial interval, in seconds (default: 0.5)
152+
max_interval: The longest single delay allowed between retries, in seconds (default: 5)
153+
backoff_coefficient: Coefficient to calculate the next retry interval (default 2.0)
144154
145155
Returns:
146156
The newly created instance object.
@@ -169,20 +179,19 @@ def create(self,
169179
id = self._http_client.post(INSTANCES_ENDPOINT, json=payload).text
170180

171181
# 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()
176-
while True:
182+
deadline = time.monotonic() + max_wait_time
183+
for i in itertools.count():
177184
instance = self.get_by_id(id)
178185
if instance.status != InstanceStatus.ORDERED:
179186
return instance
180187

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

185-
time.sleep(POLL_INTERVAL)
193+
interval = min(initial_interval * backoff_coefficient ** i, max_interval, deadline - now)
194+
time.sleep(interval)
186195

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

0 commit comments

Comments
 (0)