Особенности работы на примере 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');