Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Le contrôle de version dans Durable Functions est essentiel, car les fonctions sont inévitablement ajoutées, supprimées et modifiées au cours de la durée de vie d’une application. Durable Functions vous permet de chaîner des fonctions de manière à ce que cela n'était pas possible auparavant, et ce chaînage affecte la gestion des versions.
Cet article vous aide à :
- Identifiez si votre modification de code est un changement cassant.
- Choisissez la stratégie d’atténuation appropriée pour déployer en toute sécurité.
Comparaison de stratégies rapides
Si vous savez déjà que votre changement est critique, utilisez ce tableau pour choisir une stratégie d’atténuation.
| Stratégie | Idéal pour | Détails |
|---|---|---|
| Versionnage d’orchestration (recommandé) | La plupart des applications avec des modifications perturbatrices. La fonctionnalité runtime intégrée fonctionne avec n’importe quel serveur principal de stockage. | Accéder à la section |
| Déploiements côte à côte | Applications qui ne peuvent pas utiliser le contrôle de version d’orchestration ou qui ont besoin d’une isolation complète via des hubs de tâches distincts ou des comptes de stockage. | Accéder à la section |
| Arrêter toutes les instances en cours d'exécution | Le prototypage et le développement local où la perte d’orchestrations en vol est acceptable. | Accéder à la section |
Conseil / Astuce
Si vous recherchez la fonctionnalité de gestion de version d’orchestration intégrée qui fournit une isolation automatique de version au niveau du runtime, consultez Gestion des versions d’orchestration.
Important
Avant de déployer, vérifiez si votre modification est une modification cassant :
- Avez-vous modifié le nom, le type d’entrée ou le type de sortie d’une activité ou d’une fonction d’entité ?
- Avez-vous ajouté, supprimé ou réorganisez les appels aux activités, aux sous-orchestrations, aux minuteurs ou aux événements externes dans le code d’orchestrateur ?
- Avez-vous renommé ou supprimé une fonction que les orchestrations en cours d'exécution sont susceptibles d'appeler ?
Si vous avez répondu oui à l’un de ces éléments, utilisez l’une des stratégies d’atténuation ci-dessous pour éviter les échecs lors de l'exécution des orchestrations.
Types de changements cassants
Plusieurs exemples de modifications majeures existent. Cet article décrit les types les plus courants. Le thème principal derrière tous ces éléments est que les modifications apportées au code de fonction affectent à la fois les orchestrations de fonctions nouvelles et existantes.
Modifications de signature de fonction d’activité ou d’entité
Une modification de signature fait référence à une modification du nom, de l’entrée ou de la sortie d’une fonction. Si vous apportez ce type de modification à une fonction d’activité ou d’entité, elle peut interrompre toute fonction d’orchestrateur qui dépend de celle-ci. Ce comportement est particulièrement vrai pour les langages de type sécurisé. Si vous mettez à jour la fonction d’orchestrateur de manière à intégrer cette modification, vous risquez de décomposer les instances en cours.
Par exemple, considérez la fonction d’orchestrateur suivante.
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
bool result = await context.CallActivityAsync<bool>("Foo");
await context.CallActivityAsync("Bar", result);
}
Cette fonction prend le résultat de Foo et la transmet à Bar. Supposons que vous devez modifier la valeur de retour de Foo d’une valeur booléenne en chaîne pour prendre en charge une plus grande variété de valeurs de résultat. Le résultat ressemble à ceci :
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
string result = await context.CallActivityAsync<string>("Foo");
await context.CallActivityAsync("Bar", result);
}
Cette modification fonctionne pour toutes les nouvelles instances de la fonction d’orchestrateur, mais pourrait interrompre les instances en cours d’exécution. Par exemple, considérez le cas où une instance d’orchestration appelle une fonction nommée Foo, récupère une valeur booléenne, puis des points de contrôle. Si la modification de la signature est déployée à ce stade, l’instance ayant fait l’objet d’un point de contrôle échoue immédiatement lorsqu’elle reprend et rejoue l’appel à Foo. Cet échec se produit parce que le résultat de la table d’historique est une valeur booléenne, mais le nouveau code tente de le désérialiser dans une valeur string, ce qui entraîne un comportement inattendu ou même une exception d’exécution pour les langages de type sécurisé.
Cet exemple est l’une des nombreuses façons dont une modification de signature de fonction peut interrompre les instances existantes. En général, si un orchestrateur doit modifier la façon dont il appelle une fonction, il est probable que la modification soit problématique.
Modifications de logique d’orchestrateur
L’autre classe de problèmes de contrôle de version provient de la modification du code de fonction d’orchestrateur de manière à modifier le chemin d’exécution des instances en cours d’exécution.
Tenez compte de la fonction d’orchestrateur suivante :
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
bool result = await context.CallActivityAsync<bool>("Foo");
await context.CallActivityAsync("Bar", result);
}
Supposons maintenant que vous souhaitez ajouter un nouvel appel de fonction entre les deux appels de fonction existants.
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
bool result = await context.CallActivityAsync<bool>("Foo");
if (result)
{
await context.CallActivityAsync("SendNotification");
}
await context.CallActivityAsync("Bar", result);
}
Cette modification ajoute un nouvel appel de fonction entre SendNotificationFoo et Bar. Aucune modification de signature n’existe. Le problème survient lorsqu’une instance existante reprend à partir de l’appel à Bar. Lors de la relecture, si l’appel d’origine à Foo a renvoyé true, alors l’orchestrateur rejoue l’appel à SendNotification, qui ne figure pas dans son historique d’exécution. L'environnement d'exécution détecte cette incohérence et déclenche une erreur d’orchestration non déterministe, car il a rencontré un appel SendNotification lorsqu’il s’attendait à voir un appel à Bar. Le même type de problème peut se produire lors de l’ajout d’appels d’API à d’autres opérations durables, comme la création de minuteurs durables, l’attente d’événements externes ou l’appel de sous-orchestrations.
Stratégies d’atténuation
Avertissement
Le déploiement de changements cassants sans stratégie d’atténuation (l’approche « ne rien faire ») peut entraîner l’échec des orchestrations avec des erreurs d’orchestration non déterministes , se bloquer indéfiniment dans un Running état ou déclencher des échecs d’exécution de bas niveau qui dégradent les performances. Utilisez toujours l'une des stratégies suivantes lors du déploiement de changements majeurs.
Versioning d’orchestration (recommandé)
Contrairement aux autres stratégies de cette section, le contrôle de version d’orchestration est une fonctionnalité d’exécution intégrée qui fournit une isolation automatique de version. Vous n’avez pas besoin de gérer des déploiements, des hubs de tâches ou des comptes de stockage distincts. Au lieu de cela, le runtime lui-même suit les informations de version et garantit que les instances d’orchestration sont traitées par des workers compatibles.
Avec le contrôle de version d’orchestration :
- Chaque instance d’orchestration obtient une version associée définitivement à celle-ci lors de sa création.
- Les fonctions Orchestrator peuvent examiner leur version et exécuter des branches en conséquence, en conservant les chemins de code anciens et nouveaux dans la même base de code.
- Les workers exécutant des versions de fonction d'orchestrateur plus récentes peuvent continuer à exécuter des instances d’orchestration créées par des versions antérieures.
- Le runtime empêche les workers exécutant des versions antérieures de fonction orchestrateur de réaliser des orchestrations de versions plus récentes.
Cette approche nécessite une configuration minimale (une chaîne de version et une stratégie de correspondance facultative) et est compatible avec n’importe quel fournisseur de stockage. Il s’agit de la stratégie recommandée pour les applications qui doivent prendre en charge les changements cassants, tout en conservant des déploiements sans temps d’arrêt.
Pour obtenir des instructions détaillées sur la configuration et l’implémentation, consultez Contrôle de version d’orchestration.
Arrêter toutes les instances en cours
Une autre option consiste à arrêter toutes les instances en cours. Si vous utilisez le fournisseur par défaut stockage Azure pour Durable Functions, arrêtez toutes les instances en désactivant le contenu des files d'attente internes control-queue et workitem-queue. Vous pouvez également arrêter l’application de fonction, supprimer ces files d’attente et redémarrer l’application. Les files d’attente sont recréées automatiquement une fois l’application redémarrée. Les instances d’orchestration précédentes peuvent rester à l’état « En cours d’exécution » indéfiniment, mais elles n’encombrent pas vos journaux d’activité avec des messages d’échec ou provoquent des dommages à votre application. Cette approche est idéale pour le développement rapide de prototypes, y compris le développement local.
Avertissement
Cette approche nécessite un accès direct aux ressources de stockage sous-jacentes et n'est pas appropriée pour tous les fournisseurs de stockage pris en charge par Durable Functions.
Effectuer des déploiements côte à côte
La méthode la plus fiable pour vous assurer que les modifications perturbatrices sont déployées en toute sûreté consiste à les déployer côte à côte avec vos versions antérieures. Vous pouvez utiliser l’une des techniques suivantes :
- Autre compte de stockage : déployez toutes les mises à jour en tant que nouvelle application de fonction avec un autre compte de stockage. Cela isole entièrement l'état de la nouvelle version par rapport à l'ancienne.
- Hub de tâches différent : Déployez une nouvelle copie de l’application de fonction avec le même compte de stockage, mais avec un nom de hub de tâches mis à jour. Cette approche crée de nouveaux artefacts de stockage pour la nouvelle version, tandis que l’ancienne version continue d’utiliser ses artefacts existants.
Lorsque vous effectuez des déploiements côte à côte dans Azure, vous pouvez utiliser des emplacements déploiement pour exécuter les deux versions simultanément avec un seul emplacement actif production. Lorsque vous êtes prêt à exposer la nouvelle logique d’orchestration, remplacez l'ancienne version par la nouvelle dans l’emplacement de production.
Note
Ces conseils utilisent des termes spécifiques aux stockage Azure, mais s’appliquent généralement à tous les fournisseurs de stockage Durable Functions pris en charge.
Note
Les échanges d’emplacements de déploiement fonctionnent mieux avec les déclencheurs HTTP et webhook. Pour les déclencheurs non HTTP tels que les files d’attente ou Event Hubs, la définition du déclencheur doit dériver d’un paramètre d’application qui est mis à jour dans le cadre de l’opération d’échange.