2012-09-05 18 views
5

Mi configuración:Node.js + + mongo actualización atómica de múltiples entidades = dolor de cabeza

  1. Node.js
  2. Mongojs
  3. Una base de datos simple que contiene dos colecciones - inventario y facturas.
  4. Los usuarios pueden crear simultáneamente facturas.
  5. Una factura puede hacer referencia a varios artículos del inventario.

Mi problema:

Mantener la integridad del inventario. Imagine un escenario donde dos usuarios envían dos facturas con conjuntos de elementos superpuestos.

Una implementación ingenua (y mal) haría lo siguiente:

  1. Para cada elemento de la factura leyó el elemento correspondiente de la colección de inventario.
  2. Corrija la cantidad de elementos del inventario.
  3. Si cualquier cantidad de elementos está por debajo de cero, abandone la solicitud con el mensaje correspondiente para el usuario.
  4. Guarde los artículos del inventario.
  5. Guarde la factura.

Obviamente, esta implementación es mala, porque las acciones de los dos usuarios van a intercalarse y afectarse entre sí. En un servidor de bloqueo típico + base de datos relacional, esto se resuelve con complejos esquemas de bloqueo/transacción.

¿Cuál es la manera de + de la manera de pasar el tiempo? ¿Hay alguna herramienta que la plataforma node.js provea para este tipo de cosas?

+0

¿Has mirado en las dos fases sugerido MongoDB-commit enfoque descrito [aquí] (http://cookbook.mongodb.org/patterns/perform-two-phase-commits/)? – JohnnyHK

Respuesta

5

Puede ver un enfoque de compromiso en dos fases con MongoDB, o puede olvidarse por completo de las transacciones y desacoplar sus procesos mediante un enfoque de bus de servicio. Use Amazon como ejemplo: le permitirán enviar su pedido, pero no lo confirmarán hasta que hayan podido asegurar su artículo de inventario, cargado su tarjeta, etc. Nada de esto ocurre en una sola transacción: es un serie de pasos que pueden ocurrir en forma aislada y que pueden tener pasos de compensación aplicados cuando sea necesario.

Una implementación ingenua bus haría lo siguiente (tenga en cuenta que esto es sólo una sugerencia genérica para que pueda trabajar desde y la ejecución exacta dependerá de sus necesidades específicas de concurrencia, etc.):

  1. coloca el pedido en la cola. En este punto, puede continuar esperando a su cliente, o puede agradecerles por su orden y hacerles saber que recibirán un correo electrónico cuando haya sido procesado .
  2. un "trabajador de inventario" tomará el pedido y bloqueará el inventario artículos que necesita reservar. Esto se puede hacer de muchas maneras diferentes . Con Mongo puedes crear una colección que tenga un documento por pedido. Este documento debería tener como ID el ID del artículo de inventario y un TTL razonable (digamos 30 segundos).Mientras el trabajador tenga el candado, puede administrar los niveles de inventario de los artículos que tiene bloqueados. Una vez que su hizo sus cambios, podría eliminar el documento "bloquear".
  3. Si otro trabajador viene que quiere gestionar el mismo artículo mientras que su bloqueado, se puede poner el trabajador bloqueado en modo de suspensión durante X segundos y luego vuelva a intentar o, mejor aún, se puede poner la solicitud de nuevo en el el bus de mensajes será recogido más tarde por otro trabajador .
  4. Una vez que el trabajador haya resuelto todos los artículos del inventario, puede colocar otro mensaje en el bus de servicio que indique que se debe cargar una tarjeta , o el procesamiento debe recibir una notificación a extraer el inventario, o puede enviarse un correo electrónico enviada a la persona que hizo el orden, etc., etc.

sonidos complejos, pero una vez que tiene una configuración de bus de mensajes, es en realidad relativamente simple. A list of Node Message Bus Implementations can be found here.

Algunos desarrolladores saltearán completamente el bus de mensajes formal y utilizarán una base de datos como motor de transmisión de mensajes que puede funcionar en implementaciones simples. Google Mongo y colas

Si no espera más de 1 servidor y la implementación del bus de mensajes es demasiado voluminosa, node podría manejar el bloqueo y el envío de mensajes por usted. Por ejemplo, si realmente desea bloquear con un nodo, podría crear una matriz que almacenara los ID de los elementos del inventario. Aunque, para ser sincero, creo que el bus de mensajes es la mejor manera de hacerlo. De todos modos, aquí hay un código que he usado en el pasado para manejar el bloqueo de recursos externos simples con Node.

// attempt to take out a lock, if the lock exists, then place the callback into the array. 
this.getLock = function(id, cb) { 

     if(locks[id]) { 
      locks[id].push(cb); 
      return false; 
     } 
     else { 
      locks[id] = []; 
      return true; 
     } 
    }; 

// call freelock when done 
this.freeLock = function(that, id) { 
      async.forEach(locks[id], function(item, callback) { 
       item.apply(that,[id]); 
       callback(); 
      }, function(err){ 
       if(err) { 
        // do something on error 
       } 

       locks[id] = null; 

      }); 
     }; 
+0

Muchas gracias, se ve genial. – mark

+0

Hay algo que no entiendo: ¿qué se puede hacer si ocurre un error en su ejemplo? La parte donde se realiza el comentario "hacer algo por error". Quiero seguir con lo más simple ahora: un solo servidor y una solicitud regresa con el resultado, ya sea bueno o malo, pero sin trabajos largos. El servidor es un proceso de nodo único, entonces, ¿qué quiere decir con un trabajador de inventario? – mark

Cuestiones relacionadas