Hubs de tâches dans Fonctions durables (Azure Functions)

Un hub de tâches dans Durable Functions est une représentation de l’état actuel de l’application dans le stockage, y compris tout le travail en attente. Pendant qu’une application de fonction est en cours d’exécution, la progression des fonctions d’orchestration, d’activité et d’entité est continuellement stockée dans le hub de tâches. Cela garantit que l’application peut reprendre le traitement là où elle s’est arrêtée, si elle doit être redémarrée après avoir été temporairement arrêtée ou interrompue pour une raison quelconque. En outre, cela permet à l’application de fonction de mettre à l’échelle les rôles de travail de calcul dynamiquement.

Diagram showing concept of function app and task hub concept.

Conceptuellement, un hub de tâches stocke les informations suivantes :

  • Les états d’instance de toutes les instances d’orchestration et d’entité.
  • Les messages à traiter, y compris
    • tous les messages d’activité qui représentent les activités en attente d’exécution :
    • tous les messages d’instance en attente d’être livrés aux instances.

La différence entre les messages d’activité et d’instance est que les messages d’activité sont sans état et peuvent donc être traités n’importe où, tandis que les messages d’instance doivent être remis à une instance avec état spécifique (orchestration ou entité), identifiée par son ID d’instance.

En interne, chaque fournisseur de stockage peut utiliser une organisation différente pour représenter les états d’instance et les messages. Par exemple, les messages sont stockés dans des files d’attente de stockage Azure par le fournisseur de Stockage Azure, mais dans des tables relationnelles par le fournisseur MSSQL. Ces différences n’ont pas d’importance en ce qui concerne la conception de l’application, mais certaines d’entre elles peuvent influencer les caractéristiques de performances. Nous les abordons dans la section Représentation dans le stockage ci-dessous.

Éléments de travail

Les messages d’activité et les messages d’instance dans le hub de tâches représentent le travail que l’application de fonction doit traiter. Pendant que l’application de fonction est en cours d’exécution, elle extrait en continu des éléments de travail à partir du hub de tâches. Chaque élément de travail traite un ou plusieurs messages. Nous faisons la distinction entre deux types d’éléments de travail :

  • Éléments de travail d’activité : exécutez une fonction d’activité pour traiter un message d’activité.
  • Éléments de travail d’orchestrateur : exécutez une fonction d’orchestrateur ou d’entité pour traiter un ou plusieurs messages d’instance.

Les rôles de travail peuvent traiter plusieurs éléments de travail en même temps, soumis aux limites concurrentielles configurées par worker.

Une fois qu’un worker a terminé un élément de travail, il valide les effets dans le hub de tâches. Ces effets varient selon le type de fonction exécuté :

  • Une fonction d’activité terminée crée un message d’instance contenant le résultat, adressé à l’instance d’orchestrateur parent.
  • Une fonction d’orchestrateur terminée met à jour l’état et l’historique de l’orchestration, et peut créer des messages.
  • Une fonction d’entité terminée met à jour l’état de l’entité et peut également créer des messages d’instance.

Pour les orchestrations, chaque élément de travail représente un épisode de l’exécution de cette orchestration. Un épisode commence lorsqu’il existe de nouveaux messages que l’orchestrateur doit traiter. Un tel message peut indiquer que l’orchestration doit démarrer, ou il peut indiquer qu’une activité, un appel d’entité, un retardateur ou une sous-orchestration se sont terminés, ou il peut représenter un événement externe. Le message déclenche un élément de travail qui permet à l’orchestrateur de traiter le résultat et de continuer avec l’épisode suivant. Cet épisode se termine lorsque l’orchestrateur se termine ou atteint un point où il doit attendre de nouveaux messages.

Exemple d'exécution

Considérez une orchestration Fan-out-Fan-in qui démarre deux activités en parallèle et attend que les deux se terminent :

[FunctionName("Example")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    Task t1 = context.CallActivityAsync<int>("MyActivity", 1);
    Task t2 = context.CallActivityAsync<int>("MyActivity", 2);
    await Task.WhenAll(t1, t2);
}

Une fois cette orchestration lancée par un client, elle est traitée par l’application de fonction comme séquence d’éléments de travail. Chaque élément de travail terminé met à jour l’état du hub de tâches lors de sa validation. Voici les étapes :

  1. Un client demande de démarrer une nouvelle orchestration avec l’ID d’instance « 123 ». Une fois que le client a terminé cette requête, le hub de tâches contient un espace réservé pour l’état d’orchestration et un message d’instance :

    workitems-illustration-step-1

    L’étiquette ExecutionStarted est l’un des nombreux types d’événements d’historique qui identifient les différents types de messages et d’événements participant à l’historique d’une orchestration.

  2. Un worker exécute un élément de travail d’orchestrateur pour traiter le message ExecutionStarted. Il appelle la fonction d’orchestrateur qui commence à exécuter le code d’orchestration. Ce code planifie deux activités, puis cesse de s’exécuter lorsqu’il attend les résultats. Une fois que le worker valide cet élément de travail, le hub de tâches contient

    workitems-illustration-step-2

    L’état de runtime est maintenant Running, deux nouveaux messages TaskScheduled ont été ajoutés, et l’historique contient à présent les cinq événements OrchestratorStarted, ExecutionStarted, TaskScheduled, TaskScheduled, OrchestratorCompleted. Ces événements représentent le premier épisode de l’exécution de cette orchestration.

  3. Un worker exécute un élément de travail d’activité pour traiter l’un des messages TaskScheduled. Il appelle la fonction d’activité avec l’entrée « 2 ». Une fois la fonction d’activité terminée, cela crée un message TaskCompleted contenant le résultat. Une fois que le worker valide cet élément de travail, le hub de tâches contient

    workitems-illustration-step-3

  4. Un worker exécute un élément de travail d’orchestrateur pour traiter le message TaskCompleted. Si l’orchestration est toujours mise en cache en mémoire, il peut simplement reprendre l’exécution. Sinon, le worker relit d’abord l’historique pour récupérer l’état actuel de l’orchestration. Ensuite, il poursuit l’orchestration, fournissant le résultat de l’activité. Après avoir reçu ce résultat, l’orchestration attend toujours le résultat de l’autre activité, de sorte qu’elle cesse de s’exécuter une fois de plus. Une fois que le worker valide cet élément de travail, le hub de tâches contient

    workitems-illustration-step-4

    L’historique d’orchestration contient désormais trois autres événements OrchestratorStarted, TaskCompleted, OrchestratorCompleted. Ces événements représentent le deuxième épisode de l’exécution de cette orchestration.

  5. Un worker exécute un élément de travail d’activité pour traiter le message TaskScheduled restant. Il appelle la fonction d’activité avec l’entrée « 1 ». Une fois que le worker valide cet élément de travail, le hub de tâches contient

    workitems-illustration-step-5

  6. Un worker exécute un autre élément de travail d’orchestrateur pour traiter le message TaskCompleted. Après avoir reçu ce deuxième résultat, l’orchestration se termine. Une fois que le worker valide cet élément de travail, le hub de tâches contient

    workitems-illustration-step-6

    L’état du runtime est maintenant Completed, et l’historique d’orchestration contient à présent quatre autres événements OrchestratorStarted, TaskCompleted, ExecutionCompleted, OrchestratorCompleted. Ces événements représentent le troisième et dernier épisode de l’exécution de cette orchestration.

L’historique final de l’exécution de cette orchestration contient ensuite les 12 événements OrchestratorStarted, ExecutionStarted, TaskScheduled, TaskScheduled, OrchestratorCompleted, OrchestratorStarted, TaskCompleted, OrchestratorCompleted, OrchestratorStarted, TaskCompleted, ExecutionCompleted, OrchestratorCompleted.

Notes

La planification affichée n’est pas la seule : il existe de nombreuses planifications possibles légèrement différentes. Par exemple, si la deuxième activité se termine plus tôt, les deux messages d’instance TaskCompleted peuvent être traités par un seul élément de travail. Dans ce cas, l’historique d’exécution est un peu plus court, car il n’y a que deux épisodes, et il contient les 10 événements suivants : OrchestratorStarted, ExecutionStarted, TaskScheduled, TaskScheduled, OrchestratorCompleted, OrchestratorStarted, TaskCompleted, TaskCompleted, ExecutionCompleted, OrchestratorCompleted.

Gestion du hub de tâches

Examinons ensuite de plus près la façon dont les hubs de tâches sont créés ou supprimés, comment utiliser correctement les hubs de tâches lors de l’exécution de plusieurs applications de fonction et comment le contenu des hubs de tâches peut être inspecté.

Création et suppression

Un hub de tâches vide avec toutes les ressources requises est automatiquement créé dans le stockage lorsqu’une application de fonction est démarrée la première fois.

Si vous utilisez le fournisseur de Stockage Azure par défaut, aucune configuration supplémentaire n’est requise. Sinon, suivez les instructions de configuration des fournisseurs de stockage pour vous assurer que le fournisseur de stockage peut provisionner et accéder correctement aux ressources de stockage requises pour le hub de tâches.

Notes

Le hub de tâches n’est pas automatiquement supprimé lorsque vous arrêtez ou supprimez l’application de fonction. Vous devez supprimer manuellement le hub de tâches, son contenu ou le compte de stockage contenant si vous ne souhaitez plus conserver ces données.

Conseil

Dans un scénario de développement, vous devrez peut-être souvent redémarrer à partir d’un état propre. Pour ce faire rapidement, vous pouvez simplement modifier le nom du hub de tâches configuré. Cela force la création d’un hub de tâches vide lorsque vous redémarrez l’application. N’oubliez pas que les anciennes données ne sont pas supprimées dans ce cas.

Plusieurs applications de fonction

Si plusieurs applications de fonction partagent un compte de stockage, chaque application de fonction doit être configurée avec un nom de hub de tâches distinct. Cette exigence s’applique également aux emplacements intermédiaires : chaque emplacement intermédiaire doit être configuré avec un nom de hub de tâches unique. Un même compte de stockage peut comporter plusieurs hubs de tâches. Cette restriction s’applique généralement également aux autres fournisseurs de stockage.

Le diagramme suivant illustre un hub de tâches par application de fonction dans les comptes Stockage Azure partagés et dédiés.

Diagram showing shared and dedicated storage accounts.

Remarque

L’exception à la règle de partage de hub de tâches est si vous configurez votre application pour la reprise d’activité régionale. Pour plus d’informations, consultez l’article Reprise d’activité et géodistribution.

Inspection du contenu

Il existe plusieurs façons courantes d’inspecter le contenu d’un hub de tâches :

  1. Dans une application de fonction, l’objet client fournit des méthodes pour interroger le magasin d’instances. Pour en savoir plus sur les types de requêtes pris en charge, consultez l’article Gestion des instances.
  2. De même, l’API HTTP offre des requêtes REST pour interroger l’état des orchestrations et des entités. Pour plus d’informations, consultez Référence sur l’API HTTP.
  3. L’outil Durable Functions Monitor peut inspecter les hubs de tâches et offre différentes options pour l’affichage visuel.

Pour certains des fournisseurs de stockage, il est également possible d’inspecter le hub de tâches en accédant directement au stockage sous-jacent :

  • Si vous utilisez le fournisseur de Stockage Azure, les états d’instance sont stockés dans la table d’instance et la table d’historique qui peuvent être inspectés à l’aide d’outils tels que Explorateur Stockage Azure.
  • Si vous utilisez le fournisseur de stockage MSSQL, les requêtes et outils SQL peuvent être utilisés pour inspecter le contenu du hub de tâches à l’intérieur de la base de données.

Représentation dans le stockage

Chaque fournisseur de stockage utilise une organisation interne différente pour représenter les hubs de tâches dans le stockage. Comprendre cette organisation, bien qu’elle n’est pas requise, peut être utile lors de la résolution des problèmes d’une application de fonction ou lors de la tentative de garantir les performances, la scalabilité ou les cibles de coût. Nous expliquons ainsi brièvement, pour chaque fournisseur de stockage, comment les données sont organisées dans le stockage. Pour plus d’informations sur les différentes options du fournisseur de stockage et leur comparaison, consultez Fournisseurs de stockage Durable Functions.

Fournisseur Stockage Azure

Le fournisseur de Stockage Azure représente le hub de tâches dans le stockage à l’aide des composants suivants :

  • Deux tables Azure stockent les états d’instance.
  • Une file d’attente Azure stocke les messages d’activité.
  • Une ou plusieurs files d’attente Azure stockent les messages d’instance. Chacune de ces dénommées files d’attente de contrôle représente une partition affectée à un sous-ensemble de tous les messages d’instance, en fonction du hachage de l’ID d’instance.
  • Quelques conteneurs de blob supplémentaires utilisés pour les blobs de bail et/ou les messages volumineux.

Par exemple, un hub de tâches nommé xyz avec PartitionCount = 4 contient les files d’attente et les tables suivantes :

Diagram showing Azure Storage provider storage storage organization for 4 control queues.

Ensuite, nous décrivons ces composants et le rôle qu’ils jouent plus en détail.

Pour plus d’informations sur la façon dont les hubs de tâches sont représentés par le fournisseur de Stockage Azure, consultez la documentation du fournisseur de Stockage Azure.

Fournisseur de stockage Netherite

Netherite partitionne tous les états du hub de tâches en un nombre spécifié de partitions. Dans le stockage, les ressources suivantes sont utilisées :

  • Un conteneur de blobs Stockage Azure qui contient tous les blobs, regroupés par partition.
  • Une table Azure qui contient des métriques publiées sur les partitions.
  • Un espace de noms Azure Event Hubs pour remettre des messages entre des partitions.

Par exemple, un hub de tâches nommé mytaskhub avec PartitionCount = 32 est représenté dans le stockage comme suit :

Diagram showing Netherite storage organization for 32 partitions.

Remarque

L’état du hub de tâches est stocké dans le conteneur de blobs x-storage. La table DurableTaskPartitions et l’espace de noms EventHubs contiennent des données redondantes : si leur contenu est perdu, ils peuvent être récupérés automatiquement. Par conséquent, il n’est pas nécessaire de configurer l’espace de noms Azure Event Hubs pour conserver les messages au-delà de l’heure d’expiration par défaut.

Netherite utilise un mécanisme d’approvisionnement d’événements, basé sur un journal et des points de contrôle, pour représenter l’état actuel d’une partition. Les objets blob de blocs et les objets blob de pages sont utilisés. Il n’est pas possible de lire ce format directement à partir du stockage, de sorte que l’application de fonction doit être en cours d’exécution lors de l’interrogation du magasin d’instances.

Pour plus d’informations sur les hubs de tâches pour le fournisseur de stockage Netherite, consultez Informations sur le hub de tâches pour le fournisseur de stockage Netherite.

Fournisseur de stockage MSSQL

Toutes les données du hub de tâches sont stockées dans une base de données relationnelle unique, à l’aide de plusieurs tables :

  • Les tables dt.Instances et dt.History stockent les états d’instance.
  • La table dt.NewEvents stocke les messages d’instance.
  • La table dt.NewTasks stocke les messages d’activité.

Diagram showing MSSQL storage organization.

Pour permettre à plusieurs hubs de tâches de coexister indépendamment dans la même base de données, chaque table inclut une colonne TaskHub dans le cadre de sa clé primaire. Contrairement aux deux autres fournisseurs, le fournisseur MSSQL n’a pas de concept de partitions.

Pour plus d’informations sur les hubs de tâches pour le fournisseur de stockage MSSQL, consultez Informations sur le hub de tâches pour le fournisseur de stockage Microsoft SQL (MSSQL).

Noms de hub de tâches

Les hubs de tâches sont identifiés par un nom qui doit être conforme aux règles suivantes :

  • Nom uniquement composé de caractères alphanumériques
  • Nom commençant par une lettre
  • Nom compris entre 3 et 45 caractères

Le nom du hub de tâches est déclaré dans le fichier host.json, comme illustré dans l'exemple suivant :

host.json (Functions 2.0)

{
  "version": "2.0",
  "extensions": {
    "durableTask": {
      "hubName": "MyTaskHub"
    }
  }
}

host.json (Functions 1.x)

{
  "durableTask": {
    "hubName": "MyTaskHub"
  }
}

Les hubs de tâches peuvent également être configurés à l’aide de paramètres d’application, comme indiqué dans l’exemple de fichier host.json suivant :

host.json (Functions 1.0)

{
  "durableTask": {
    "hubName": "%MyTaskHub%"
  }
}

host.json (Functions 2.0)

{
  "version": "2.0",
  "extensions": {
    "durableTask": {
      "hubName": "%MyTaskHub%"
    }
  }
}

Le nom du hub de tâches est défini sur la valeur du paramètre d’application MyTaskHub. Le fichier local.settings.json suivant montre comment définir le paramètre MyTaskHub sur samplehubname :

{
  "IsEncrypted": false,
  "Values": {
    "MyTaskHub" : "samplehubname"
  }
}

Notes

Lors de l’utilisation d’emplacements de déploiement, c’est une bonne pratique que de configurer le nom du hub de tâches en utilisant des paramètres d’application. Si vous voulez qu’un emplacement particulier utilise toujours un hub de tâches particulier, utilisez des paramètres d’application « liés à l’emplacement ».

Outre host.json, les noms de hub de tâches peuvent également être configurés dans les métadonnées de liaison du client d’orchestration. Cela est utile si vous avez besoin d’accéder aux orchestrations ou aux entités qui résident dans une application de fonction distincte. Le code suivant montre comment écrire une fonction recourant à la liaison du client d’orchestration pour utiliser un hub de tâches configuré en tant que paramètre d’application :

[FunctionName("HttpStart")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
    [DurableClient(TaskHub = "%MyTaskHub%")] IDurableOrchestrationClient starter,
    string functionName,
    ILogger log)
{
    // Function input comes from the request content.
    object eventData = await req.Content.ReadAsAsync<object>();
    string instanceId = await starter.StartNewAsync(functionName, eventData);

    log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

    return starter.CreateCheckStatusResponse(req, instanceId);
}

Notes

Le précédent exemple porte sur Durable Functions 2.x. Pour Durable Functions 1.x, vous devez utiliser DurableOrchestrationContext au lieu de IDurableOrchestrationContext. Pour en savoir plus sur les différences entre les versions, consultez l’article Versions de Durable Functions.

Notes

La configuration de noms de hub de tâches dans les métadonnées de liaison du client est uniquement nécessaire lorsque vous utilisez une application de fonction pour accéder aux orchestrations et aux entités d’une autre application de fonction. Si les fonctions clientes sont définies dans la même application de fonction que les orchestrations et les entités, vous devez éviter de spécifier des noms de hub de tâches dans les métadonnées de liaison. Par défaut, toutes les liaisons clientes obtiennent leurs métadonnées de hub de tâches à partir des paramètres de host.json.

Les noms de hubs de tâches doivent commencer par une lettre et contenir uniquement des lettres et des chiffres. S’il n’est pas spécifié, un nom de hub de tâches par défaut sera utilisé comme indiqué dans le tableau suivant :

Version d’extension durable Nom du hub de tâches par défaut
2.x Lorsqu’il est déployé dans Azure, le nom du hub de tâches est dérivé du nom de l’application de fonction. En cas d’exécution en dehors d’Azure, le nom du hub de tâches par défaut est TestHubName.
1.x Le nom du hub de tâches par défaut pour tous les environnements est DurableFunctionsHub.

Pour en savoir plus sur les différences entre les versions d’extension, consultez l’article Versions de Durable Functions.

Notes

Le nom permet de différencier les hubs de tâches les uns des autres lorsqu’ils sont plusieurs dans un compte de stockage partagé. Si plusieurs applications de fonction partagent un compte de stockage partagé, vous devez configurer explicitement des noms différents pour chaque hub de tâches dans les fichiers host.json. Dans le cas contraire, les applications de fonction seront en concurrence les unes avec les autres pour les messages, ce qui pourrait entraîner un comportement inhabituel, y compris des orchestrations qui se retrouveraient inopinément « bloquées » dans l’état Pending ou Running.

Étapes suivantes