Особенности работы на примере PHP
В MongoDB еще в версии 4.2 добавили честные ACID транзакции. MongoDB - это уже не просто key-value хранилище наподобие Redis (как некоторые используют), а все же более серьезная база данных.
Многие разработчики до сих пор скептически относятся к NoSQL базам в качестве основной базы. Если рассматривать монгу, то да, сехмы все еще приходится контролировать на стороне клиента и описывать в приложении через разные ORM, Data Mappers и Active Records библиотеки.
У нас на проекте MongoDB используется как основная база данных и пока с ней не было проблем. Я уже привык к агрегатам вместо сложных SQL запросов, а так же привык описывать схемы в коде и контролировать целостность базы через приложение.
Одно из интересных преимуществ монги для стартапа - не нужно заморачиваться с миграциями. Только миграции на инициализацию индексов. Вы выкатываете код и новый код уже работает с новой схемой. Правда в коде нужно дополнительно создавать проверки на наличие того или иного поля. Но может это и не так уж и плохо.
Возвращаясь к транзакциям...
В PHP на данный момент офицальный драйвер - это MongoDB Driver. Не всегда очевидно как с ним работать, поэтому для своих проектов написал обертку для более удобного манипулирования.
Когда вы почитаете документацию и начнете работать с транзакциями, вдруг выяснится что они не работают, но при этом никаких ошибок не выдается... Первая ошибка, которая встречается - это то, что транзакционную сессию нужно передавать во все запросы, которые должны попасть в транзакцию:
<?php declare(strict_types=1);
// ...
try
{
$session = $client->startSession();
$session->startTransaction();
$db->vacancies->delete(
['_creator' => $user_id,
['session' => $session]
);
$db->users->deleteOne(
['_id' => $user_id,
['session' => $session]
);
$session->commitTransaction();
}
catch(Exception $e)
{
$session->abortTransaction();
}
// ...
Но когда вы напишите правильно запросы, может оказаться что ваша база данных не поддерживает транзакции, если база запущена в single mode, по сути по дефолту, не в режиме replica set и будет выдавать что-то типа:
transactions are available for replica sets only
Если вы используете Docker с официального репозитория, то инициализировать MongoDB в докер контейнере через docker-compose можно следующим образом:
version : '3'
services:
#
# MongoDB
#
mongo:
image: mongo:4.2.3-bionic
hostname: mongo
container_name: mongo
command: --auth --replSet rs0
restart: always
environment:
TZ: "Europe/Moscow"
MONGO_INITDB_ROOT_USERNAME: "${MONGO_USER}"
MONGO_INITDB_ROOT_PASSWORD: "${MONGO_PASS}"
ports:
- 127.0.0.1:27017:27017
#EOF#
Собственно через команду
command: --replSet rs0
по сути мы устанавливаем имя нашего сервера rs0.
После этого запускаем контейнер:
docker-compose up -d
Проверить настройки сервера можно командой:
// in mongo shell:
db.runCommand({getCmdLineOpts:1});
Затем нужно всего один раз зайти в базу (можно через консоль, можно через GUI - как вы привыкли) и выполнить команду:
// in mongo shell
rs.initiate()
Вам должна показаться статистика, где будет показана конфигурация. В итоге вы получили кластер из одного сервера MongoDB.
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "mongo:27017",
"ok" : 1
}
Теперь можно проверить, что всё работает прямо в базе через запросы или в нашем PHP коде.
По идее после этого все должно заработать и у вас появится возможность использовать транзакции. Я так же рекомендую заглянуть в документацию и прочитать об ограничениях и возможностях транзакций.
Cannot create namespace in multi-document transaction
MongoDB славен тем, как я уже упоминал, что вы можете не создавать заранее коллекцию (таблицу в терминах реляционных СУБД), и она автоматически создастся при первой вставке.
НО! Если вы используете транзакции с несуществующими коллекциями, то вы получите сообщение об ошибке. В документации есть про это следующее:
Operations that affect the database catalog, such as creating or dropping a collection or an index, are not allowed in multi-document transactions. For example, a multi-document transaction cannot include an insert operation that would result in the creation of a new collection. See Restricted Operations.
Вам нужно заранее создать коллекцию.
db.createCollection('collection_name');