Overview
Al utilizar PyMongo para realizar una operación en el servidor, también puedes limitar el tiempo que el servidor tiene para finalizar esta operación. Para ello, especifica un tiempo de espera para la operación del lado del cliente. El tiempo de espera se aplica a todos los pasos necesarios para completar la operación, incluyendo la selección del servidor, la comprobación de la conexión, la serialización y la ejecución en el lado del servidor. Cuando finaliza el tiempo de espera, PyMongo genera una excepción de tiempo de espera.
Puedes especificar un tiempo de espera de dos maneras: utilizando el timeout() método o utilizando la opción de conexión timeoutMS.
Método timeout()
Para especificar un tiempo de espera en su código, llame al método timeout() y pásele la duración del tiempo de espera, en segundos. Debe llamar al método timeout() en una instrucción with, como se muestra en el siguiente ejemplo:
with pymongo.timeout(10): collection.insert_one({"name": "Yngwie"})
En el ejemplo anterior, si la operación de inserción no finaliza en 10 segundos, PyMongo genera un error.
El tiempo de espera que se especifique se aplica a todas las operaciones dentro del bloque with. En el siguiente ejemplo, las operaciones de inserción y búsqueda deben terminar ambas dentro de un total de 10 segundos:
with pymongo.timeout(10): coll.insert_one({"name": "Yngwie"}) coll.find_one({"name": "Yngwie"})
Bloques de tiempo de espera anidados
Cuando la ejecución entra en un bloque de timeout anidado, el timeout exterior se pausa. Cuando la ejecución sale del bloque anidado de tiempo de espera, se reanuda el tiempo de espera anterior.
Importante
Un bloque de espera anidado puede acortar la fecha límite de un bloque de espera exterior, pero no puede ampliarlo.
El siguiente ejemplo muestra cómo usar llamadas anidadas al método timeout():
1 with pymongo.timeout(5): 2 collection.find_one() 3 with pymongo.timeout(3): 4 collection.find_one() 5 collection.find_one() 6 with pymongo.timeout(10): 7 collection.find_one() 8 collection.find_one()
En el ejemplo anterior, el código realiza los siguientes pasos:
Línea 1: crea un bloque de tiempo de espera de cinco segundos.
- 2Línea: Llama al
find_one()método dentro del bloque de tiempo de espera de cinco segundos. El servidor debe - complete esta operación dentro del tiempo de espera de cinco segundos.
- 2Línea: Llama al
Línea 3: Crea un bloque de tiempo de espera anidado de tres segundos.
- Línea 4: Llama al método
find_one()dentro del bloque de tiempo de espera de tres segundos. El servidor debe - Complete esta operación en tres segundos.
- Línea 4: Llama al método
- Línea 5: Llama al método
find_one()fuera del bloque de timeout de tres segundos. El servidor debe - completa esta operación dentro del tiempo de espera original de cinco segundos.
- Línea 5: Llama al método
Línea:6 crea un bloque de tiempo de espera anidado de diez segundos.
- Línea 7: llamando el método
find_one()dentro del bloque de tiempo de espera de diez segundos. Un tiempo de espera anidado - El bloque no puede extender un bloque externo de tiempo de espera, por lo que el servidor debe completar esta operación dentro del tiempo de espera original de cinco segundos.
- Línea 7: llamando el método
Línea 8: Llama al método
find_one()fuera del bloque de tiempo de espera de diez segundos. El servidor debe completar esta operación dentro del tiempo de espera original de cinco segundos.
Seguridad de subprocesos y tareas
El método timeout() es seguro para hilos. El tiempo de espera solo se aplica al hilo actual, y varios hilos pueden configurar diferentes tiempos de espera en paralelo.
El método timeout() también es seguro para asyncio. El tiempo de espera solo se aplica a la Tarea actual y se pueden configurar distintos tiempos de espera para varias Tareas al mismo tiempo.
El siguiente ejemplo muestra cómo utilizar el método timeout() con
Motor, el controlador de Python de MongoDB para aplicaciones asíncronas:
import motor.motor_asyncio client = motor.motor_asyncio.AsyncIOMotorClient() coll = client["test-db"]["test-collection"] with pymongo.timeout(10): await coll.insert_one({"name": "Yngwie"}) await coll.find_one({"name": "Yngwie"})
Opción de conexión timeoutMS
Para especificar un tiempo de espera al conectarse a una implementación de MongoDB, configure la opción de conexión timeoutMS con la duración del tiempo de espera, en milisegundos. Puede hacerlo de dos maneras: pasando un argumento al constructor MongoClient o mediante un parámetro en su cadena de conexión.
Los siguientes ejemplos de código utilizan la opción timeoutMS para especificar un tiempo de espera de 10 segundos:
client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname:<port>", timeoutMS=10000)
uri = "mongodb://<db_username>:<db_password>@<hostname:<port>/?timeoutMS=10000" client = pymongo.MongoClient(uri)
Si especifica la opción timeoutMS, PyMongo aplica automáticamente el tiempo de espera especificado a cada operación del servidor. Los siguientes ejemplos de código especifican un tiempo de espera de 10 segundos, y luego llaman a los métodos insert_one() y find_one(). Para ver cómo se compara la opción timeoutMS con el uso del método timeout(), selecciona la pestaña correspondiente.
uri = "mongodb://<db_username>:<db_password>@<hostname@:<port>/?timeoutMS=10000" client = pymongo.MongoClient(uri) coll = client["test-db"]["test-collection"] coll.insert_one({"name": "Yngwie"}) # Uses a 10-second timeout. coll.find_one({"name": "Yngwie"}) # Also uses a 10-second timeout.
client = MongoClient() coll = client["test-db"]["test-collection"] with pymongo.timeout(10): coll.insert_one({"name": "Yngwie"}) with pymongo.timeout(10): coll.find_one({"name": "Yngwie"})
Importante
timeout() Sobrescribe timeoutMS
Si especifica la opción timeoutMS y luego llama al método timeout() en su código, PyMongo ignora el valor timeoutMS dentro del bloque timeout:
client = MongoClient("mongodb://localhost/?timeoutMS=10000") coll = client["test-db"]["test-collection"] coll.insert_one({"name": "Yngwie"}) # Uses the client's 10-second timeout # pymongo.timeout overrides the client's timeoutMS. with pymongo.timeout(20): coll.insert_one({"name": "Yngwie"}) # Uses the 20-second timeout with pymongo.timeout(5): coll.find_one({"name": "Yngwie"}) # Uses the 5-second timeout
Manejo de excepciones
Cuando una operación del servidor excede el tiempo de espera especificado, PyMongo genera una excepción de tiempo de espera y establece la propiedad PyMongoError.timeout en True.
El siguiente ejemplo de código muestra una forma de gestionar una excepción de tiempo de espera. Dentro del bloque except, el código verifica la propiedad timeout para determinar si la excepción fue causada por un tiempo de espera.
try: with pymongo.timeout(10): coll.insert_one({"name": "Yngwie"}) time.sleep(10) # The deadline has now expired. The next operation will raise # a timeout exception. coll.find_one({"name": "Yngwie"}) except PyMongoError as exc: if exc.timeout: print(f"block timed out: {exc!r}") else: print(f"failed with non-timeout error: {exc!r}")
Solución de problemas
Las siguientes secciones describen los errores que podrías encontrar al utilizar tiempos de espera.
ServerSelectionTimeoutError
Este error indica que el cliente no pudo encontrar un servidor disponible para ejecutar la operación dentro del tiempo de espera establecido:
pymongo.errors.ServerSelectionTimeoutError: No servers found yet, Timeout: -0.00202266700216569s, Topology Description: <TopologyDescription id: 63698e87cebfd22ab1bd2ae0, topology_type: Unknown, servers: [<ServerDescription ('localhost', 27017) server_type: Unknown, rtt: None>]>
NetworkTimeout
Este error indica que el cliente no pudo establecer una conexión dentro del tiempo de espera dado o que la operación fue enviada pero el servidor no respondió a tiempo:
pymongo.errors.NetworkTimeout: localhost:27017: timed out
ExecutionTimeout
Este error puede indicar que el servidor canceló la operación porque se superó el tiempo de espera indicado. Incluso si PyMongo genera esta excepción, es posible que la operación se haya completado parcialmente en el servidor.
pymongo.errors.ExecutionTimeout: operation exceeded time limit, full error: {'ok': 0.0, 'errmsg': 'operation exceeded time limit', 'code': 50, 'codeName': 'MaxTimeMSExpired'}
También podría indicar que el cliente canceló la operación porque no fue posible completarla dentro del tiempo de espera indicado:
pymongo.errors.ExecutionTimeout: operation would exceed time limit, remaining timeout:0.00196 <= network round trip time:0.00427
WTimeoutError
Este error indica que el servidor no pudo completar la operación de escritura solicitada en el tiempo de espera proporcionado y siguiendo el nivel de confirmación de escritura (write concern) especificado:
pymongo.errors.WTimeoutError: operation exceeded time limit, full error: {'code': 50, 'codeName': 'MaxTimeMSExpired', 'errmsg': 'operation exceeded time limit', 'errInfo': {'writeConcern': {'w': 1, 'wtimeout': 0}}}
Error de escritura masiva
Este error indica que el servidor no pudo completar un método insert_many() o bulk_write() dentro del tiempo de espera dado y siguiendo el nivel de confirmación de escritura (write concern) especificado:
pymongo.errors.BulkWriteError: batch op errors occurred, full error: {'writeErrors': [], 'writeConcernErrors': [{'code': 50, 'codeName': 'MaxTimeMSExpired', 'errmsg': 'operation exceeded time limit', 'errInfo': {'writeConcern': {'w': 1, 'wtimeout': 0}}}], 'nInserted': 2, 'nUpserted': 0, 'nMatched': 0, 'nModified': 0, 'nRemoved': 0, 'upserted': []}
Documentación de la API
Para obtener más información sobre el uso de timeouts en PyMongo, consulta la siguiente documentación de la API: