es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Pregunta de entrevista sobre manejo de concurrencia en Node JS.

Asistí a una entrevista de codificación de NodeJS. Obtuve el siguiente código que se ejecuta de forma asíncrona desde diferentes navegadores (se supone). Nuestra solución necesita bloquear la ejecución de la función si la actualización por ID es la misma pero se llama desde un lugar diferente (digamos, desde un navegador). Y luego liberar el bloqueo para la ejecución de la siguiente solicitud.

Aquí no se deben realizar cambios en el código mencionado a continuación.

async function update(id, data) {
    console.log(`start -> id:${id}, data:${data}`);
    await randomDelay(); // La actualización está sucediendo aquí
    console.log(`end -> id:${id}, data:${data}`);
}

//=============================================================================
//================= No cambiar nada a continuación ===============================
//=============================================================================

//---- update() se está llamando desde muchos lugares ----
update(1, "navegador 1");
update(1, "navegador 2");

//========================= Funciones de utilidad ===================================
//========================= No cambies nada aquí================================

async function sleep(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(), ms);
    });
}

async function randomDelay() {
    const randomTime = Math.round(Math.random() * 1000);
    return sleep(randomTime);
}

Esto dará una salida como la siguiente.

start -> id:1, data:navegador 1
start -> id:1, data:navegador 2
end -> id:1, data:navegador 1
end -> id:1, data:navegador 2

La respuesta esperada es:

start -> id:1, data:navegador 1
end -> id:1, data:navegador 1
start -> id:1, data:navegador 2
end -> id:1, data:navegador 2

Ten en cuenta los comentarios en el código “No cambies nada a continuación”. ¿Cuál sería la solución posible?

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Para resolver este problema, necesitamos implementar un mecanismo de bloqueo para evitar la ejecución concurrente de la función update con el mismo parámetro id. Una forma de hacer esto es utilizando un hashmap y un cerrojo (mutex lock).

    Podemos crear un objeto hashmap accesible globalmente para almacenar el estado de los ids bloqueados. Cada vez que se llama a la función update, verificamos si el id ya está bloqueado. Si lo está, esperamos hasta que se libere el bloqueo antes de ejecutar la actualización. Si no lo está, adquirimos el bloqueo y procedemos con la actualización. Después de completar la actualización, liberamos el bloqueo para que las actualizaciones posteriores puedan ejecutarse.

    Aquí está el código modificado:

    const locks = {};
    
    async function update(id, data) {
      console.log(`inicio --> id:${id}, data:${data}`);
    
      if (locks[id]) {
        // el id está actualmente bloqueado, esperar hasta que se libere el bloqueo
        await locks[id];
      }
    
      // adquirir el bloqueo para este id
      const lock = new Promise(resolve => {
        locks[id] = resolve;
      });
    
      // ejecutar la actualización con un retraso aleatorio
      await randomDelay();
    
      console.log(`fin --> id:${id}, data:${data}`);
    
      // liberar el bloqueo para el id
      delete locks[id];
      lock.then(() => {});
    }
    
    //---- update() es llamado desde muchos lugares ----
    update(1, "navegador 1");
    update(1, "navegador 2");
    
    //========================= Funciones de utilidad ===================================
    //========================= No cambiar nada aquí ====================================
    
    async function sleep(ms) {
      return new Promise(resolve => {
        setTimeout(resolve, ms);
      });
    }
    
    async function randomDelay() {
      const randomTime = Math.round(Math.random() * 1000);
      return sleep(randomTime);
    }
    

    En este código modificado, primero definimos un objeto global locks para almacenar el estado de los ids bloqueados. La función update verifica si el id está bloqueado verificando si existe el valor locks[id]. Si existe, esperamos a que se libere el bloqueo usando await locks[id]. Una vez que se libera el bloqueo, procedemos a ejecutar la actualización. Antes de ejecutar la actualización, adquirimos el bloqueo para el id dado creando una nueva Promise y estableciéndola como el valor de locks[id]. Después de ejecutar la actualización, liberamos el bloqueo eliminando la entrada locks[id] y resolviendo la promesa del bloqueo con lock.then(() => {}).

    De esta manera, nos aseguramos de que las actualizaciones con el mismo parámetro id no se ejecuten de manera concurrente, sino que se ejecuten una a la vez en el orden en que se recibieron.

Comments are closed.