¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Errores y reintentos en llamadas a la nube

Después de esta lección: Puedo distinguir un error que se reintenta de uno que se arregla.

Lección de 55 minSin comandos

Algunos errores se arreglan solos

Un servicio en la nube responde a cada llamada, y a veces la respuesta es un error. Un código de error te dice de qué clase es.

Un bucket de S3 que no existe seguirá sin existir si vuelves a preguntar. Una tabla de DynamoDB ocupada puede responder un segundo después. Esta lección enseña a distinguir los dos casos y a reintentar el correcto.

Al final de esta lección

Nota

Puedo distinguir un error que se reintenta de uno que se arregla.

Palabras clave

PalabraQué significaEjemplo
Código de errorun nombre corto de lo que salió malNoSuchKey
Código de estadoun número que clasifica una respuesta en una familia404
Limitaciónun servicio que rechaza llamadas porque enviaste demasiadasthrottling
Reintentoenviar otra vez la misma llamada
Espera crecienteesperar más antes de cada reintentobackoff
Jitteruna parte aleatoria de la espera, para que los clientes no se muevan a la vez

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Un error tiene un código y un estado

Cuando una llamada falla, boto3 te da un error. Lleva un código de estado, como 404, y un código de error, como NoSuchKey.

Comprueba el código de error y el estado. No compruebes el mensaje: su redacción puede cambiar entre servicios, y la del emulador difiere de la real en algunos casos.

Ejemplo resuelto: errores leídos de respuestas capturadas

LlamadaCodeEstado¿Reintentar?
get_object, la clave no existeNoSuchKey404No, arregla la clave
head_object, la clave no existe404404No, no hay cuerpo que leer
list_objects_v2, el bucket no existeNoSuchBucket404No, crea el bucket
put_item, condición falsaConditionalCheckFailedException400No, la respuesta es la respuesta
query, nombre de clave equivocadoValidationException400No, arregla la llamada
send_message, la cola no existeAWS.SimpleQueueService.NonExistentQueue400No, crea la cola

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Run: lee el código y el estado

Código: python
answers = [
    {"error": {"Code": "NoSuchKey", "Message": "Falta.", "HTTPStatusCode": 404}},
    {"error": {"Code": "ConditionalCheckFailedException", "Message": "No.", "HTTPStatusCode": 400}},
    {"error": {"Code": "InternalServerError", "Message": "Vaya.", "HTTPStatusCode": 500}},
    {"Items": []},
]
for answer in answers:
    if "error" not in answer:
        print("funcionó")
    else:
        error = answer["error"]
        print(error["Code"], error["HTTPStatusCode"])

Reintenta lo que puede funcionar la próxima vez

Algunos errores dicen «ahora no». Un error de limitación significa que enviaste demasiadas llamadas. Un estado de 500 o más significa que el servicio tuvo un fallo. Los dos pueden pasar si esperas.

Otros errores dicen «esta llamada no». Tu llamada estaba mal, así que la misma llamada falla siempre. Arréglala en lugar de reintentar.

Un error frecuente

Advertencia

Mucha gente reintenta todos los errores por si acaso. Una clave equivocada falla entonces cinco veces y se pierde la espera. Reintenta solo la limitación y los fallos del servidor. Arregla todos los demás errores, porque la misma llamada recibe la misma respuesta.

Run: separa reintentar y arreglar

Código: python
# Los códigos de limitación son los documentados, no capturados aquí.
THROTTLING = ["ThrottlingException", "ProvisionedThroughputExceededException", "SlowDown"]

def worth_retrying(error):
    if error["HTTPStatusCode"] >= 500:
        return True
    return error["Code"] in THROTTLING

errors = [
    {"Code": "NoSuchKey", "HTTPStatusCode": 404},
    {"Code": "SlowDown", "HTTPStatusCode": 503},
    {"Code": "ThrottlingException", "HTTPStatusCode": 400},
    {"Code": "ValidationException", "HTTPStatusCode": 400},
]
for error in errors:
    print(error["Code"], worth_retrying(error))
# Añade "RequestLimitExceeded" a THROTTLING y pruébalo.

La espera crece en cada intento

Si un servicio está ocupado, un reintento un milisegundo después lo ocupa más. Así que espera, y espera más después de cada fallo. Duplicar la espera es lo habitual. Pon un tope para que la espera nunca pase de un límite.

Ahora añade jitter: espera una parte aleatoria de ese tiempo. Sin él, cien clientes se despiertan a la vez y vuelven a fallar a la vez.

Ejemplo resuelto: el techo de cada espera

IntentoDuplicadoCon tope de 8Espera si pick es 0,5
10,5 segundos0,50,25
21 segundo10,5
32 segundos21
44 segundos42
58 segundos84
616 segundos84

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Run: una espera que se puede probar

Código: python
# pick es un número de 0 a 1. Un cliente real pasa random.random().
# Una prueba pasa un número fijo, así que la respuesta nunca cambia.
def backoff(attempt, base, cap, pick):
    ceiling = min(cap, base * 2 ** (attempt - 1))
    return ceiling * pick

for attempt in range(1, 7):
    print(attempt, backoff(attempt, 0.5, 8, 1.0), backoff(attempt, 0.5, 8, 0.5))
# Cambia el tope de 8 a 2. ¿Qué intentos cambian?

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.