2012-09-19 29 views
12

Me doy cuenta de que MongoDB, por su propia naturaleza, no admite, y probablemente nunca lo hará, este tipo de transacciones. Sin embargo, he descubierto que necesito usarlos de una manera un tanto limitada, así que se me ocurrió la siguiente solución, y me pregunto: es esta la mejor manera de hacerlo, y se puede mejorar ¿sobre? (antes de ir a implementarlo en mi aplicación!)Multi-colección, 'transacciones' multi-documentos en MongoDB

Obviamente, la transacción se controla a través de la aplicación (en mi caso, una aplicación web de Python). Para cada documento en esta transacción (en cualquier colección), se añaden los siguientes campos:

'lock_status': bool (true = locked, false = unlocked), 
'data_old': dict (of any old values - current values really - that are being changed), 
'data_new': dict (of values replacing the old (current) values - should be an identical list to data_old), 
'change_complete': bool (true = the update to this specific document has occurred and was successful), 
'transaction_id': ObjectId of the parent transaction 

Además, hay una colección transaction que almacena documentos detallando cada transacción en curso. Se parecen a:

{ 
    '_id': ObjectId, 
    'date_added': datetime, 
    'status': bool (true = all changes successful, false = in progress), 
    'collections': array of collection names involved in the transaction 
} 

Y esta es la lógica del proceso. Afortunadamente funciona de tal manera que si se interrumpe o falla de alguna otra manera, se puede revertir correctamente.

1: Establecer un documento transaction

2: Para cada documento que se ve afectada por esta transacción:

  • Conjunto lock_status a true (a 'bloqueo' del documento de ser modificado)
  • Conjunto data_old y data_new a sus valores antiguos y nuevos
  • Establecer change_complete a false
  • Establecer transaction_id a la OBJECTID del documento transaction que acaba de hacer

3: realizar la actualización.Para cada documento afectados:

  • Reemplazar todos los campos afectados en ese documento con los data_new valores
  • Establecer change_complete a true

4: Establecer la transaction de status a true (como todos los datos del documento ha sido modificado con éxito)

5: Para cada documento afectadas por la transacción, hacer algo de limpieza:

  • retire la data_old y data_new, ya que son ya no se necesita
  • conjunto lock_status a false (desbloquear el documento)

6:transaction retirar el documento creado en el paso 1 (o como se sugiere, lo marca como completa)


Creo que lógicamente funciona de tal manera que si falla en cualquier punto, todos los datos se pueden deshacer o la transacción puede continuarse (dependiendo de lo que se quiera hacer). Obviamente, todas las reversiones/recuperaciones/etc. lo realiza la aplicación y no la base de datos, utilizando los documentos transaction y los documentos en las otras colecciones con ese transaction_id.

¿Hay algún error flagrante en esta lógica que haya olvidado o pasado por alto? ¿Hay una manera más eficiente de hacerlo (por ejemplo, menos escritura/lectura de la base de datos)?

Respuesta

10

Como respuesta genérica, las confirmaciones de múltiples documentos en MongoDB se pueden realizar como confirmaciones de dos fases, que se han documentado de forma bastante exhaustiva en el manual (Consulte: http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/).

El patrón sugiere el manual es brevemente a continuación:

  • realizar una recogida separada transactions, que incluye documento de destino, documento de origen, valor y estado (de la transacción)
  • Crear nuevo objeto de transacción con initial como state
  • empezar a hacer una transacción y actualizar state a pending
  • Aplicar transacciones a ambos documentos (destino, fuente)
  • transacción de actualización de estado a committed
  • Uso encontrar para determinar si los documentos reflejan el estado de la transacción, si bien, transacción de actualización estado de done

Además:

  • Usted que manejar manualmente escenarios de fallo (algo que no ocurrió como se describe más adelante)
  • Es necesario aplicar manualmente una operación de deshacer, básicamente, mediante la introducción de un nombre state valor canceling

Algunas notas específicas para su aplicación:

  • No recomendaría que agregue campos como lock_status, data_old, data_new en documentos de origen/destino. Estas deberían ser propiedades de las transacciones, no los documentos en sí.
  • para generalizar el concepto de documentos de destino/origen, creo que se podría utilizar DBref s: http://www.mongodb.org/display/DOCS/Database+References
  • no me gusta la idea de borrar documentos de la transacción cuando se hacen. Establecer el estado en done parece una mejor idea ya que esto le permite depurar más adelante y descubrir qué tipo de transacciones se han realizado. Estoy bastante seguro de que tampoco te quedarás sin espacio en el disco (y para esto también hay soluciones).
  • En su modelo, ¿cómo se garantiza que todo ha cambiado como se esperaba? ¿Inspecciona los cambios de alguna manera?
+1

La sugerencia de 'DBrefs' es buena. Pero ¿funcionará ese método de confirmación en 2 fases con múltiples documentos (sin ensuciarse?) - en mi caso, necesito actualizar 1 documento en una colección yn documentos en otra colección (el número varía), al mismo tiempo. – johneth

+1

Creo que también puedes agregar matrices de DBrefs. – jsalonen

+0

Tomaré su consejo y no borraré documentos de transacción. La garantía de que las cosas han cambiado según lo esperado, de documento a documento, se hace usando el campo 'change_complete'. – johneth