Je travaille dans une équipe qui cherche à mettre en œuvre des déploiements sans interruption de service. Nous prévoyons d'utiliser une stratégie de déploiement bleu/vert pour y parvenir. L'une des choses que je me rends compte en faisant de la recherche, c'est à quel point il devient compliqué d'apporter des modifications à la base de données. Une opération simple comme renommer une colonne peut prendre cycles de version complète jusqu'à ce qu'elle soit terminée!
Il me semble qu'avoir le déploiement complet d'un changement prendre plusieurs cycles de publication présente beaucoup de risques d'erreur humaine. Dans l'article lié, il montre que des modifications de code sont nécessaires pour 2 versions et une migration de base de données est nécessaire pour 3 versions.
Actuellement, si nous voulons nous souvenir de faire quelque chose, nous pouvons créer un ticket dans notre système de gestion des problèmes, ce qui crée de l'encombrement et peut également être déplacé vers un sprint ultérieur ou le backlog par la direction; ou nous pouvons créer un commentaire TODO, qui sera probablement complètement oublié.
Ce que je recherche, c'est un moyen pour qu'un commentaire TODO puisse avoir une date limite, et notre système d'intégration continue (actuellement indécis que nous utiliserons) rejetterait la génération si ce délai était expiré.
Par exemple, si nous renommons une colonne, nous pourrions créer la migration initiale pour celle-ci, puis deux commentaires TODO pour garantir que les deux migrations restantes sont créées:
// TODO by v55: Create migration to move constraints to new column, remove references to old column in app
// TODO by v56: Create migration to drop old column
Cela semble assez simple à mettre en œuvre, mais je me demande si quelque chose comme ça existe déjà, car je ne veux pas réinventer la roue.
J'ai l'impression que je pourrais souffrir d'un problème XY ici, étant donné que les déploiements continus et les déploiements bleu/vert sont considérés comme une meilleure pratique, il semble étrange que je ne trouve pas de solution pour rendre les mises à jour de la base de données moins douloureuses. Si vous pensez que je cherche complètement la mauvaise chose, faites-le moi savoir dans un commentaire! Cela dit, l'exemple de base de données que j'ai donné n'est qu'un exemple, et je pense que les commentaires TODO avec des dates d'échéance seraient également utiles dans d'autres situations, donc même si je m'approche de cette situation spécifique, je voudrais vraiment répondre à mes question réelle aussi. Merci!
EDIT: Je viens de penser à une autre situation où cela pourrait être utile. Si vous utilisez les bascules de fonctionnalités pour activer certaines parties de votre application lorsqu'elles sont prêtes, vous devez faire attention à les nettoyer, sinon vous risquez de vous retrouver avec Toggle Debt . Les commentaires avec des délais pourraient être un bon moyen de s'en souvenir.
Cette question est vraiment deux questions en une.
De toutes les façons de suivre les actions, c'est la pire. Les commentaires TODO sont bons pendant le travail actif ou comme moyen de suggestion à un mainteneur, "voici quelque chose qui pourrait peut-être être amélioré à l'avenir". Mais si vous comptez sur les commentaires TODO pour faire le travail, vous êtes voué à l'échec.
Que faire à ce sujet
Les commentaires TODO sont essentiellement des dettes techniques, ils doivent donc être traités comme toute autre dette technique. Soit vous les abordez immédiatement, si vous en avez le temps, soit vous les placez dans l'arriéré afin qu'ils puissent être suivis et hiérarchisés.
De manière générale, et cela est totalement subjectif et ouvert au débat, les commentaires de TODO pourraient être considérés comme une odeur de code. Si un commentaire TODO parvient à être intégré dans le contrôle de version, vous devez vous demander, allez-vous vraiment le suivre maintenant? Sinon, ça va. Soyez honnête avec vous-même et mettez-le dans l'arriéré.
La façon dont vous gérez cet arriéré se résume au processus opérationnel, à la politique de l'entreprise et peut-être à une certaine autonomie personnelle. Mais vous avez toujours besoin d'un backlog suivi et priorisé pour vous assurer que cela se produit.
Oui, les modifications de la base de données sont délicates avec une politique sans interruption de service. Quelques astuces pour aider à le rendre moins douloureux:
Processus post-déploiement
Créez un processus de post-déploiement qui s'exécute dans le cadre de la même version. Cependant, vous voulez que cela fonctionne. Sur le dernier système sur lequel j'ai travaillé, j'ai conçu un déploiement en 4 phases:
L'idée était que, dans la mesure du possible, nous mettions autant de modifications de base de données en préapplication que possible.
Postapp était réservé aux cas inhabituels où nous devions apporter des modifications de schéma incompatibles. Dans ces cas, la préapplication apporterait suffisamment de modifications pour rendre le nouveau code d'application compatible (peut-être en créant une vue temporaire pour la compatibilité), et postapp nettoierait ces artefacts temporaires.
La phase de la fenêtre de maintenance était réservée aux modifications qui nécessitaient vraiment des temps d'arrêt ou lorsque le risque ou le coût d'un déploiement en direct n'en valait pas la peine. Par exemple, les scripts qui modifient d'énormes quantités de données peuvent avoir besoin de verrouiller une table entière.
Déployer fréquemment
Si vous déployez de nouvelles versions assez fréquemment, vous pouvez atteindre un point où la réalisation d'un changement sur 2 ou 3 versions est triviale. Les longs cycles de publication amplifient le coût des modifications de la base de données.
N'utilisez pas de TODO. Vous avez déjà une liste TODO dans votre projet. C'est ce qu'on appelle le traqueur de problèmes.
Je pense que le vrai problème est dans cette phrase:
nous pouvons créer un ticket dans notre système de gestion des problèmes, ce qui crée de l'encombrement et peut également être déplacé vers un sprint ultérieur ou le backlog par la direction.
Si votre outil de suivi des problèmes crée trop d'encombrement, trouvez des moyens de résoudre ce problème. Peut-être un type/tag de numéro spécial qui implique moins de cérémonie. Peut-être des sous-problèmes. Peut-être moins de cérémonie. Nous ne pouvons pas vraiment le dire. Mais si votre outil de suivi des problèmes crée tellement de travail, que les gens préfèrent formuler une question élaborée sur un forum public que d'ajouter simplement ce problème, quelque chose ne va vraiment pas.
Si votre gestion retarde indûment la dernière partie d'une tâche, vous avez deux options:
demandez à votre direction pourquoi c'est une mauvaise idée.
gérer comme une tâche unique. Cela pourrait être la solution étalon-or. Dans un monde parfait, vous devriez pouvoir effectuer les trois changements nécessaires à chaque étape. Appliquez-en un à la branche principale, laissez-le construire et déployer. En attendant, appliquez la seconde à la branche principale, laissez-la construire et déployer et ainsi de suite afin que tout se passe dans le même sprint, et si ce n'est pas le cas, ce n'est pas fait. Peut-être même que quelque chose d'automatique est logique lorsque vous effectuez logiquement un déploiement, mais il est en fait divisé en 3.
Ce que je recherche, c'est un moyen pour qu'un commentaire TODO puisse avoir une date limite, et notre système d'intégration continue (actuellement indécis que nous utiliserons) rejetterait la génération si ce délai était expiré.
Ce que vous demandez est faisable si vous êtes prêt à faire le travail et à le suivre.
// TODO by v55: Créer une migration pour déplacer les contraintes vers une nouvelle colonne, supprimer les références à l'ancienne colonne dans l'application // TODO by v56: Créer une migration pour supprimer l'ancienne colonne
grep for //TODO by v55
quand il est temps de déployer la v55. Déployer la génération exécute un script qui le fait en tant que test d'intégration.
Vous pouvez associer 55 à votre suivi de version ou simplement le demander.
Cela devient intéressant si vous voulez vérifier // TODO by v54 lorsque vous faites 55. Plutôt, recherchez la base de code 55 fois, recherchez simplement // TODO by. Filtrez ensuite ce résultat de 1 à 55. Désormais, 56 ne déclenchera pas un échec.
Vous pourriez penser "oh nous n'en aurons pas besoin. Nous les corrigerons à chaque fois tant que nous aurons le chèque". Non, non.
Nous avons eu un problème très similaire dans notre équipe. Pour résoudre ce problème, nous avons écrit une vérification d'analyse statique qui gère ces TODO en vérifiant le problème JIRA ou Git qu'ils référencent. Notre build échoue lorsque le problème spécifié dépasse la colonne "En développement".
Par conséquent, nous pouvons confortablement avoir des TODO sans se soucier de les oublier.
J'ai créé une implémentation open-source de cela, en Java. Oui, un avertissement est que j'ai écrit ceci, mais comme je l'ai dit, il est entièrement open source et sous licence.
L'outil s'appelle Westie et un exemple du vérificateur de problème Jira se trouve sur le fichier README.md. Voir aussi GitIssueAnalyser.
Pour empêcher l'autopromotion si vous avez d'autres questions, envoyez-moi un message. Si vous décidez de l'utiliser et avez des suggestions, veuillez soulever des problèmes sur github.
TLDR: Écrivez (et testez) vos scripts DB maintenant, pas plus tard; il suffit de les coder pour que leur exécution dépende de la version DB.
Exemple
Par exemple, imaginons que vous souhaitiez changer le nom d'une colonne de SSN
en TaxID
, une exigence courante lors de l'internationalisation.
Pour ce faire, vous aurez peut-être temporairement une colonne TaxID
et SSN
. Et tout en prenant en charge les deux versions, vous aurez un déclencheur pour mettre à jour l'une à partir de l'autre. Mais vous ne voulez pas conserver ce déclencheur indéfiniment, donc plus tard, lorsque la compatibilité descendante n'est plus nécessaire, vous voulez que ce déclencheur soit supprimé (et la colonne SSN
supprimée). Nous allons coder tout cela à l'avance sans avoir besoin d'articles ToDo.
Dans notre exemple, nous déploierons le build 102 (qui a la nouvelle colonne) tout en conservant la compatibilité avec le build 101 (qui n'en a pas).
Voici les étapes.
1. Configurer la table de version
Ajoutez une table unique appelée Configuration
avec deux colonnes, Name
et Value
.
Ajoutez une ligne avec un Name
de "TargetVersion" et définissez Value
sur la version de la nouvelle version à déployer.
Ajoutez une ligne avec un Name
de "CompatibleWith" et définissez Value
sur le numéro de version minimum avec lequel le déploiement doit être compatible.
Inspectez et mettez à jour ces lignes avant chaque déploiement.
2. Modifier les scripts de déploiement
Ajoutez un script qui crée une nouvelle colonne de TaxID
, côte à côte avec SSN
, et la remplit à partir de la colonne SSN
. Mettez ce code dans une instruction If
qui vérifie TargetVersion; si la version cible est trop basse (c'est-à-dire que le TaxID
n'est pas encore nécessaire), sautez.
SELECT @TargetVersion = TargetVersion FROM Configuration
IF @TargetVersion < '102' THEN RETURN
ALTER TABLE Customer ADD COLUMN taxID VarChar(12) NOT NULL
UPDATE Customer SET TaxID = SSN
Ajoutez un script qui crée un déclencheur qui remplit TaxID
lors de l'insertion ou de la mise à jour de SSN
et vice versa. Placez ce code dans une instruction If
qui vérifie la version cible et la version compatible; ignorer si TargetVersion est trop faible (le TaxID
n'est pas nécessaire) ou si la version CompatibleWith est trop élevée (le champ SSN
n'est pas nécessaire).
SELECT @TargetVersion = TargetVersion,
@CompatibleWith = CompatibleWith
FROM Configuration
IF @TargetVersion < '102' THEN RETURN
IF @CompatibleWith > '101' THEN RETURN
CREATE TRIGGER SSNAndTaxIDTrigger ON Customer etc.
Ajoutez un script pour supprimer la colonne SSN
. Insérez-le dans une instruction If
qui supprime la colonne uniquement si la version de CompatibleWith est suffisamment élevée (SSN
n'est plus nécessaire).
SELECT @CompatibleWith = CompatibleWith FROM Configuration
IF @CompatibleWith <= '101' THEN RETURN
IF OBJECT_ID('SSNAndTaxIDTrigger') IS NOT NULL DROP TRIGGER SSNAndTaxIDTrigger
IF EXISTS (SELECT * FROM syscolumns c JOIN sysobject o ON o.id = c.is WHERE o.Name = 'Custeomr' AND c.Name = 'SSN') BEGIN
ALTER TABLE Customer DROP COLUMN SSN
END
3. Test
Assurez-vous de tester votre déploiement avec n'importe quelle combinaison de numéros de version bleu/vert que vous souhaitez pouvoir prendre en charge en production. Vous pouvez tester dès que le code est prêt, en manipulant la table Configuration
dans votre environnement QA.
4. Dans votre playbook de déploiement
Ajoutez une étape permettant à un ingénieur de mettre à jour la version CompatibleWith et les lignes TargetVersion. Si vous déployez vers Blue, définissez TargetVersion sur le numéro de version de Blue et la version CompatibleWith sur le numéro de version de Green; inversez-les si vous déployez Green.
Pièges
C'est OK pour vos scripts de déploiement de faire référence et de s'appuyer sur les numéros de version contenus dans cette table DB. PAS de code d'exécution.
Si vous commencez à écrire votre code d'exécution pour inspecter les numéros de version, vous introduisez un nouveau niveau de complexité dans votre application qui pourrait potentiellement devenir un énorme problème de maintenabilité. Chaque chemin d'exécution d'exécution doit être testé; si vous portez ces conditions à l'avenir, QA devra mettre en place une matrice de la douleur pour les valider avec chaque version. Mon conseil est de conserver des conditions comme celles-ci dans les scripts de déploiement uniquement.
Le résultat de tout cela
En fin de compte, vous devriez être en mesure d'écrire tout le code à l'avance (et de le tester aussi) sans craindre qu'il ne s'exécute trop tôt. En outre, le code nettoiera le déclencheur de compatibilité descendante le moment venu sans que vous ayez à vous en préoccuper davantage.
De cette façon, vous pouvez écrire et tester tout le code à l'avance, lorsque vous y pensez, et vous n'avez pas besoin de traiter ces commentaires ToDo désordonnés.
Vous obtenez beaucoup de recul sur votre idée TODO, mais personnellement je n'y vois aucun problème. En fin de compte, le meilleur (et le plus simple) moyen de s'assurer que la migration passe en production est d'échouer un test unitaire dans le cas contraire. Il vous faudra littéralement moins d'une minute pour supprimer une fonction de migration vide qui lève une exception si la version est de 55 ou plus (ou quelles que soient les exigences).
Ensuite, si vous essayez de le publier, vous vous retrouverez avec un test qui a échoué, et quelqu'un devra transformer cette exception en un véritable code de migration.