Подтвердить что ты не робот

Операции Mongoose.js

Я знаю, что MongoDB не поддерживает транзакции, как реляционные базы данных, но мне все еще интересно, как добиться атомарности для нескольких операций. Охота в Интернете, я вижу, что люди упоминают Транзакции без транзакций. Чтение через слайды, я все еще не понимаю, как реализовать это с помощью Mongoose.js.

Возьмите этот фрагмент кода, например:

player.save(callback1);
story.save(callback2);

Как реализовать callback1 и callback2, чтобы они либо успешно выполнялись вместе, либо сбой вместе?

4b9b3361

Ответ 1

Если вы действительно должны иметь транзакции по нескольким типам документов (в отдельных коллекциях), то для достижения этой цели есть одна таблица, в которой хранятся действия.

db.actions.insert(
{ actions: [{collection: 'players', _id: 'p1', update: {$set : {name : 'bob'} } },
            {collection: 'stories', _id: 's1', update: {$set : {location: 'library'} } }], completed: false }, callback);

Эта вставка атомарна, и все делается сразу. Затем вы можете выполнять команды в коллекции "действия" и отмечать их как завершенные или удалять их по мере их завершения, вызывая ваш первоначальный обратный вызов, когда все будет завершено. Это работает только в том случае, если цикл обработки ваших действий - единственное, что обновляет db. Конечно, вам придется прекратить использовать мангуст, но чем скорее вы это сделаете, тем лучше вы все равно.

Ответ 2

Вы можете имитировать транзакцию путем ручного возврата изменений при возникновении ошибки. В приведенном выше примере просто удалите сохраненный документ, если другой не работает.

Вы можете сделать это легко, используя Async:

    function rollback (doc, cb) {
      doc.remove(cb);
    }

    async.parallel([
          player.save.bind(player),
          story.save.bind(story),
      ],
      function (err, results) {
        if (err) {
          async.each(results, rollback, function () {
            console.log('Rollback done.');
          });
        } else {
          console.log('Done.');
        }
      });

Очевидно, что сам откат может потерпеть неудачу - если это неприемлемо, вам, вероятно, придется реструктурировать свои данные или выбрать другую базу данных.

Примечание. Я подробно обсудил это сообщение .

Ответ 3

Этот вопрос довольно старый, но для тех, кто натыкается на эту страницу, вы можете использовать fawn. Это пакет npm, который решает эту проблему. Раскрытие: я написал его

Скажем, у вас есть два банковских счета, один принадлежит Джону Смиту, а другой принадлежит Broke Individual. Вы хотели бы перевести $20 от Джона Смита на Broke Individual. Предполагая, что все имена и фамилии являются уникальными, это может выглядеть так:

var Fawn = require("fawn");
var task = Fawn.Task()

//assuming "Accounts" is the Accounts collection 
task.update("Accounts", {firstName: "John", lastName: "Smith"}, {$inc: {balance: -20}})
  .update("Accounts", {firstName: "Broke", lastName: "Individual"}, {$inc: {balance: 20}})
  .run()
  .then(function(){
    //update is complete 
  })
  .catch(function(err){
    // Everything has been rolled back. 

    //log the error which caused the failure 
    console.log(err);
  });

Протест: задачи в настоящее время не изолированы (работают над этим), поэтому, технически, возможно, что две задачи могут извлекать и редактировать один и тот же документ только из-за того, как работает MongoDB.

Это действительно просто общая реализация примера двух фазовых ошибок на сайте учебника: https://docs.mongodb.com/manual/tutorial/perform-two-phase-commits/

Ответ 4

Транзакции не поддерживаются в MongoDB из коробки. Однако они могут быть реализованы с помощью двухфазного протокола фиксации. Здесь вы можете увидеть официальную рекомендацию о том, как сделать 2PC в MongoDB. Этот подход является весьма универсальным и может применяться к различным сценариям, таким как

  • документы из разных коллекций (ваш случай)
  • более 2 документов
  • пользовательские действия над документами

Ответ 5

Было предпринято несколько попыток реализовать решения. Ни один из них не был особенно активен на момент написания. Но, возможно, они работают отлично!

niahmiah README стоит посмотреть. Он отмечает некоторые недостатки использования транзакций, а именно:

  • Все обновления и удаления для соответствующих коллекций должны проходить через процессор транзакций, даже если вы не намеренно совершали транзакцию. Если вы этого не сделаете, транзакции могут не делать то, что они должны делать.
  • Эти обновления, а также транзакции, которые вы изначально хотели, будут значительно медленнее, чем обычно.

niahmiah также предполагает, что могут быть альтернативы использованию транзакций.


Просто совет: если вам не нравится идея блокировать часто используемые коллекции, вы можете рассмотреть возможность разделения чувствительных (транзакционных) данных от менее чувствительных данных.

Например, если вы в настоящее время держите credits в коллекции пользователей, то вам лучше отделить его на две коллекции: User и UserWallet и перемещение поля credits в кошельки. Затем вы можете свободно обновлять документы пользователя, не опасаясь вмешиваться в транзакции в коллекциях UserWallet.