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
atrue
(a 'bloqueo' del documento de ser modificado) - Conjunto
data_old
ydata_new
a sus valores antiguos y nuevos - Establecer
change_complete
afalse
- Establecer
transaction_id
a la OBJECTID del documentotransaction
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
atrue
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
ydata_new
, ya que son ya no se necesita - conjunto
lock_status
afalse
(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)?
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
Creo que también puedes agregar matrices de DBrefs. – jsalonen
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