En el verano de 2018 (es decir, en este momento, en el momento de escribir este artículo), sucedió lo increíble: las transacciones honestas de ACID se llevaron a MongoDB . Con el lanzamiento de la cuarta versión de este DBMS orientado a documentos, se puede utilizar para aplicaciones un poco más serias.
Para aquellos que están en el tanque, en pocas palabras: las transacciones nos permiten realizar una serie de cambios en varios documentos y guardarlos a la vez, o también cancelar todos los cambios realizados dentro de la transacción de la misma manera si algo salió mal o la aplicación falla .
Desafortunadamente, el desarrollador no es tan fácil de usar esta súper función. A continuación le diré por qué y qué hacer al respecto.
Si abrimos la documentación para el DBMS en la sección Transacciones , podemos ver el siguiente comentario:
Las transacciones de varios documentos están disponibles solo para conjuntos de réplicas. Las transacciones para clústeres fragmentados están programadas para MongoDB 4.2
Esto nos dice que un servidor MongoDB simple no admite transacciones, solo un clúster en modo conjunto de réplica . El soporte para clústeres fragmentados también vendrá más adelante en la versión 4.2.
Al mismo tiempo, un servidor normal nos permitirá iniciar una transacción, guardarla o cancelarla, pero no se puede hacer nada dentro de ella, se mostrará un error como este:
WriteCommandError({ : 0, : , : 20, : })
Afortunadamente, cualquiera puede ejecutar un clúster MongoDB de servidor único. En mis máquinas, en las que me dedico al desarrollo, ejecuto todos los DBMS en contenedores acoplables . Por ejemplo, iniciar un servidor MongoDB normal se ve así:
docker run -v ~/mongo/:/data/db --name mongo --restart=always -p 27017:27017 -d mongo mongod --smallfiles
Analicemos las claves de inicio:
- -v ~ / mongo /: / data / db significa montar el directorio local ~ / mongo / in / data / db del contenedor, por lo que la base de datos se almacenará en la máquina host, lo que nos permitirá eliminar el contenedor en ejecución, actualizar las versiones, etc. d. con la preservación de nuestros datos;
- --name mongo establece el nombre del contenedor;
- --restart = siempre dice que en caso de que un servicio se bloquee en el contenedor, debe reiniciarse, así como también iniciar el contenedor después de cargar el sistema operativo;
- -p 27017: 27017 "reenvía" el puerto a la máquina host;
- -d indica que necesitas iniciar el contenedor como un demonio;
- mongo : el nombre de la imagen para ejecutar el contenedor;
- mongod --smallfiles - comando para iniciar el servicio en el contenedor.
Cómo iniciar un servidor simple, lo traje solo como referencia. Ahora comprendamos qué se debe hacer para iniciar un servidor que admita transacciones.
En primer lugar, debe crear una nueva red dentro del acoplador, en la que funcionarán todos los servidores de nuestro clúster. Sí, escribí anteriormente que habrá un servidor, pero se debe crear una red, de lo contrario, nada funcionará.
docker network create mongo-cluster
A continuación, en los parámetros de inicio del contenedor, debe especificar el uso de la nueva red --net mongo-cluster , y también pasar el parámetro al servidor para que funcione en modo de configuración de réplica: --replSet rs0 . Además, omití intencionalmente --restart = cambiar siempre , ya que No siempre uso MongoDB en el trabajo en este momento y no quiero que comience con el sistema operativo.
docker run -v ~/mongo/:/data/db --name mongo -p 27017:27017 -d mongo mongod --smallfiles --replSet rs0
Genial, el contenedor se está ejecutando, como podemos ver ejecutando el comando docker ps y viendo algo como lo siguiente:
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2292d7e0778b mongo "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:27017->27017/tcp mongo
A continuación, debemos inicializar el clúster, para esto vamos a la consola del servidor en ejecución, creamos la configuración de nuestro clúster y realizamos la inicialización:
docker exec -it mongo mongo
Hecho Tenemos un clúster de un servidor MongoDB. Ahora puede verificar que todo funcione como se esperaba.
rs0:PRIMARY> session = db.getMongo().startSession() session { "id" : UUID("7eb81006-983f-4398-adc7-5ed23e027377") } rs0:PRIMARY> database = session.getDatabase("test") test rs0:PRIMARY> // rs0:PRIMARY> database.col.insert({name: "1"}) WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> database.col.insert({name: "2"}) WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> database.col.insert({name: "3"}) WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> database.col.insert({name: "4"}) WriteResult({ "nInserted" : 1 }) rs0:PRIMARY> // , rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "4" } rs0:PRIMARY> // rs0:PRIMARY> session.startTransaction() rs0:PRIMARY> // rs0:PRIMARY> database.col.update({name: "4"}, {name: "44"}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs0:PRIMARY> // rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "44" } rs0:PRIMARY> // , -: rs0:PRIMARY> // { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "4" } rs0:PRIMARY> // rs0:PRIMARY> session.commitTransaction() rs0:PRIMARY> // rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "44" } rs0:PRIMARY> // rs0:PRIMARY> session.startTransaction() rs0:PRIMARY> database.col.update({name: "44"}, {name: "42"}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> database.col.update({name: "1"}, {name: "21"}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "21" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> session.commitTransaction() rs0:PRIMARY> // rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "21" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> // , rs0:PRIMARY> session.startTransaction() rs0:PRIMARY> database.col.update({name: "21"}, {name: "1"}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "1" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> // rs0:PRIMARY> session.abortTransaction() rs0:PRIMARY> database.col.find({}) { "_id" : ObjectId("5b45026edc396f534f11952f"), "name" : "21" } { "_id" : ObjectId("5b450272dc396f534f119530"), "name" : "2" } { "_id" : ObjectId("5b450274dc396f534f119531"), "name" : "3" } { "_id" : ObjectId("5b450276dc396f534f119532"), "name" : "42" } rs0:PRIMARY> // ! ! rs0:PRIMARY>
Por lo tanto, sin esfuerzo, puede probar las transacciones de Mong ahora mismo sin iniciar un clúster multiservidor. Le aconsejo que consulte la documentación y lea sobre las restricciones de transacción. Por ejemplo, las transacciones "en vivo" durante no más de 1 minuto, si no logra guardar los cambios, se cancelarán.
PD: el propósito de este artículo no es enseñarte cómo usar Docker o trabajar con monga, sino solo una forma rápida de probar nuevas herramientas de este interesante DBMS.