Как повторить выполнение задачи Django Celery при возникновении внутренней ошибки сервера?
I am trying to use Celery to send anywhere from 1 to about 25 consecutive requests to a third-party API. The measure of success is whether I get a URL back in the response payload: response_json["data"]["url"]. Celery or not, sometimes I get the data I expect, and sometimes I don't.
Я решил поэкспериментировать с Celery, чтобы повторить вызов API, используя при этом преимущества встроенного экспоненциального отката, который, казалось бы, идеально подходит для моих нужд, но я испытываю трудности с его реализацией.
<Вот пример одного из нескольких подобных подходов, которые я пробовал:
@shared_task(autoretry_for=(Exception), retry_backoff=True, retry_backoff_max=120)
def third_party_api_request(payload, api_url, headers):
response = requests.request("POST", api_url, headers=headers, data=payload)
response_json = response.json()
return response_json["data"]["url"]
# output:
Internal Server Error:
-- snip --
autoretry_for = tuple(
TypeError: 'type' object is not iterable
Другой подход, который я пробовал:
@shared_task(bind=True)
def third_party_api_request(self, payload, api_url, headers):
try:
response = requests.request("POST", api_url, headers=headers, data=payload)
response_json = response.json()
return response_json["data"]["url"]
except TypeError as exc:
logger.error("Error sending request to API: %s", exc)
raise self.retry(exc=exc)
# output:
ERROR 2022-04-22 17:31:40,131 tasks 72780 123145528369152 Error sending request to API: 'NoneType' object is not subscriptable
Internal Server Error:
-- snip --
TypeError: 'NoneType' object is not subscriptable
-- snip --
raise ret
celery.exceptions.Retry: Retry in 180s: TypeError("'NoneType' object is not subscriptable")
ERROR 2022-04-22 17:31:40,409 log 72780 123145528369152 Internal Server Error:
-- snip --
TypeError: 'NoneType' object is not subscriptable
-- snip --
raise ret
celery.exceptions.Retry: Retry in 180s: TypeError("'NoneType' object is not subscriptable")
И еще один подход, с похожими результатами:
@shared_task(autoretry_for=(TypeError), retry_backoff=True, retry_backoff_max=120)
def send_http_request_to_proctoru_task(payload, api_url, headers):
response = requests.request("POST", api_url, headers=headers, data=payload)
response_json = response.json()
try:
return response_json["data"]["url"]
except TypeError:
logger.error("API response: %s", response_json)
raise
Проблема заключалась в том, что я неправильно разместил оператор возврата. Мне предстоит еще много доработок, но следующий код решает проблему в вопросе, который я задал ранее сегодня. Я читал сообщения, документацию и статьи в течение нескольких дней, и я хотел бы приписать себе все заслуги, но именно эта запись в блоге помогла мне осознать мою ошибку: https://testdriven.io/blog/retrying-failed-celery-tasks/.
@shared_task(name="send_http_request_to_proctoru_task", bind=True, max_retries=6)
def send_http_request_to_proctoru_task(self, api_url, headers, payload):
try:
response = requests.request("POST", api_url, headers=headers, data=payload)
response_json = response.json()
if response_json["response_code"] == 2:
raise Exception()
return response_json["data"]["url"]
except Exception as exc:
logger.warning("Exception raised. Executing retry %s" % self.request.retries)
raise self.retry(exc=exc, countdown=2 ** self.request.retries)