mongodb - query - sequelize repository pattern
No es posible bloquear un documento mongodb. ¿Qué pasa si necesito? (11)
Sé que no puedo bloquear un solo documento mongodb, de hecho, tampoco hay forma de bloquear una colección.
Sin embargo, tengo este escenario, donde creo que necesito alguna forma de evitar que más de un hilo (o proceso, no sea importante) modifique un documento. Aquí está mi escenario.
Tengo una colección que contiene un objeto de tipo A. Tengo un código que recupera un documento de tipo A, agrego un elemento en una matriz que es una propiedad del documento ( a.arr.add(new Thing()
) y luego guarde el documento en mongodb. Este código es paralelo, varios subprocesos en mis aplicaciones pueden realizar estas operaciones y por ahora no hay forma de evitar que los subprocesos realicen estas operaciones en paralelo en el mismo documento. Esto es malo porque uno de los Los hilos podrían sobrescribir las obras del otro.
Utilizo el patrón de repositorio para abstraer el acceso a la colección mongodb, por lo que solo tengo operaciones CRUD a mi disposición.
Ahora que lo pienso, tal vez sea una limitación del patrón de repositorio y no una limitación de mongodb lo que me está causando problemas. De todos modos, ¿cómo puedo hacer que este código sea seguro para subprocesos? Supongo que hay una solución bien conocida para este problema, pero como soy nuevo en mongodb y en el patrón del repositorio, no lo veo de inmediato.
Gracias
"Doctor, me duele cuando hago esto "
"Entonces no hagas eso!"
Básicamente, lo que estás describiendo suena como que tienes una dependencia en serie allí: MongoDB o lo que sea, tu algoritmo tiene un punto en el que la operación debe ser serializada. Ese será un cuello de botella inherente, y si es absolutamente necesario hacerlo, tendrá que organizar algún tipo de semáforo para protegerlo.
Entonces, el lugar para mirar es tu algoritmo. ¿Puedes eliminar eso? ¿Podría, por ejemplo, manejarlo con algún tipo de resolución de conflictos, como "obtener registro en la actualización local; almacenar registro" para que después de la tienda el nuevo registro sea el que se obtuvo con esa clave?
En lugar de escribir la pregunta en otra pregunta, trato de responder esta: me pregunto si este WiredTiger Storage manejará el problema que señalé aquí: Limitar las inserciones en mongodb
La solución clásica cuando se desea hacer algo seguro para subprocesos es utilizar bloqueos (mutexes). Esto también se conoce como bloqueo pesimista en lugar de bloqueo optimista descrito docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations .
Hay escenarios en los que el bloqueo pesimista es más eficiente (más detalles here ). También es mucho más fácil de implementar (la mayor dificultad del bloqueo optimista es la recuperación de una colisión).
MongoDB no proporciona un mecanismo para un bloqueo. Pero esto se puede implementar fácilmente a nivel de aplicación (es decir, en su código):
- Adquirir bloqueo
- Leer documento
- Modificar documento
- Escribir documento
- Desbloquear bloqueo
La granularidad del bloqueo puede ser diferente: global, específica de colección, específica de registro / documento. Cuanto más específico sea el bloqueo, menor será su penalización de rendimiento.
Oye, la única forma en la que creo que ahora es agregar un parámetro de estado y usar la operación findAndModify() , que te permite modificar un documento de forma atómica. Es un poco más lento, pero debería hacer el truco.
Así que digamos que agrega un atributo de estado y cuando recupera el documento, cambie el estado de "IDLE" a "PROCESSING". Luego actualiza el documento y lo guarda de nuevo en la colección, actualizando el estado a "IDLE" nuevamente.
Ejemplo de código:
var doc = db.runCommand({
"findAndModify" : "COLLECTION_NAME",
"query" : {"_id": "ID_DOCUMENT", "status" : "IDLE"},
"update" : {"$set" : {"status" : "RUNNING"} }
}).value
Cambie COLLECTION_NAME y ID_DOCUMENT a un valor adecuado. Por defecto, findAndModify () devuelve el valor anterior, lo que significa que el valor de estado seguirá siendo IDLE en el lado del cliente. Así que cuando haya terminado con la actualización, simplemente guarde / actualice todo de nuevo.
Lo único que debe tener en cuenta es que solo puede modificar un documento a la vez.
Espero eso ayude.
Parece que desea utilizar los operadores atómicos de MongoDB: mongodb.org/display/DOCS/Atomic+Operations
Respondiendo a mi propia pregunta porque encontré una solución al hacer una investigación en Internet.
Creo que lo que necesito hacer es usar un Control de Concurencia Optimista .
Consiste en agregar una marca de tiempo, un hash u otro identificador único (usaré UUID) a todos los documentos. El identificador único debe modificarse cada vez que se modifica el documento. Antes de actualizar el documento haré algo como esto (en pseudo-código):
var oldUUID = doc.uuid;
doc.uuid = new UUID();
BeginTransaction();
if (GetDocUUIDFromDatabase(doc.id) == oldUUID)
{
SaveToDatabase(doc);
Commit();
}
else
{
// Document was modified in the DB since we read it. We can''t save our changes.
RollBack();
throw new ConcurencyException();
}
Si el orden de los elementos en la matriz no es importante para usted, entonces el operador $push debería ser lo suficientemente seguro como para evitar que las hebras sobrescriban los demás cambios.
Si tiene un sistema con> 1 servidores, necesitará un bloqueo distributivo.
Prefiero usar Hazelcast .
Al guardar, puede obtener el bloqueo de Hazelcast por identificador de entidad, obtener y actualizar datos, luego liberar un bloqueo.
Solo usa lock.lock()
lugar de lock.tryLock()
Aquí puede ver cómo configurar Hazelcast en su contexto de primavera:
https://github.com/azee/template-api/blob/master/template-rest/src/main/resources/webContext.xml
Tropezó con esta pregunta mientras trabajaba en las actualizaciones de mongodb. A diferencia de cuando se hizo esta pregunta, ahora mongodb admite el bloqueo de nivel de documento fuera de la caja.
De: http://docs.mongodb.org/manual/faq/concurrency/
"¿Qué tan granulares son los bloqueos en MongoDB?
Cambiado en la versión 3.0.
A partir de la versión 3.0, MongoDB se entrega con el motor de almacenamiento WiredTiger, que utiliza el control de concurrencia optimista para la mayoría de las operaciones de lectura y escritura. WiredTiger utiliza solo bloqueos de intención en los niveles global, de base de datos y de colección. Cuando el motor de almacenamiento detecta conflictos entre dos operaciones, uno incurrirá en un conflicto de escritura que hace que MongoDB reintente de forma transparente esa operación ".
Una alternativa es hacer en su lugar la actualización.
por ej.
http://www.mongodb.org/display/DOCS/Updating#comment-41821928
db.users.update( { level: "Sourcerer" }, { ''$push'' : { ''inventory'' : ''magic wand''} }, false, true );
lo que empujará ''varita mágica'' a toda la matriz de inventario del usuario "Sourcerer". La actualización a cada documento / usuario es atómica.
Actualización: Con MongoDB 3.2.2 utilizando la implementación de WiredTiger Storage como motor predeterminado, MongoDB utiliza el bloqueo predeterminado en el nivel del documento. Se introdujo en la versión 3.0, pero se estableció de forma predeterminada en la versión 3.2.2. Por lo tanto, MongoDB ahora tiene bloqueo de nivel de documento.