Detección de trabajos atascados: heartbeats, timeouts y alertas
Aprende a detectar trabajos atascados con heartbeats sencillos, timeouts sensatos y alertas claras para saber si el trabajo va lento, falló o realmente está atascado.

Por qué «no se está ejecutando» es tan difícil de depurar
Cuando alguien dice «no se está ejecutando», normalmente quiere decir «hice clic en el botón y no pasó nada». El problema es que la mayoría de las apps hacen mucho trabajo después del clic, fuera de la vista. Sin señales claras, no puedes saber si el trabajo nunca arrancó, arrancó y solo va lento, o arrancó y se quedó atascado a mitad de camino.
Un trabajo en segundo plano es simplemente una tarea que tu app hace más tarde, sin hacer esperar al usuario en la pantalla. Ejemplos comunes: generar un PDF, importar un CSV, enviar correos, crear un informe o sincronizar datos. El usuario lo desencadena y la app lo entrega a un worker que corre en segundo plano.
Desde fuera, tres resultados distintos pueden verse exactamente iguales:
- Lento: está ejecutándose, pero toma más tiempo del esperado.
- Fallado: se detuvo con un error y no se completará solo.
- Atascado: arrancó, pero ya no avanza.
Los tres se sienten como «no pasa nada». Tu panel puede seguir mostrando «en cola» o «procesando» durante horas. Los mensajes de soporte se acumulan. Los fundadores acaban adivinando: ¿está caído el servidor?, ¿terminará en la noche?, ¿hizo algo mal el usuario?
La detección de trabajos atascados convierte esa adivinanza en una respuesta clara. Quieres una forma sencilla de ver:
- ¿El trabajo arrancó?
- ¿Sigue vivo ahora mismo?
- ¿Cuándo fue la última vez que avanzó?
- ¿Cuándo lo declaramos en timeout?
- ¿A quién se alerta y qué debe hacer después?
Una vez visibles esas preguntas, «no se está ejecutando» pasa a ser un problema diagnosticable, no un misterio.
Lento vs fallado vs atascado: una definición simple
Cuando alguien dice «el trabajo no se está ejecutando», normalmente habla de una de tres cosas. Si no nombras cuál es, los equipos tienden a adivinar, reintentar y crear trabajo duplicado (incluyendo cobrar dos veces a un cliente o enviar el mismo correo dos veces).
Lento
Un trabajo lento sigue avanzando. Puede estar subiendo archivos, llamando a una API o procesando registros, solo que más despacio de lo esperado. La señal clave es que algo cambia: las marcas de tiempo se actualizan, los números de progreso suben o aparecen nuevas líneas de log.
Fallado
Un trabajo fallado se detuvo. No se completará por sí solo. Puede haber crashado, encontrado un error crítico o perdido acceso a algo que necesita (por ejemplo, un permiso o un archivo faltante). El punto clave es que terminó, y la única forma de completar el trabajo es arreglar la causa y volver a ejecutarlo.
Atascado
El trabajo atascado es el más complicado. El trabajo «existe» (está en la cola o marcado como en ejecución), pero ya no hace progreso. Está lo bastante vivo como para bloquear otro trabajo, pero no lo bastante sano como para terminar.
En la práctica:
- Lento: el progreso sigue cambiando, aunque sea poco.
- Fallado: termina con un error y se detiene.
- Atascado: parece estar ejecutándose, pero nada cambia durante demasiado tiempo.
Ejemplo: el correo del informe diario no salió. Si reintentás a ciegas, podrías enviarlo dos veces después. Si sabés que falló, podés volver a ejecutarlo con seguridad. Si sabés que está atascado, podés reiniciarlo a propósito en lugar de esperar horas.
Los tres bloques: heartbeats, timeouts y alertas
Si querés que la detección de trabajos atascados parezca simple, pensalo como seguir un envío. Necesitás prueba de que se mueve, un punto donde decidís que no, y una forma de avisar a alguien.
1) Heartbeats: prueba de vida (o progreso)
Un heartbeat es una pequeña señal que un trabajo envía mientras corre. Puede ser tan básico como «sigo trabajando» cada minuto, o mejor, «procesados 3 de 20 items». La meta no es detalle fino. Es confianza de que el trabajo avanza.
Los buenos heartbeats son consistentes y baratos. Un trabajo de exportar archivos puede hacer heartbeat después de cada bloque escrito. Una sincronización de datos puede heartbeat después de actualizar cada cliente. Si un trabajo no puede reportar progreso significativo, al menos debería reportar «estoy vivo» en un horario fijo.
2) Timeouts: cuando «quizá lento» se convierte en «atascado»
Un timeout es la regla que convierte el silencio en un estado claro. Responde: ¿cuánto estás dispuesto a esperar sin heartbeat antes de tratar el trabajo como atascado?
Una configuración simple se ve así:
- Elige un intervalo esperado de heartbeat (por ejemplo, cada 60 segundos).
- Permite una ventana de gracia (por ejemplo, 5 heartbeats perdidos).
- Marca el trabajo como atascado cuando se excede esa ventana.
- Registra el último progreso conocido para saber dónde se detuvo.
3) Alertas: un mensaje que un humano puede actuar
Una alerta debe llegar a una persona real e incluir suficiente contexto para responder sin adivinar: qué trabajo, qué cliente, cuándo fue el último progreso y qué hizo el sistema después (reintento, pausa o fallo).
No necesitás un tablero sofisticado. Una vista de estado simple alcanza: última ejecución, última vez de heartbeat y si se activó un timeout. Eso convierte «no se está ejecutando» en un problema observable en lugar de un debate.
Cómo añadir heartbeats sin sobreingeniería
Un heartbeat es solo una pequeña señal de «sigo vivo» que tu trabajo actualiza mientras corre. Convierte la detección de trabajos atascados de adivinanza a un hecho verificable: ¿cuándo fue la última vez que el trabajo reportó progreso?
Empezá con la señal de progreso más pequeña que puedas actualizar de forma segura. Para la mayoría, una sola fila en una tabla de la base de datos es suficiente. Mantenlo simple y durable para que un reinicio no lo borre.
Elegí un heartbeat difícil de fingir
Elige un campo que coincida con lo que significa «progreso» para el trabajo:
last_heartbeat_at(timestamp)current_step(por ejemplo: "fetching data", "writing file", "sending email")processed_count(cuántos ítems se completaron)
Actualizá el heartbeat solo después de terminar una unidad real de trabajo (una página obtenida, un lote escrito, una factura procesada). Así reflejará movimiento real, no un bucle que gira para siempre.
Fijá un ritmo de actualización que encaje con el trabajo
Un heartbeat que dispara demasiado seguido genera ruido. Si es muy raro, te enterás tarde.
Como guía: actualizar por paso en trabajos cortos, cada 1 a 5 minutos en trabajos largos, y después de cada llamada externa en cualquier cosa que dependa de una API o una subida. Hacelo visible. Una vista administrativa pequeña alcanza: ID del trabajo, estado, paso actual y hora del último heartbeat. Entonces «no se está ejecutando» se convierte en «se dejó de enviar heartbeats a las 14:14 mientras escribía el paso 3».
Elegir timeouts que coincidan con la realidad
Un timeout no debería ser un número al azar. Es una promesa: «si este trabajo no avanzó para X, probablemente algo anda mal y responderemos de forma predecible».
Empezá con dos timeouts, no con uno:
- Un soft timeout: «esto está tardando más de lo normal»
- Un hard timeout: «dejar de esperar, algo está mal»
Basá los valores en ejecuciones reales, no en suposiciones. Mirá tu ejecución más lenta normal (no el mejor día, ni un corte total) y usala como punto de partida. Si los informes suelen terminar en 2 a 4 minutos pero a veces tardan 8, un soft timeout en 6 y un hard timeout en 15 es mejor que un hard de 5 que dispara todo el tiempo.
Algunos trabajos son «trabajo rápido más espera larga». Esperar a una API externa, una subida de archivo o un proveedor de pagos puede pausar el progreso sin que el trabajo esté roto. Manejá eso dando a cada paso su propio límite, así podés ver dónde se está consumiendo el tiempo.
Una forma simple de fijar timeouts
Usá esto como punto de partida y ajustá según datos reales:
- Soft timeout: 1.5x a 2x tu ejecución normal más lenta
- Hard timeout: 3x a 5x tu ejecución normal más lenta
- Timeouts por paso: ponerlos por cada llamada externa o espera, basados en el comportamiento normal del servicio
Cuando un timeout se active, decidí el comportamiento por adelantado. Opciones simples: marcar como atascado y alertar a alguien, reintentar si es seguro, o detener y pedir revisión.
Razones comunes por las que los trabajos se atascan (sin jerga técnica)
La mayoría de los trabajos atascados no son misteriosos. Usualmente están esperando algo, bloqueados por algo o repitiendo el mismo paso sin avanzar.
Cuatro causas aparecen una y otra vez:
- El worker se detiene a mitad. La máquina que corre el trabajo puede crashar, reiniciarse, quedarse sin memoria o perder red. El trabajo ya arrancó, así que parece "en progreso", pero nadie lo está ejecutando.
- Espera a otro servicio que no responde. Proveedores de email, pasarelas de pago, APIs de IA, almacenamiento de archivos o sistemas asociados. Si el servicio se cuelga (no da un error limpio), el trabajo puede quedarse ahí para siempre a menos que pongas un límite.
- Algo lo bloquea en la base de datos. Un trabajo puede intentar actualizar un registro mientras otro proceso tiene un lock. Desde fuera parece inactivo. Dentro está atascado en un embotellamiento.
- El trabajo entra en un bucle o repite lo mismo. Un bug pequeño puede causar reintentos infinitos, procesamiento duplicado o un ciclo que nunca llega a "done". Técnicamente está corriendo, pero no avanza.
Una comprobación rápida: ¿dejó de hacer progreso o se detuvo por completo? Si un trabajo está marcado como en ejecución pero "filas procesadas" no cambió en 20 minutos, estás ante una espera, un bloqueo o un bucle.
Alertas que ayudan a actuar
Una alerta solo sirve si le dice a una persona real qué hacer después. Si solo dice «error del worker» a las 2 a.m., la gente la silencia y perdes el propósito del monitoreo.
Empezá con un canal que ya miren, como email o chat de equipo. Agregá escalado luego, cuando confíes en las alertas.
Alertá solo sobre estados en los que alguien pueda actuar: un trabajo está atascado (sin heartbeat por X minutos), un trabajo falló varias veces seguidas, o la cola se está acumulando (por ejemplo, el trabajo más antiguo tiene más de 15 minutos). Evitá alertas de "FYI" como "trabajo iniciado" salvo que estés debuggeando.
Cada alerta debería incluir contexto suficiente para decidir rápido:
- Nombre del trabajo y a qué feature pertenece
- Usuario o cuenta afectada (si aplica)
- Hora de inicio y última hora de heartbeat
- Último paso conocido (por ejemplo: "Generating PDF")
- Responsable y primera acción
Hacé la primera acción explícita y aburrida. Ejemplo: "Owner: Soporte. Primera acción: reintentar una vez. Si falla otra vez, escalar a Ingeniería con este ID de trabajo."
Paso a paso: hacer los trabajos diagnosables en un día
Podés hacer la detección real de trabajos atascados en un día si lo mantenés simple: definí cómo se ve "saludable" y luego medilo.
Empezá listando los pocos trabajos en segundo plano que causarían daño si se detuvieran silenciosamente (sincronización de facturación, informes nocturnos, envíos de email, importaciones de archivos). Para cada uno, definí qué significa "hecho" en términos simples: una fila guardada, un informe entregado, un lote de emails completado, un archivo marcado como procesado.
Después, agregá un heartbeat. Es solo una marca de tiempo que tu trabajo actualiza mientras trabaja. Actualizala al inicio, ocasionalmente durante el progreso y otra vez al final. Ahora "no se está ejecutando" se vuelve "el último heartbeat fue hace 23 minutos mientras procesaba el paso 3."
Luego fijá una regla de timeout por trabajo. Basala en heartbeats faltantes para trabajos largos o en tiempo máximo de ejecución para trabajos cortos. Elegí algo realista según comportamiento normal, no optimista.
Finalmente, da un solo lugar para mirar. Cuatro estados alcanzan:
- Running (heartbeat reciente)
- Done (registro de finalización)
- Failed (error registrado)
- Stuck (timeout activado)
Probá que funcione
Forzalo: pará el worker a mitad de tarea o simulá un crash. Confirmá dos cosas: el trabajo aparece como atascado y la alerta incluye cuál es el trabajo, cuándo empezó y la hora del último heartbeat.
Una vez en marcha, ya no estás adivinando. Estás observando.
Errores comunes y trampas a evitar
La meta es simple: cuando algo deja de moverse, te enterás rápido y sabés qué hacer. Los equipos suelen fallar porque las señales se ven "verdes" hasta que un cliente se queja.
Una trampa común es tratar una única señal de "iniciado" como un heartbeat. Si tu worker reporta solo al comienzo, una congelación a mitad puede verse sana durante horas.
Los timeouts también fallan cuando se fijan por suposición. Si son más estrictos que las ejecuciones normales lentas (informes de fin de mes, importes grandes, pico de tráfico), recibirás alertas por trabajo que habría terminado bien. La gente aprende a ignorarlas, y eso anula el propósito.
Los reintentos son otra fuente de problemas silenciosos. Si un trabajo puede ejecutarse dos veces y causar un segundo cobro, un correo duplicado o un reembolso doble, el auto-retry transforma un fallo en un rompecabezas para soporte.
Las alertas son difíciles de actuar cuando les faltan lo básico: qué cliente está afectado, qué paso alcanzó, quién lo posee y si alguien ya la reconoció.
Una comprobación útil: si una alerta despierta a alguien, esa persona debería poder responder «qué se rompió, quién está afectado y cuál es la siguiente acción segura» en menos de dos minutos. Si no, la alerta no está bien.
Checklist rápido antes de desplegar monitoreo
Antes de implementarlo, apuntá a un resultado: cuando alguien diga «no se está ejecutando», poder responder qué se detuvo, cuándo y qué hacer después.
- Para cualquier trabajo marcado como "running", ¿podés ver rápido cuándo se registró su último check-in?
- ¿Tenés una regla simple para "atascado", escrita en una sola frase (por ejemplo: "no check-in en 10 minutos significa atascado")?
- Cuando una alerta se dispara, ¿dice qué trabajo, qué cliente o workspace, cuándo progresó por última vez y el último paso completado?
- Si reintentás, ¿estás protegido contra duplicados (doble cobro, enviar el mismo email dos veces, crear dos informes)?
- En emergencia, ¿puede alguien pausar o relanzar el trabajo sin tocar código, y está claro quién puede hacerlo?
Una prueba práctica: pedí a una persona no técnica que lea una alerta y explique qué haría después. Si no puede, la alerta no está lista.
Escenario de ejemplo: el informe perdido que en realidad estaba atascado
Un fundador hace clic en "Generate report" para un cliente, cierra la pestaña y espera. Diez minutos después, no llega el correo ni aparece en la app. El mensaje de soporte es breve: "No se está ejecutando."
Con detección de trabajos atascados, el panel cuenta una historia más clara:
- Job ID #18422 inició a las 10:03
- Paso actual: "Export to PDF"
- Último heartbeat: hace 18 minutos
- Estado: Stuck (se esperaba heartbeat cada 60 segundos)
Una alerta útil llega al lugar correcto y dice lo que importa:
- El trabajo de informe está atascado en "Export to PDF"
- Último progreso: 12,430 filas procesadas
- Cuenta afectada: Acme Co
- Acción segura: reintentar desde el paso de exportación (sin doble cobro, sin emails duplicados)
Ahora la persona de guardia tiene un camino limpio. Primero, reinicia el trabajo de forma segura, usando un reintento que reutiliza el mismo registro de salida en lugar de crear un segundo informe. El cliente recibe su informe.
Luego se arregla la causa. En este caso suele ser una petición dentro del paso de exportación que nunca devuelve, o un lock en la base de datos que congela la consulta hasta que el worker se rinde.
La próxima vez es más calmado porque el trabajo se divide en pasos claros y cada paso tiene su propio timeout.
Próximos pasos: hacer tus trabajos confiables y fáciles de soportar
Empezá pequeño. Elegí el trabajo en segundo plano que más problemas cause (informes perdidos, facturas falladas, emails retrasados, importaciones). Agregá heartbeats y una regla de timeout a ese trabajo primero. Aprenderás más de un trabajo bien instrumentado que de un monitoreo ligero en todas partes.
Escribí qué significa "saludable" en lenguaje claro para que cualquiera lo juzgue de un vistazo. Ejemplo: "Este trabajo debe actualizar su heartbeat al menos una vez por minuto y terminar en 8 minutos. Si entra en timeout, debe alertar." Esa frase se vuelve tu definición compartida de normal.
Si tu base de código empezó como un prototipo generado por IA, asumí que hay casos borde ocultos aunque en general funcione. Las fallas suelen aparecer como trabajos atascados porque un worker espera para siempre, falla silenciosamente o reintenta de forma que ensucia los datos.
Si estás lidiando con una app generada por IA que se cuelga en segundo plano y querés un camino rápido y práctico a producción, FixMyMess (fixmymess.ai) se enfoca en diagnosticar y reparar problemas como heartbeats faltantes, reintentos inseguros y lógica de workers rota para que tus trabajos sean observables y soportables.
Una vez estables los básicos, la mejora siguiente suele ser: reintentos más seguros (que se puedan ejecutar dos veces sin problemas), una página de estado simple para salud de trabajos y verificaciones de despliegue que detecten problemas de configuración antes de llegar a producción.
Preguntas Frecuentes
What exactly is a background job?
Un trabajo en segundo plano es trabajo que tu aplicación hace después de que el usuario hace una acción, sin mantenerlo en una pantalla de carga. Ejemplos comunes: generación de informes, importación de CSV, exportación de PDF y envío de correos electrónicos.
Why does “it’s not running” feel so hard to debug?
Porque desde fuera tres situaciones distintas pueden parecer idénticas: el trabajo está lento, falló o está atascado. Sin señales como actualizaciones de progreso o marcas de tiempo, no puedes saber cuál es el caso.
How can I tell if a job is slow rather than stuck?
Lento significa que sigue avanzando, solo que tarda más de lo esperado. Verás evidencia como marcas de tiempo actualizadas, contadores que aumentan o nuevas entradas de log/progreso.
What’s the difference between a failed job and a stuck job?
Un trabajo fallido se ha detenido y no se completará por sí solo. Termina con un error o alcanza un problema serio; la única forma de completarlo es corregir la causa y volver a ejecutarlo de forma segura.
What is a heartbeat and why do I need it?
Un heartbeat es una pequeña actualización de “prueba de vida” que el trabajo escribe mientras se ejecuta, como una marca de tiempo o un contador procesado. Si los heartbeats dejan de actualizarse, tienes una señal clara de que el trabajo no está progresando.
What’s the simplest way to add heartbeats without overengineering?
Registra una única fila simple por ejecución del trabajo, con campos como last_heartbeat_at, current_step y un número de progreso básico. Actualízala solo después de completar una unidad real de trabajo para que refleje progreso verdadero y no un bucle que gira sin hacer nada.
How do I choose timeouts that won’t spam me with false alerts?
Empieza con un timeout suave que marque “más lento de lo normal” y un timeout duro que declare “está atascado”. Una regla práctica: el soft timeout alrededor de 1.5–2× tu ejecución normal más lenta, y el hard timeout alrededor de 3–5×.
What are the most common real reasons jobs get stuck?
Causas comunes: el trabajador se cae a mitad de ejecución, un servicio externo se cuelga sin responder, un bloqueo en la base de datos impide actualizaciones o un bug hace que el trabajo repita el mismo paso. Los heartbeats junto con un campo de “último paso” suelen indicar a qué categoría pertenece.
What should a good stuck-job alert include?
Incluye nombre del trabajo, usuario/cuenta afectada, hora de inicio, última marca de vida (last heartbeat), último paso conocido y la primera acción segura a tomar (reintentar una vez, pausar o escalar). Si la alerta no dice qué hacer, se ignorará.
What’s a one-day plan to make stuck jobs diagnosable?
Empieza por el trabajo que más dolor cause y define en lenguaje claro qué significa “hecho”. Añade un heartbeat, establece una regla de timeout y muestra cuatro estados: Running, Done, Failed, Stuck. Con eso cualquiera puede ver rápidamente qué pasa.