2 июн. 2010 г.

Service Broker: Алгоритм обнаружения отравленных сообщений

 Компонент Microsoft SQL Server Service Broker предоставляет механизм надежной доставки сообщений. Чтение сообщения из очереди и запись информации в базу данных производится в единой транзакции, что гарантирует доставку сообщений. Тем не менее, выполнение чтения сообщения в единой транзакции с обработкой данных усложняет детектирование так называемых "отравленных сообщений" - сообщений, которые по какой-либо причине не могут быть обработаны немедленно или не могут быть обработаны в принципе.

 Компонент Service Broker имеет собственный алгоритм детектирования отравленных сообщений, основанный на том факте, что сообщение, которое не удалось обработать пять раз подряд, вероятно, невозможно будет обработать и в шестой. С этим утверждение не поспоришь - все верно. Но... разработчики компонента Service Broker решили, что такое сообщение не должно генерировать ошибку на уровне диалога, а должно полностью останавливать очередь сообщений. Подразумевается, что пяти откатов транзакции должно хватить для того, что бы программа, обрабатывающая сообщения очереди обнаружила отравленное сообщение и приняла меры. Если этого не произошло - очередь останавливается, о чем можно узнать подписавшись на событие BROKER_QUEUE_DISABLED.

 Факт остановки очереди при ошибке в сообщении диалога стал предметом долгих обсуждений в форумах, посвященных программированию компонента Service Broker, в результате чего разработчики Microsoft SQL Server 2008 R2 добавили опцию POISON_MESSAGE_HANDLING (STATUS = { ON | OFF }). Эта опция позволяет отключить детектирование отравленных сообщений средствами компонента и возложить эту функцию полностью на плечи программиста. Остается один вопрос - как выполнить такое детектирование и что делать с мертвыми сообщениями. Именно на эту тему я и хочу поделиться своими мыслями.

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

Алгоритм обнаружения отравленных сообщений с нечеткой логикой (fuzzy-logic).

 На самом деле, в 99,9% случаев нам должно быть все равно, сколько раз сообщение не удалось обработать до момента обнаружения его отравленной натуры. Важен сам факт обнаружения и своевременного удаления его из очереди с целью снижения бесполезной нагрузки на сервер. Поэтому мы можем применить нечеткую логику, основаную на том, что если мы видим, что сообщение не удалось обработать более некоторого количества раз (например, пять раз), то его можно считать отравленным и извлекать из очереди в соответствии с требуемой логикой обработки таких сообщений.
    В результате имеем такой алгоритм:
  • 1. Нам потребуется дополнительная таблица, содержащая информацию о факте обнаружения отравленного сообщения:
    create table dbo.poison_message_details (incident_id int primary key identity, conversation_handle uniqueidentifier, ...)
    create index ix_poison_message_details on table dbo.poison_message_details(conversation_handle);

  • 2. В процедуру обработки очереди добавляем проверку наличия и обработку отравленных сообщений:

    set xact_abort Off;

    begin try
    -- начинаем транзакцию
    begin transaction;

    -- читаем очередное сообщение
    receive message;

    -- если сообщения в очереди
    -- отсутствуют - завершаем работу

    if @@rowcount = 0 begin
    rollback transaction; return;
    end;

    -- проверяем, что текущее сообщение - отравленное
    if exists(select *
    from dbo.poison_message_details
    where conversation_handle = @conversation_handle
    having count > @threshold) begin
    -- обрабатываем отравленное сообщение
    exec dbo.process_poison_message;
    commit transaction; return;
    end;

    -- обычная обработка сообщений
    if @message_type_name = '...' begin
    exec dbo.process_message;
    commit transaction; return;
    end;

    end try
    begin catch

    -- отменяем текущую транзакцию,
    -- если она не была отменена ранее

    if @@trancount > 0 rollback transaction;
    -- сохраняем информацию о факте
    -- обнаружения отравленного сообщения

    insert dbo.poison_message_details(
    conversation_handle, ...)
    values (@conversation_handle, ...);

    end catch;
    return;

 Что дает этот алгоритм? Алгоритм позволяет после чтения сообщения определить, является ли оно отравленным и обработать его в соответствии с логикой, заданной для обработки отравленных сообщений. При этом невозможно гарантировать, что количество возвратов сообщения в очередь будет точно соответствовать заданному числу. Гарантировать можно только тот факт, что это количество будет не менее, чем значение, указанное в переменной @threshold. Метод простой и действенный. Если требуется чистить таблицу dbo.poison_message_details, то это может быть частью логики обработки отравленного сообщения.

Что делать с отравленными сообщениями?
    Варианты на вскидку (возможно как раздельное, так и одновременное их использование):
  • 1. Просто ничего не делать. Данный вариант является самым простым, но сводит на нет 100% гарантию доставки сообщений, так как отравленные сообщения будут просто потеряны.
  • 2. Завершить диалог с сообщением об ошибке;
  • 3. Залогировать сообщение в таблицу или другую очередь;
  • 4. Отправить сообщение администратору;
  • 5. Отправить сообщение в очередь-отстойник, где сообщение будет лежать некоторое время, а затем будет повторно отправлено в обработку;

Комментариев нет:

Отправить комментарий