Usando asyncio con Odoo y Mercadolibre

24 de noviembre de 2025 por
Gustavo Orrillo
| Sin comentarios aún
 

Una realidad que enfrentamos los que trabajamos con Meracdolibre y Odoo es la necesidad de manipular decenas de miles de publicaciones (no estoy exagerando. Por ejemplo, tengo un cliente que tiene más de 53,000 variantes. Si a cada variante la multiplican por la cantidad de cuotas... facil terminas con más de 100,000 publicaciones).

Bueno, como muchos sabemos Odoo es muy bueno en muchas cosas pero no es precisamente muy rápido. Menos cuando se trata de manipular miles de registros. Que podemos hacer para acelerar el proceso de trabajar con miles de publicaciones (procesos que involucran leer las publicaciones, calcular su precio, actualizar su precio y stock)? Una herramienta muy util que conocí en los últimos días es la librería asyncio. Que  es una librería que nos permite realizar llamadas asincrónicas con Odoo. 

Las operaciones que involucran la invocación de webservices tienen muchos tiempos muertos por la latencia involucrada en la operación misma. asyncio permite en Python dentro del mismo thread, realizar dichas llamadas de forma segura. No vamos a entrar en detalles sobre como funciona (hay excelentes tutoriales sobre eso) ni discutir porque Odoo no brinda soporte a asyncio (no tiene porque hacerlo). Si vamos a dar un ejemplo sobre lo que se tarda obtener los datos de 9013 publicaciones usando la librería requests, y lo que se tarda utilizando las librerías asyncio y aiohttp.

Primero, usando la librería requests tardamos unos 870 segundos en leer unas 9013 publicaciones (más de 14 minutos)


Y este es el código que utilizamos para realizar el procesamiento


def get_items():
with open("canal1_general.csv", newline="", encoding="utf-8") as f:
reader = csv.reader(f)
for row in reader:
print(row)
items.append(row[0])
return items

def get_item(item_id):
url = f"{API_BASE}/items/{item_id}"
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}"
}
resp = requests.get(url, headers=headers)
if resp.status_code == 200:
return resp.json()
else:
print(f"Error {resp.status} for {item_id}")
return None

def main():
item_ids = get_items()
begin = datetime.now()
for item in item_ids:
result = get_item(item)
if result:
print(result["id"], result["title"], result["price"])
end = datetime.now()
print('%s'%((end - begin).seconds))

if __name__ == "__main__":
main()

Como pueden ver, obtengo la lista de publicaciones de un archivo de texto (podría haberlo hecho con xmlrpc) y luego las proceso con requests. Más de 14 minutos procesando como muestro en el screenshot. Veamos ahora con aiosync cuanto tardamos.


10 segundos! es brutal la diferencia. 870 segundos versus 10 segundos.

Y el código para hacer el mismo procesamiento es el siguiente.


def get_items():
with open("canal1_general.csv", newline="", encoding="utf-8") as f:
reader = csv.reader(f)
for row in reader:
print(row)
items.append(row[0])
return items

async def get_item(session, item_id):
url = f"{API_BASE}/items/{item_id}"
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}"
}
#import pdb;pdb.set_trace()
async with session.get(url, headers=headers) as resp:
if resp.status == 200:
return await resp.json()
else:
print(f"Error {resp.status} for {item_id}")
return None

async def main():
item_ids = get_items() # replace with yours

begin = datetime.now()
async with aiohttp.ClientSession() as session:
tasks = [get_item(session, iid) for iid in item_ids]
results = await asyncio.gather(*tasks)
for item in results:
if item:
print(item["id"], item["title"], item["price"])
end = datetime.now()
print('%s'%((end - begin).seconds))

if __name__ == "__main__":
asyncio.run(main())

No hay grandes cambios, pero si una lógica de procesamiento diferente. Lógica que vale la pena aprender debido a la ganancia que tenemos en performance. En otros posts vamos a seguir ahondando en la librería asyncio

Gustavo Orrillo 24 de noviembre de 2025
Compartir
Archivar
Identificarse dejar un comentario