Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix retry arg and improve retry strategy #362

Merged
merged 10 commits into from
Mar 10, 2025
2 changes: 1 addition & 1 deletion roboflow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from roboflow.models import CLIPModel, GazeModel # noqa: F401
from roboflow.util.general import write_line

__version__ = "1.1.54"
__version__ = "1.1.55"


def check_key(api_key, model, notebook, num_retries=0):
Expand Down
30 changes: 21 additions & 9 deletions roboflow/adapters/rfapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Optional

import requests
from requests.exceptions import RequestException
from requests_toolbelt.multipart.encoder import MultipartEncoder

from roboflow.config import API_URL, DEFAULT_BATCH_NAME, DEFAULT_JOB_NAME
Expand All @@ -26,6 +27,7 @@ class AnnotationSaveError(RoboflowError):
def __init__(self, message, status_code=None):
self.message = message
self.status_code = status_code
self.retries = 0
super().__init__(self.message)


Expand Down Expand Up @@ -85,14 +87,21 @@ def upload_image(
"file": ("imageToUpload", imgjpeg, "image/jpeg"),
}
)
response = requests.post(upload_url, data=m, headers={"Content-Type": m.content_type}, timeout=(300, 300))

try:
response = requests.post(upload_url, data=m, headers={"Content-Type": m.content_type}, timeout=(300, 300))
except RequestException as e:
raise ImageUploadError(str(e)) from e

else:
# Hosted image upload url
upload_url = _hosted_upload_url(api_key, project_url, image_path, split, coalesced_batch_name, tag_names)

# Get response
response = requests.post(upload_url, timeout=(300, 300))
try:
# Get response
response = requests.post(upload_url, timeout=(300, 300))
except RequestException as e:
raise ImageUploadError(str(e)) from e

responsejson = None
try:
Expand Down Expand Up @@ -147,12 +156,15 @@ def save_annotation(
api_key, project_url, annotation_name, image_id, job_name, is_prediction, overwrite
)

response = requests.post(
upload_url,
data=json.dumps({"annotationFile": annotation_string, "labelmap": annotation_labelmap}),
headers={"Content-Type": "application/json"},
timeout=(60, 60),
)
try:
response = requests.post(
upload_url,
data=json.dumps({"annotationFile": annotation_string, "labelmap": annotation_labelmap}),
headers={"Content-Type": "application/json"},
timeout=(60, 60),
)
except RequestException as e:
raise AnnotationSaveError(str(e)) from e

# Handle response
responsejson = None
Expand Down
39 changes: 25 additions & 14 deletions roboflow/core/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import requests

from roboflow.adapters import rfapi
from roboflow.adapters.rfapi import ImageUploadError
from roboflow.adapters.rfapi import AnnotationSaveError, ImageUploadError
from roboflow.config import API_URL, DEMO_KEYS
from roboflow.core.version import Version
from roboflow.util.general import Retry
Expand Down Expand Up @@ -515,26 +515,34 @@ def save_annotation(
job_name=None,
is_prediction: bool = False,
annotation_overwrite=False,
num_retry_uploads=0,
):
project_url = self.id.rsplit("/")[1]
annotation_name, annotation_str = self._annotation_params(annotation_path)
t0 = time.time()
upload_retry_attempts = 0
retry = Retry(num_retry_uploads, AnnotationSaveError)

annotation = rfapi.save_annotation(
self.__api_key,
project_url,
annotation_name, # type: ignore[type-var]
annotation_str, # type: ignore[type-var]
image_id,
job_name=job_name, # type: ignore[type-var]
is_prediction=is_prediction,
annotation_labelmap=annotation_labelmap,
overwrite=annotation_overwrite,
)
try:
annotation = rfapi.save_annotation(
self.__api_key,
project_url,
annotation_name, # type: ignore[type-var]
annotation_str, # type: ignore[type-var]
image_id,
job_name=job_name, # type: ignore[type-var]
is_prediction=is_prediction,
annotation_labelmap=annotation_labelmap,
overwrite=annotation_overwrite,
)
upload_retry_attempts = retry.retries
except AnnotationSaveError as e:
e.retries = upload_retry_attempts
raise

upload_time = time.time() - t0

return annotation, upload_time
return annotation, upload_time, upload_retry_attempts

def single_upload(
self,
Expand Down Expand Up @@ -563,6 +571,7 @@ def single_upload(
uploaded_image, uploaded_annotation = None, None
upload_time, annotation_time = None, None
upload_retry_attempts = 0
annotation_upload_retry_attempts = 0

if image_path:
uploaded_image, upload_time, upload_retry_attempts = self.upload_image(
Expand All @@ -579,13 +588,14 @@ def single_upload(
image_id = uploaded_image["id"] # type: ignore[index]

if annotation_path and image_id:
uploaded_annotation, annotation_time = self.save_annotation(
uploaded_annotation, annotation_time, annotation_upload_retry_attempts = self.save_annotation(
annotation_path,
annotation_labelmap,
image_id,
batch_name,
is_prediction,
annotation_overwrite,
num_retry_uploads=num_retry_uploads,
)

return {
Expand All @@ -594,6 +604,7 @@ def single_upload(
"upload_time": upload_time,
"annotation_time": annotation_time,
"upload_retry_attempts": upload_retry_attempts,
"annotation_upload_retry_attempts": annotation_upload_retry_attempts,
}

def _annotation_params(self, annotation_path):
Expand Down
2 changes: 2 additions & 0 deletions roboflow/core/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ def _upload_image(imagedesc):
batch_name=batch_name,
sequence_number=imagedesc.get("index"),
sequence_size=len(images),
num_retry_uploads=num_retries,
)

return image, upload_time, upload_retry_attempts
Expand Down Expand Up @@ -376,6 +377,7 @@ def _save_annotation(image_id, imagedesc):
annotation_labelmap=labelmap,
image_id=image_id,
job_name=batch_name,
num_retry_uploads=num_retries,
)

return annotation, upload_time
Expand Down
17 changes: 14 additions & 3 deletions roboflow/util/general.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import sys
import time
from random import random


def write_line(line):
Expand All @@ -13,8 +15,16 @@ def __init__(self, max_retries, retry_on):
self.retry_on = retry_on
self.retries = 0

def backoff(self):
"""
Backoff for a random time based on number of retries.
"""
base_t_ms = 100
max_t_ms = 30000
sleep_ms = random() * min(max_t_ms, base_t_ms * 2**self.retries)
time.sleep(int(sleep_ms) / 1000)

def __call__(self, func, *args, **kwargs):
self.retries = 0
retry_on = self.retry_on
if not retry_on:
retry_on = (Exception,)
Expand All @@ -24,8 +34,9 @@ def __call__(self, func, *args, **kwargs):
return func(*args, **kwargs)
except BaseException as e:
if isinstance(e, retry_on):
self.retries += 1
if self.retries > self.max_retries:
if self.retries >= self.max_retries:
raise
self.backoff()
self.retries += 1
else:
raise
Loading