Проблема многоступенчатой ​​транзакции MongoDB - PullRequest
2 голосов
/ 15 июля 2011

Если нам нужно обновить документ CUSTOMER и отправить электронное письмо в той же транзакции, каков наилучший способ убедиться, что это делается атомарно? Мы создаем веб-сайт ecomm, и нам нужны эти функциональные возможности, поскольку, когда клиент приобретает товар, мы должны обновить его историю заказов и отправить ему подтверждение по электронной почте. В Java, использующей базу данных RDBMS, мы могли бы сделать это очень просто, просто обновив базу данных и отправив сообщение JMS, содержащее содержимое и детали электронной почты; JDBC и JMS поддерживают распределенные транзакции, поэтому любой из них можно откатить, если что-то пойдет не так, но с MongoDB это не так. Есть ли в Монго функция обмена сообщениями?

Мы думали об использовании флага «emailSentFlag» во встроенном документе orderHistory Заказчика. Когда заказ размещен, флаг устанавливается в false. Затем мы использовали бы внешнее задание, которое сканирует все истории заказов, где emailSentFlag = "false", и отправляет электронное письмо в этот момент, но это возвращает нас обратно в ту же ситуацию, так как после отправки мы должны были бы установить флаг в "true" адрес электронной почты, и это не атомарно.

> customer {
>     name:
>     email:
>     orderHistory{
>        orderId:
>        status:
>        emailSentFlag:

Ответы [ 4 ]

2 голосов
/ 16 июля 2011

Нет транзакций в Монго, как уже говорили другие.

Я бы порекомендовал вам создать почтовую очередь вместо установки флага, а затем выполнить поиск по всей коллекции. Я бы предпочел поместить запись в почтовую очередь, а затем обработать эту очередь, используя внешнее задание.

По крайней мере, так я бы попытался сделать это в первую очередь. Кто-нибудь, любой комментарий, хороший или плохой?

1 голос
/ 16 июля 2011

Стоит задуматься о том, действительно ли эти действия должны происходить атомарно:

  • Добавление строки истории заказов
  • Отправить подтверждение по электронной почте

Что быбыть критерием отката для отправки электронной почты?Неверный адрес электронной почты?Не удалось подключиться к почтовому серверу?Письмо отправлено, но не получено?Вы хотите отменить транзакцию клиента, потому что почтовый сервер не работает?

Я бы сказал, что можно создать новую строку заказа с customer.orderHistory.emailSentFlag, а затем назначить задание для обработки emailSentFlag = 0.Приказы на отправку писем с подтверждением, затем установите флажок после того, как электронное письмо было успешно отправлено.

Если вы получили отскок или доставка по какой-то причине не удалась, вы всегда можете сбросить флаг и попробовать снова.Или, что еще лучше, отправьте работу по электронной почте в систему почтовой очереди, которая обрабатывает все это для вас.

1 голос
/ 15 июля 2011

Вы можете выполнять атомарные операции на уровне ДОКУМЕНТА: http://www.mongodb.org/display/DOCS/Atomic+Operations

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

Вы можете даже использовать DB Ref из документа электронной почты для документа клиента.

0 голосов
/ 07 июня 2012

Это база данных без схемы, так что воспользуйтесь ей! :-) Вы можете использовать утку, но добавив поле к любому типу документа, который распознается по типам. Убедитесь, что вы используете имена, которые не используются в документе.

В любом документе с задачей (например, отправка электронного письма) добавьте массив tasks: []. Когда вы обновляете документ, и вам нужно, чтобы с ним связалась «атомарная» вещь, поместите задачу в эту очередь. Например, для электронной почты: { email: "cust@client.com", at: 0 }. Фоновый процесс опрашивает эти задачи, устанавливает срок их действия в будущем, выполняет их и удаляет их. Если что-то не получится, задание прекратит работу и будет перезапущено. Таким образом, вы можете отправить два письма, но это более или менее неизбежно для почты, даже для транзакций.

вы можете запустить это в оболочке mongodb:

#db.orders.remove()
db.orders.save( { name: "abc", tasks : [  { email: 'a@dom.com', at: 0}, { email: 'b@dom.com', at: 0} ] })
db.orders.save( { name: "def", tasks : [  { email: 'c@dom.com', at: 0}] })

now  = 10; future = 100; me = "some unique identifier for this scanning process"

while ( true ) {
  task = db.orders.findAndModify({
    query:   { "tasks.at": { $lt: now }},
    update : { $set : { "tasks.$.at": future, "tasks.$.who": me } }, 
    fields:  { "tasks":1},
    new:     true } )

  if ( !task)
    break;

  for ( var i in task.tasks ) {
    if ( task.tasks[i].who === me )
      print( "Send email " + task.tasks[i].email )  
  }

  db.orders.update( { _id: task._id }, { $pull: { tasks: { who: me } } } )
}

Посмотрите на http://www.mongodb.org/display/DOCS/findAndModify+Command аналогичный пример.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...