Постановка Windows Azure ‹--› производство, вызывающее конфликты и ошибки в хранилище таблиц

Вчера у нас была ужасная проблема / опыт, когда мы пытались поменять местами нашу постановочную ‹--> производственную роль.

Вот наша установка:

У нас есть рабочая роль, которая забирает сообщения из очереди. Эти сообщения обрабатываются ролью. (Вставки для хранения таблиц, выбор БД и т. Д.). Это может занять 1-3 секунды на сообщение очереди в зависимости от того, сколько сообщений в хранилище таблиц ему нужно сделать. Он удалит сообщение, когда все будет готово.

Проблема при замене:

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

Когда роль хотела обработать сообщение очереди, она выдавала постоянный поток ошибок EntityAlreadyExists. Из-за этих ошибок сообщения очереди не удалялись. Это привело к тому, что сообщения очереди были помещены обратно в очередь и обратно для обработки и так далее ....

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

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

При удалении промежуточного и производственного этапов и повторной публикации в продакшн все стало работать нормально.

Возможные проблемы?

Мы мало 2 понятия не имеем, что произошло на самом деле.

  • Может быть, обе роли получили одни и те же сообщения, одна из них написала сообщение, а другая допустила ошибку?
  • ...???

Возможное решение?

У нас есть некоторые идеи, как решить эту «проблему».

  • Сделать аварийное сообщение для отработки отказа системы? Когда счетчик удаления из очереди превышает X, мы должны просто удалить это сообщение из очереди или поместить его в отдельную «подозрительную очередь».
  • Перехватите ошибку EntityAlreadyExists и просто удалите это сообщение из очереди или поместите его в отдельную очередь.
  • ...????

Несколько ролей

Полагаю, у нас возникнет такая же проблема при назначении нескольких ролей?

Большое спасибо.

РЕДАКТИРОВАТЬ 24/02/2012 - Дополнительная информация

  • На самом деле мы используем GetMessage ()
  • Каждый элемент в очереди уникален и будет генерировать уникальные сообщения в таблице Storage. Немного дополнительной информации о процессе: пользователь что-то публикует, и его нужно будет распространить среди других пользователей. Сообщение, сгенерированное этим пользователем, будет иметь уникальный идентификатор (guid). Это сообщение будет отправлено в очередь и получено рабочей ролью. Сообщение распределяется по нескольким другим таблицам (partitionkey -> UserId, rowkey -> Некоторая отметка времени в тиках и уникальный идентификатор сообщения. Таким образом, почти нет шансов, что те же сообщения будут отправлены в нормальной ситуации.
  • Тайм-аут невидимости МОЖЕТ быть логическим объяснением, потому что некоторые сообщения могут быть распределены по 10-20 таблицам. Это означает 10-20 вставок без возможности пакетной обработки. Можете ли вы установить или увеличить это время невидимости?
  • Отсутствие удаления сообщения из очереди из-за исключения МОЖЕТ быть объяснением, потому что мы еще не реализовали какое-либо подозрительное сообщение при сбое;).

person Kevin Cloet    schedule 23.02.2012    source источник
comment
Последняя реализация очереди позволяет изменять тайм-аут видимости. msdn.microsoft.com/en-us/library/windowsazure/hh452234. aspx   -  person hocho    schedule 24.02.2012


Ответы (5)


Вы явно ошибаетесь в обработке двойных сообщений. Тот факт, что ваш идентификатор уникален, не означает, что сообщение не будет обрабатываться дважды в некоторых случаях, например:

  1. Роль умирает и с частично завершенной работой, поэтому сообщение снова появится для обработки в очереди.
  2. Неожиданный сбой роли, поэтому сообщение снова попадает в очередь.
  3. FC переносит вашу роль, и у вас нет кода для обработки этой ситуации, поэтому сообщение снова попадает в очередь.

Во всех случаях вам нужен код, который обрабатывает тот факт, что сообщение появится снова. Один из способов - использовать DequeueCount и проверьте, сколько раз сообщение было удалено из очереди и получено для обработки. Убедитесь, что у вас есть код, который обрабатывает частичную обработку сообщения.

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

person Panos    schedule 26.03.2012

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

person Igorek    schedule 23.02.2012

Есть несколько возможных причин:

Как вы читаете сообщения в очереди? Если вы выполняете Peek Message, сообщение будет по-прежнему видимым для другого экземпляра роли (или вашей промежуточной среды) до того, как сообщение будет удалено. Убедитесь, что вы используете функцию «Получить сообщение», чтобы сообщение было невидимым до тех пор, пока его нельзя будет удалить.

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

Эта проблема почти наверняка не имеет ничего общего с Staging vs Production, но, скорее всего, вызвана тем, что несколько экземпляров читают из одной очереди. Вероятно, вы можете воспроизвести ту же проблему, указав 2 экземпляра, или развернув один и тот же код в 2 разных производственных службах, или запустив код локально на своем компьютере разработчика (все еще указывающий на хранилище Azure) с использованием 2 экземпляров.

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

person kwill    schedule 23.02.2012
comment
Спасибо за комментарий. Я отредактировал свой пост с дополнительной информацией :) - person Kevin Cloet; 24.02.2012

При работе с очередями вам нужно кодировать с учетом идемпотентности и ожидать и обрабатывать EntityAlreadyExists как жизнеспособный ответ.

Как предполагали другие, причины могут быть

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

Не глядя на код, я предполагаю, что имеет место вариант 3 или 4.

Если вы не можете обнаружить проблему с помощью обзора кода, вы можете подумать о добавлении журнала на основе времени и обертки try / catch, чтобы лучше понять.

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

Добавлено 24 февраля

Чтобы уточнить, изменение тайм-аута невидимости не является общим решением проблемы этого типа. Также обратите внимание, что эта функция, хотя и доступна в REST API, может быть недоступна в клиенте очереди.

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

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

Если вы делаете обновления, то это совсем другая игра с мячом. Один из способов состоит в том, чтобы объединить обновления с фиктивными вставками в одну таблицу с одним и тем же ключом раздела, чтобы исключить ошибку, если обновление произошло ранее, и пропустить обновление. Позже после удаления сообщения вы можете удалить фиктивные вставки. Однако все это добавляет сложности, поэтому гораздо лучше пересмотреть архитектуру продукта; например, вам действительно нужно вставлять / обновлять столько таблиц?

person hocho    schedule 24.02.2012
comment
Ваш вариант 3 и 4 действительно выглядит правильно. Я разместил немного больше информации. - person Kevin Cloet; 24.02.2012

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

var queueMessage = GetNextMessageFromQueue();    

Foo myFoo = GetFooFromTableStorage(queueMessage.FooId);

if (myFoo == null)
{
    myFoo = new Foo {
                        PartitionKey = queueMessage.FooId
                    };

    AddFooToTableStorage(myFoo);
}

DeleteMessageFromQueue(queueMessage);

Если у вас есть два соседних сообщения в очереди с одинаковым FooId, вполне вероятно, что оба экземпляра будут проверять, существует ли Foo, но не найдут его, а затем попытаются его создать. Какой бы экземпляр ни попытался сохранить элемент последним, вы получите ошибку «Сущность уже существует». Поскольку он содержит ошибку, он никогда не попадает в часть кода с сообщением об удалении и поэтому через некоторое время становится видимым в очереди.

Как уже говорили другие, работа с ядовитыми сообщениями - действительно хорошая идея.

Обновление 27/02. Если это не последующие сообщения (которые, исходя из вашей схемы ключей раздела / строки, я бы сказал, что это маловероятно), то моя следующая ставка будет заключаться в том, что это же сообщение снова появится в очереди после таймаут видимости. По умолчанию, если вы используете .GetMessage (), время ожидания составляет 30 секунд. Он имеет перегрузку, которая позволяет указать, как долго этот временной интервал . Также существует функция .UpdateMessage (), который позволяет вам обновлять этот тайм-аут по мере обработки сообщения. Например, вы можете установить начальную видимость на 1 минуту, а затем, если вы все еще обрабатываете сообщение через 50 секунд, продлите его еще на минуту.

person knightpfhor    schedule 24.02.2012
comment
Сообщения на 100% уникальны :) Я обновил свой пост, добавив немного дополнительной информации. - person Kevin Cloet; 24.02.2012