Regla
Liberar bloquea incluso en excepción caminos.
Cada bloqueo adquisición debe tener a garantizado
liberación, incluso cuando excepciones excepciones.
Idiomas compatibles lenguajes:** Java, C, C++, PHP, JavaScript,
TypeScript, Go, PythonIntroducción
Los bloqueos no liberados son una de las causas más comunes de interbloqueos (deadlocks) y cuelgues del sistema en aplicaciones Node.js en producción. Cuando ocurre una excepción entre la adquisición y la liberación de un bloqueo, este permanece retenido indefinidamente. Otras operaciones asíncronas que esperan ese bloqueo se cuelgan para siempre, causando fallos en cascada en todo el sistema. Un solo mutex no liberado puede paralizar una API completa porque el bucle de eventos se bloquea y las solicitudes se acumulan. Esto ocurre con librerías como async-mutex, mutexify, o cualquier implementación manual de bloqueo donde la liberación no sea automática.
Por qué es importante
Estabilidad y disponibilidad del sistema: Los bloqueos no liberados causan interbloqueos que congelan las operaciones asíncronas en Node.js. En servidores Express o Fastify, esto agota los workers disponibles, haciendo que la aplicación sea incapaz de manejar nuevas solicitudes. La única recuperación es reiniciar el proceso, causando tiempo de inactividad. En arquitecturas de microservicios, los bloqueos no liberados en un servicio pueden provocar fallos en cascada en los servicios dependientes, ya que estos agotan su tiempo de espera para las respuestas.
Degradación del rendimiento: Antes del interbloqueo completo, los bloqueos no liberados causan graves problemas de rendimiento. Las operaciones asíncronas compiten por recursos bloqueados, creando una cola de promesas pendientes que nunca se resuelven. La contención de bloqueos crea picos de latencia impredecibles que degradan la experiencia del usuario. A medida que aumenta el número de solicitudes concurrentes bajo carga, la contención se agrava exponencialmente.
Complejidad de la depuración: Los interbloqueos (deadlocks) por bloqueos no liberados son notoriamente difíciles de depurar en aplicaciones Node.js en producción. Los síntomas aparecen lejos de la causa raíz, los procesos colgados muestran promesas pendientes pero no qué ruta de excepción no liberó el bloqueo. Reproducir la secuencia exacta de excepciones que desencadenaron el interbloqueo es a menudo imposible en entornos de desarrollo.
Agotamiento de recursos: Más allá de los propios bloqueos, la falta de liberación de estos a menudo se correlaciona con la falta de liberación de otros recursos como conexiones a bases de datos, clientes de Redis o manejadores de archivos. Esto agrava el problema, creando múltiples fugas de recursos que colapsan los sistemas más rápidamente bajo carga.
Ejemplos de código
❌ No conforme:
const { Mutex } = require('async-mutex');
const accountMutex = new Mutex();
async function transferFunds(from, to, amount) {
await accountMutex.acquire();
if (from.balance < amount) {
throw new Error('Insufficient funds');
}
from.balance -= amount;
to.balance += amount;
accountMutex.release();
}
Por qué es inseguro: Si se lanza el error de fondos insuficientes, accountMutex.release() nunca se ejecuta y el mutex permanece bloqueado indefinidamente. Todas las llamadas posteriores a transferFunds() se quedará colgado esperando el mutex, congelando todo el sistema de pagos.
✅ Conforme:
const { Mutex } = require('async-mutex');
const accountMutex = new Mutex();
async function transferFunds(from, to, amount) {
const release = await accountMutex.acquire();
try {
if (from.balance < amount) {
throw new Error('Insufficient funds');
}
from.balance -= amount;
to.balance += amount;
} catch (error) {
logger.error('Transfer failed', {
fromId: from.id,
toId: to.id,
amount,
error: error.message
});
throw error;
} finally {
release();
}
}Por qué es seguro: El detectar el bloque registra el error con contexto antes de relanzarlo, y el finalmente el bloque garantiza que la función de liberación del mutex se ejecuta tanto si la operación tiene éxito, como si lanza un error, o si el error se relanza desde el 'catch'. El bloqueo siempre se libera, evitando interbloqueos.
Conclusión
La liberación del bloqueo debe estar garantizada, no condicionada a una ejecución exitosa. Utilice try-finally bloques en JavaScript o el runExclusive() función de ayuda proporcionada por librerías como async-mutex. Cada adquisición de bloqueo debería tener una ruta de liberación incondicional visible en el mismo bloque de código. La gestión adecuada de bloqueos no es opcional; es la diferencia entre un sistema estable y uno que se cuelga aleatoriamente bajo carga.
.avif)
