Gestion des messages incohérents

Cette rubrique décrit une méthode pouvant être employée par une application utilisant Service Broker pour détecter un message incohérent et le supprimer de la file d'attente sans recourir à la détection automatique des messages incohérents.

Service Broker propose une fonction de détection automatique des messages incohérents. La détection automatique des messages incohérents définit l'état de la file d'attente sur OFF si une transaction qui reçoit des messages de la file d'attente est restaurée cinq fois. Cette fonctionnalité constitue un dispositif de protection contre les défaillances graves qu'une application ne peut pas détecter par programme. Toutefois, une application ne doit pas s'appuyer sur cette fonctionnalité pour le traitement normal. Étant donné que la détection automatique des messages incohérents arrête la file d'attente, cette fonctionnalité suspend en réalité l'intégralité du traitement de l'application tant que le message incohérent n'a pas été supprimé. Une application doit plutôt tenter de détecter et de supprimer les messages incohérents dans le cadre de la logique d'application.

La stratégie présentée dans cette section part du principe qu'un message doit être supprimé s'il échoue un certain nombre de fois. Pour de nombreuses applications, cette hypothèse est valide. Toutefois, avant d'adopter cette stratégie dans votre application, réfléchissez aux questions suivantes :

  • Le critère de nombre d'échecs est-il fiable pour votre application ? En fonction de l'application, il peut être normal que des messages échouent de temps en temps. Par exemple, dans une application de saisie de commandes, le temps de traitement du service qui traite une commande peut être inférieur à celui du service qui ajoute un nouvel enregistrement client. Dans ce cas, il est normal qu'une commande d'un nouveau client ne puisse pas nécessairement être traitée immédiatement. L'application doit prendre en compte le délai lorsqu'il s'agit de déterminer si un message est un message incohérent ou non. Le service devra peut-être autoriser plusieurs échecs avant de supprimer le message.

  • Votre application peut-elle vérifier rapidement et de manière fiable le contenu d'un message pour détecter qu'il échouera systématiquement ? Si c'est le cas, il est préférable d'adopter cette stratégie plutôt que de compter le nombre d'échecs de traitement du message par le programme. Par exemple, une note de frais ne contenant pas de nom d'employé ou d'ID d'employé ne peut pas être traitée. Dans ce cas, le programme peut être plus performant s'il répond immédiatement par une erreur à un message qui ne peut pas être traité au lieu de tenter de traiter le message. Envisagez également un autre contrôle de validation. Par exemple, si l'ID est présent mais qu'il se situe en dehors de la plage de valeurs assignées (par exemple, un nombre négatif), l'application peut immédiatement mettre fin à la conversation.

  • Devez-vous supprimer un message après un échec ? Si l'application gère un volume important de messages et que chaque message a une durée de vie limitée, il peut être plus efficace de supprimer immédiatement les messages qui provoquent l'échec d'une opération. Par exemple, si le message fournit un rapport de progression du service cible, le service initiateur peut choisir d'ignorer un rapport de progression vide en validant la réception sans traiter le message. Dans ce cas, la conversation se poursuit.

Réfléchissez aux questions suivantes lorsque vous déterminez comment l'application gère un message incohérent :

  • Votre application doit-elle consigner l'échec et le contenu du message ? Dans de nombreux cas, cela n'est pas nécessaire. Toutefois, pour certaines applications, il peut être judicieux de conserver le contenu du message.

  • Votre application doit-elle consigner d'autres informations liées à l'échec ? Dans certains cas, il peut être utile d'assurer le suivi d'autres informations liées à la conversation. Par exemple, vous pouvez utiliser l'affichage catalogue sys.conversation_endpoints pour identifier l'instance Service Broker distante qui a généré le message incohérent.

  • Votre application doit-elle mettre un terme à la conversation par le biais d'une erreur, ou le contrat du service doit-il autoriser une application à indiquer une erreur sans fermer la conversation ? Pour de nombreux services, la réception d'un message incohérent signifie que la tâche décrite dans le contrat ne peut pas être effectuée. Dans ce cas, l'application met un terme à la conversation par le biais d'une erreur. Dans d'autres cas, la conversation peut être en mesure de se poursuivre même si un message échoue. Par exemple, un service qui reçoit des données d'inventaire d'un atelier de manutention peut parfois recevoir un message indiquant un numéro de référence inconnu. Au lieu de mettre fin à la conversation, le service peut enregistrer le message dans une table séparée pour qu'un opérateur puisse le contrôler ultérieurement.

Exemple : détection d'un message incohérent

Cet exemple Transact-SQL illustre un service simple et sans état qui inclut la logique de gestion des messages incohérents. Avant que la procédure stockée reçoive un message, la procédure enregistre la transaction. Lorsque la procédure ne parvient pas à traiter un message, elle restaure la transaction au point d'enregistrement. La restauration partielle retourne le message à la file d'attente tout en maintenant toujours un verrou sur le groupe de conversations pour le message. Dans la mesure où le programme préserve le verrouillage du groupe de conversations, il peut mettre à jour une table qui conserve une liste de messages ayant échoué sans risque qu'un autre agent de lecture de file d'attente puisse traiter le message.

L'exemple suivant définit la procédure stockée d'activation pour l'application :

CREATE PROCEDURE ProcessExpenseReport
AS
BEGIN
  WHILE (1 = 1)
    BEGIN
      BEGIN TRANSACTION ;
      DECLARE @conversationHandle UNIQUEIDENTIFIER ;
      DECLARE @messageBody VARBINARY(MAX) ;
      DECLARE @messageTypeName NVARCHAR(256) ;

      SAVE TRANSACTION UndoReceive ;

        WAITFOR ( 
                  RECEIVE TOP(1)
                    @messageTypeName = message_type_name,
                    @messageBody = message_body,
                    @conversationHandle = conversation_handle
                    FROM ExpenseQueue
                 ), TIMEOUT 500 ;

        IF @@ROWCOUNT = 0
        BEGIN
          ROLLBACK TRANSACTION ;
          BREAK ;
        END ;

        -- Typical message processing loop: dispatch to a stored
        -- procedure based on the message type name.  End conversation
        -- with an error for unknown message types.

        -- Process expense report messages. If processing fails,
        -- roll back to the save point and track the failed message.

        IF (@messageTypeName =
              '//Adventure-Works.com/AccountsPayable/ExpenseReport')
          BEGIN
            DECLARE @expenseReport NVARCHAR(MAX) ;
            SET @expenseReport = CAST(@messageBody AS NVARCHAR(MAX)) ;
            EXEC AdventureWorks.dbo.AddExpenseReport
              @report = @expenseReport ;
            IF @@ERROR <> 0
             BEGIN
               ROLLBACK TRANSACTION UndoReceive ;
               EXEC TrackMessage @conversationHandle ;
             END ;
            ELSE
             BEGIN
               EXEC AdventureWorks.dbo.ClearMessageTracking
                 @conversationHandle ;
             END ;
           END ;
        ELSE

        -- For error messages and end dialog messages, end the
        -- conversation.

        IF (@messageTypeName =
              'https://schemas.microsoft.com/SQL/ServiceBroker/Error' OR
             @messageTypeName =
              'https://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
          BEGIN
            END CONVERSATION @conversationHandle ;
            EXEC dbo.ClearMessageTracking @conversationHandle ;
          END ;


         COMMIT TRANSACTION ;
    END ;
END ;

La procédure stockée TrackMessage assure le suivi du nombre d'échecs d'un message. Lorsqu'il s'agit du premier échec du message, la procédure insère un nouveau compteur pour le message dans la table ExpenseServiceFailedMessages. Sinon, la procédure contrôle le compteur pour connaître le nombre d'échecs du message. La procédure incrémente le compteur lorsque sa valeur est inférieure à un nombre prédéfini. Lorsque la valeur du compteur est supérieure au nombre prédéfini, la procédure met fin à la conversation par le biais d'une erreur et supprime de la table le compteur de la conversation.

CREATE PROCEDURE TrackMessage
@conversationHandle uniqueidentifier
AS
BEGIN
  IF @conversationHandle IS NULL
    RETURN ;

  DECLARE @count INT ;
  SET @count = NULL ;
  SET @count = (SELECT count FROM dbo.ExpenseServiceFailedMessages
                  WHERE conversation_handle = @conversationHandle) ;

  IF @count IS NULL
    BEGIN
      INSERT INTO dbo.ExpenseServiceFailedMessages
        (count, conversation_handle)
        VALUES (1, @conversationHandle) ;
    END ;
  IF @count > 3
    BEGIN
      EXEC dbo.ClearMessageTracking @conversationHandle ;
      END CONVERSATION @conversationHandle
        WITH ERROR = 500
        DESCRIPTION = 'Unable to process message.' ;
    END ;
  ELSE
    BEGIN
      UPDATE dbo.ExpenseServiceFailedMessages
        SET count=count+1
        WHERE conversation_handle = @conversationHandle ;
    END ;
END ;
GO

La définition de table ExpenseServiceFailedMessages contient simplement une colonne conversation_handle et une colonne count, comme illustré dans l'exemple suivant :

CREATE TABLE ExpenseServiceFailedMessages (
  conversation_handle uniqueidentifier PRIMARY KEY,
  count smallint
) ;

La procédure ClearMessageTracking supprime le compteur pour une conversation de la table ExpenseServiceFailedMessages, comme illustré dans l'exemple suivant :

CREATE PROCEDURE ClearMessageTracking
  @conversationHandle uniqueidentifier
AS
BEGIN
   DELETE FROM dbo.ExpenseServiceFailedMessages
     WHERE conversation_handle = @conversationHandle ;
END ;
GO

La stratégie présentée ici est délibérément simple. Vous devez vous inspirer des idées de cette rubrique pour créer une application qui correspond à vos besoins. Par exemple, si votre application gère l'état, il peut être plus efficace d'inclure les informations de suivi des messages ayant échoué dans les tables d'état de l'application.

Les procédures stockées présentées ci-avant ne gèrent pas les erreurs qui provoquent l'échec d'une transaction. Si ce service reçoit un message qui provoque l'échec de l'intégralité de la transaction, la transaction est restaurée. Si cette situation se produit cinq fois, la détection automatique des messages incohérents définit l'état de la file d'attente sur OFF. Dans ce cas, le message incohérent doit être supprimé par une autre application ou par un administrateur.

Si vous pensez que le traitement que vous effectuez sur le message peut provoquer l'échec d'une transaction, vous pouvez utiliser les instructions TRY et CATCH pour gérer l'erreur. Pour plus d'informations sur la gestion des erreurs, consultez Gestion des erreurs du moteur de base de données.