2014-09-14 1 views
0

У меня есть совместный веб-приложение, которое обрабатывает JSON-объекты, такие как следующие:Как хранить операции json-patch в очереди redis и гарантировать их согласованность?

var post = { 
    id: 123, 
    title: 'Sterling Archer',  
    comments: [ 
    {text: 'Comment text', tags: ['tag1', 'tag2', 'tag3']}, 
    {text: 'Comment test', tags: ['tag2', 'tag5']} 
    ] 
}; 

Мой подход использует rfc6902 (JSONPatch) specification с jsonpatch библиотекой для исправления JSON документа. Все такие документы хранятся в базе данных MongoDB, и, как вы знаете, последняя очень медленная для частой записи.

Чтобы получить более высокую скорость и Высоконагруженные приложения я использую Redis в очереди за накладные операции, как следующее:

{ "op": "add", "path": "/comments/2", "value": {text: 'Comment test3', tags: ['tag4']}" } 

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

Я пока не понимаю, что я должен делать в случае повреждения патч, как: только 3

{ "op": "add", "path": "/comments/0/tags/5", "value": 'tag4'} 

Пластырь выше, не будет применяться к документу выше, потому что tags массив имеет длину (по данным официальной документации http://tools.ietf.org/html/rfc6902#page-5)

The specified index MUST NOT be greater than the number of elements in the array. 

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

Итак, мой вопрос: как я могу гарантировать, что все исправления, хранящиеся в очереди redis, верны и не повреждают основной документ?

+0

на высоком уровне, что бы вы предпочли, чтобы произойти в случае искаженного разлома? вы хотите попытаться исправить проблему или просто выбросить diff? – aembke

+0

Как пользователь создает плохой патч? – zenbeni

+0

Я не знаю. Мне нужно как-то предотвратить испорченный diff. Может быть, есть лучшие практики? – Erik

ответ

1

Как и любая система, которая может стать несовместимой, вы должны разрешить патчи применять как можно быстрее, если вы хотите скорее поймать конфликты и уменьшить вероятность их столкновения. Вероятно, это ваш основной вопрос, если вы не уведомите других клиентов о любых обновленных данных как можно скорее (и просто ожидаете, что CRON будет запущен для обновления общих данных, к которым могут обращаться другие клиенты).

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

  1. Пользователь применил некоторые операции, которые были потеряны при переводе. Как? Я не знаю, но это объясняет несоответствие.
  2. Операции не применяются в правильном порядке. Как? Я не знаю. У меня нет кода, чтобы уйти.

Хотя у меня нет кода, чтобы уйти, я могу сделать снимок в темноте и помочь вам проанализировать последний момент. Первое, что нам нужно проанализировать, это различные сценарии, которые могут возникнуть при обновлении «общего» ресурса. Важно отметить, что в любой системе, которая должна быть в конечном итоге последовательной, мы заботимся о:

  1. Порядок операций.
  2. Как мы будем иметь дело с конфликтами.

Последнее действительно зависит от вас, и вам понадобится хорошая система уведомлений/сообщений, чтобы обновить «правду», которую видят клиенты.

Сценарий 1

Пользователь A применяет операции 1 & 2. Документ обновляется на сервере, а затем пользователь B получает уведомление об этом. Пользователь B будет применять операции 3 & 4, но эти операции (в этом порядке) не конфликтуют с операциями 1 & 2. Все хорошо в мире. Это хорошая ситуация.

Сценарий 2

Пользователь A применяет операции 1 & 2. Пользователь B применяет операции 3 & 4.

Если применить операции атомарно для каждого пользователя, вы можете получить следующие очереди:

[1,2,3,4] [3,4,1,2]

В любом случае вдоль линии, если есть конфликт, вы должны уведомить пользователя A или пользователя B на основе «кто первым попал туда», (или любой другой весовой семант ics, который вы хотите использовать). Опять же, как вы справляетесь с конфликтами, зависит от вас. Если вы не читали на векторных часах, вы должны это сделать.

Если вы не применять операции атомарно для каждого пользователя, вы можете получить следующие очереди:

[1,2,3,4] [3,4,1,2] [1,3,2 , 4] [3,1,4,2] [3,1,2,4] [1,3,4,2]

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

повторение

Некоторые важные вещи, которые вы должны помнить:

  1. Убедитесь, что обновления в очереди атомарно применяется для каждого пользователя.
  2. Выясните, как вы будете иметь дело с несколькими версиями общего ресурса, возникающими из-за множественных мутаций от разных клиентов (опять же я предлагаю вам читать на векторных часах).
  3. Не обновляйте общий ресурс, к которому могут обращаться несколько клиентов в режиме реального времени в качестве задания cron.
  4. Когда есть конфликт, который не может быть разрешен, выясните, как вы с этим справитесь.
  5. В результате пункта 3 вам потребуется создать систему уведомлений, чтобы клиенты могли быстро получать обновленные ресурсы. В результате пункта 4 вы можете указать, чтобы сообщить клиентам, что что-то пошло не так с их обновлением. Что-то, что только что пришло мне в голову, это то, что вы уже используете Redis, у которого есть возможности pub/sub.

EDIT:

Похоже, Google Docs обрабатывает разрешения конфликтов с преобразованиями.То есть, переместив целые символы/линии, чтобы освободить место для гибридного применения всех операций: https://drive.googleblog.com/2010/09/whats-different-about-new-google-docs_22.html

Как я уже говорил, все зависит от того, как вы хотите справиться со своими собственными конфликтами, что в значительной степени должно быть определено самим приложением/продуктом и его вариантами использования.

+0

Спасибо за ответ. В настоящее время я использую номер ревизии и увеличиваю каждый патч. Поэтому, если пользователь отправляет неправильный номер rev, я бросаю его патч. Я понимаю ваше предложение и хочу попробовать Редис. что вы думаете по поводу следующего сценария: я храню свой JSON-документ в MongoDB (например), а при необходимости сохраняю в Redis (до пользователей онлайн) и после пользователей в автономном режиме я очищаю все данные Redis в MongoDB назад – Erik

+0

Я склонен соглашаться с @ Ответ Дхрува Патхака. Хотя я дал вам краткое описание того, как вы можете столкнуться с вашими проблемами, я думаю, вы должны начать просто и сделать что-то вроде Celery + Redis + Mongo. – Vinay

1

IMHO вы вводите ненужную сложность вместо более простого решения. Это будут мои альтернативные предложения вместо вашего подхода к json patch cron, который очень сложно сделать последовательным и атомарным.

  1. Использование MongoDB только: При правильном проектировании баз данных и индексации, а также надлежащего распределения hdarware/сегментирование, производительность при записи в MongoDB очень быстро. И виды операций, которые вы используете в jsonpatch, поддерживаются в файлах BSON mongodb и их языке запросов .eg $ push, $ set, $ inc, $ pull и т. Д. Возможно, вы хотите не прерывать действия пользователей с помощью синхронной записи в Mongodb, для этого решение использует async queus, как указано в пункте № 2.

    2. очередь Использование задачи & MongoDB: Вместо того, чтобы хранить патчи в Redis, как вы делаете сейчас, вы можете нажать задачу внесения исправлений в очередь задач, которая будет асинхронном сделать обновление MongoDB, и пользователь не будет испытывать какой-либо медленная производительность. Одна очень хорошая очередь задач - Celery, которая может использовать Redis в качестве брокера & обмена сообщениями. Таким образом, каждое обновление пользователей получает одну задачу и будет применяться к mongodb по очереди задач, и не будет никакого повышения производительности.

+0

Хотя я дал вам способы проанализировать ситуацию и потенциально решить проблемы согласованности, я согласен с этим ответом. – Vinay

+0

@DhruvPathak Мне нравятся ваши предложения, но не понимаю, как я могу обновить свой документ с помощью '$ push',' $ set', '$ inc',' $ pull' и т. Д. Скажем, как я могу обновить 'post' document with patch '{" op ":" add "," path ":"/comments/2 "," value ": {text: 'Comment test3', tags: ['tag4']}' с помощью операторов MongoDB. Пожалуйста, вы можете привести краткий пример? – Erik

+0

@Erik Это будет 'db.myCollection.update {{id: 123}, {'$ push': {'comments': {text: 'Comment test3', tags: ['tag4']}}) ' – DhruvPathak