Como sentir as transações no MongoDB agora

No verão de 2018 (ou seja, no momento em que este artigo foi escrito), o incrível aconteceu - transações ACID honestas foram trazidas para o MongoDB . Com o lançamento da quarta versão deste DBMS orientado a documentos, ele pode ser usado para aplicativos um pouco mais sérios.


Para quem está no tanque, em poucas palavras: as transações nos permitem realizar uma série de alterações em vários documentos e salvá-las de uma só vez, ou também cancelar todas as alterações feitas na transação da mesma maneira, se algo der errado ou o aplicativo falhar .


Infelizmente, o desenvolvedor não é tão fácil de usar esse super recurso. Abaixo, vou lhe dizer por que e o que fazer sobre tudo isso.


Se abrirmos a documentação para o DBMS na seção Transações , podemos ver o seguinte comentário:


As transações com vários documentos estão disponíveis apenas para conjuntos de réplicas. As transações para clusters sharded estão agendadas para o MongoDB 4.2

Isso nos diz que um simples servidor MongoDB não suporta transações, apenas um cluster no modo de conjunto de réplicas . O suporte para clusters sharded também será disponibilizado posteriormente na versão 4.2.


Ao mesmo tempo, um servidor normal nos permitirá iniciar uma transação, salvá-la ou cancelá-la, mas nada pode ser feito dentro dela, um erro como este será exibido:


WriteCommandError({ "ok" : 0, "errmsg" : "Transaction numbers are only allowed on a replica set member or mongos", "code" : 20, "codeName" : "IllegalOperation" }) 

Felizmente, qualquer um pode executar um cluster MongoDB de servidor único. Nas minhas máquinas, nas quais estou envolvido no desenvolvimento, executo todos os DBMSs em contêineres de encaixe . Por exemplo, iniciar um servidor MongoDB comum é semelhante a este:


 docker run -v ~/mongo/:/data/db --name mongo --restart=always -p 27017:27017 -d mongo mongod --smallfiles 

Vamos analisar as chaves de inicialização:


  • -v ~ / mongo /: / data / db significa montar o diretório local ~ / mongo / in / data / db do contêiner, para que o próprio banco de dados seja armazenado na máquina host, o que nos permitirá excluir o contêiner em execução, atualizar as versões etc. d. com a preservação de nossos dados;
  • --name mongo define o nome do container;
  • --restart = sempre diz que, no caso de qualquer serviço travar no contêiner, ele deve ser reiniciado e iniciar o contêiner após o carregamento do sistema operacional;
  • -p 27017: 27017 "encaminha" a porta para a máquina host;
  • -d indica que você precisa iniciar o contêiner como um daemon;
  • mongo - o nome da imagem para executar o contêiner;
  • mongod --smallfiles - comando para iniciar o serviço no contêiner.

Como iniciar um servidor simples, eu trouxe apenas para referência. Agora vamos entender o que precisa ser feito para iniciar um servidor que suporta transações.


Antes de tudo, você deve criar uma nova rede dentro da janela de encaixe, na qual todos os servidores do nosso cluster funcionarão. Sim, escrevi acima que haverá um servidor, mas uma rede deve ser criada; caso contrário, nada funcionará.


 docker network create mongo-cluster 

Em seguida, nos parâmetros de inicialização do contêiner, é necessário especificar o uso da nova rede --net mongo-cluster e também passar o parâmetro para o servidor para trabalhar no modo de conjunto de réplicas: --replSet rs0 . Além disso, omiti intencionalmente a opção --restart = always , pois No momento, nem sempre uso o MongoDB no trabalho e não quero que ele comece com o sistema operacional.


 docker run -v ~/mongo/:/data/db --name mongo -p 27017:27017 -d mongo mongod --smallfiles --replSet rs0 

Ótimo, o contêiner está em execução, como podemos ver executando o comando docker ps e vendo algo como o seguinte:


 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 

Em seguida, precisamos inicializar o cluster, para isso vamos para o console do servidor em execução, criamos a configuração do nosso cluster e realizamos a inicialização:


 docker exec -it mongo mongo # output omited # > config = { "_id" : "rs0", "members" : [ { "_id" : 0, "host" : "mongo:27017" } ] } > rs.initiate(config) { "ok" : 1, "operationTime" : Timestamp(1531248932, 1), "$clusterTime" : { "clusterTime" : Timestamp(1531248932, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } } rs0:SECONDARY> rs0:PRIMARY> 

Feito! Temos um cluster de um servidor MongoDB. Agora você pode verificar se tudo está funcionando conforme o esperado.


 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> 

Portanto, sem sobrecarregar, você pode tentar transações Mong agora mesmo sem iniciar um cluster multiservidor. Aconselho que você consulte a documentação e leia sobre restrições de transação. Por exemplo, essas transações "ficam ativas" por não mais de um minuto; se você não conseguir salvar as alterações, elas serão canceladas.


PS: o objetivo deste artigo não é ensinar como usar o docker ou trabalhar com o monga, mas apenas uma maneira rápida de experimentar novas ferramentas desse interessante DBMS.

Source: https://habr.com/ru/post/pt417131/


All Articles