Le branching par copie sur écriture (copy-on-write) rend la création de copies isolées de données de production rapide et économique pour les tests CI/CD et les cas d'usage d'agents.
par Yasamin Tari et Kartik Mathur
Snorkel AI crée des outils de développement de données pour les équipes d'IA, utilisés par des annotateurs de données experts et des fournisseurs de modèles de pointe. Dans ce cas d'usage spécifique, Snorkel déploie du code via un pipeline CI construit sur Kubernetes. Chaque pull request ouverte, ainsi que les commits ad hoc, disposent de leur propre namespace Kubernetes éphémère (un « namespace PR »). Les développeurs y déploient leur branche et exécutent la suite de tests complète avant la fusion.
Ces tests sont plus utiles lorsqu'ils s'exécutent sur de vraies données. La base de données de production de Snorkel est un cluster Amazon Aurora PostgreSQL. Fournir à chaque namespace PR sa propre copie de cette base de données était la partie difficile. Restaurer une copie prenait environ 20 minutes par environnement, ce qui est trop lent lorsque les développeurs ouvrent des PR toute la journée et que la file d'attente de fusion a besoin d'un nouvel environnement pour chaque exécution. Les alternatives (bases de données de staging partagées, fixtures réduites ou données synthétiques) recréent les problèmes que les tests de type production sont censés résoudre : des tests qui interfèrent les uns avec les autres, des schémas obsolètes et des bugs qui n'apparaissent qu'avec de vraies formes de données.
Snorkel considérerait Lakebase comme un succès pour la CI/CD s'il répondait à ces critères :
Lakebase est la base de données Postgres serverless de Databricks. Son stockage et son calcul sont séparés, et elle prend en charge le branching : vous pouvez créer une nouvelle branche isolée et inscriptible d'une base de données à partir de n'importe quel point dans le temps, de la même manière que vous créez une branche Git.
Les branches sont en copy-on-write. La création d'une branche est une opération de métadonnées, donc aucune donnée n'est copiée lors de sa création. Une nouvelle branche partage des pages de stockage avec son parent, et seules les pages modifiées par la branche sont écrites comme nouveau stockage. Pour la CI, cela signifie :

1. Conserver une copie fraîche de la production sur la branche principale. Lakebase est du Postgres standard, il fonctionne donc comme cible pour AWS DMS sans outils personnalisés. Snorkel pointe une tâche DMS vers sa base de données Aurora de production et la réplique dans la branche principale du projet Lakebase : un chargement complet d'abord, puis une capture continue des données modifiées qui maintient la branche principale à environ 2 minutes de la production. Le mode LOB complet reporte les valeurs JSONB de 10 à 20 Mo. L'équipe exécute déjà DMS, il n'y avait donc aucun nouveau pipeline de réplication à construire ou à maintenir. La branche principale est la « copie dorée » à partir de laquelle chaque branche PR démarre.
2. Créer une branche lorsqu'une PR s'ouvre. Lorsqu'un développeur ouvre une PR, le pipeline CI appelle l'API Lakebase pour créer une branche à partir de la principale, définit un temps d'expiration sur celle-ci et transmet les détails de connexion de la branche au namespace PR. La branche est prête en moins d'une minute.
3. Exécuter les tests sur de vraies données. L'application dans le namespace PR se connecte à sa branche via Postgres standard. Les tests peuvent exécuter des migrations, écrire des données et modifier des schémas sans affecter la production, la branche principale ou toute autre PR.
4. Supprimer la branche lorsque la PR se ferme ou que le test réussit, avec un TTL comme filet de sécurité. Lorsque la PR est fusionnée ou fermée, le pipeline supprime la branche. Si cette étape de nettoyage ne s'exécute pas, la branche expire d'elle-même lorsque son TTL prend fin.
Deux exigences de sécurité ont exclu les autres options, elles méritent donc leur propre section.
Connectivité privée. Les données de production se déplacent d'Aurora vers Lakebase, et de Lakebase vers le cluster CI, via un réseau privé au lieu de l'internet public.
Identité. Snorkel a deux types d'utilisateurs de base de données : les développeurs qui se connectent de manière interactive pour déboguer un environnement PR, et les pipelines CI qui s'exécutent sans intervention humaine. Okta SSO est conçu autour de la connexion interactive par navigateur, nous avons donc divisé la conception par type d'utilisateur :
Deux fonctionnalités de Lakebase permettent à ce modèle de fonctionner à grande échelle : le branching en copy-on-write et l'expiration des branches (TTL).

Le copy-on-write rend les branches instantanées. Une nouvelle branche ne copie aucune donnée. Elle pointe vers les pages de stockage existantes de son parent, et seules les pages modifiées par la branche sont écrites comme nouveau stockage. Créer une branche à partir d'une copie de production de ~1 To prend à peu près autant de temps que d'en créer une à partir d'une base de données de 1 Go : des secondes, et non les ~20 minutes qu'une restauration complète prenait. Chaque namespace PR obtient immédiatement sa propre copie de production isolée et inscriptible.
Le copy-on-write rend les branches économiques. Chaque branche partage la plupart de ses données avec la branche principale, de sorte que le stockage grandit avec les modifications apportées par chaque branche, et non avec la taille de la base de données. Pour Snorkel, cela représente environ 5 à 10 Go par branche PR en plus de la base partagée. Le calcul est par branche et s'adapte à zéro lorsqu'il est inactif, de sorte que seules les branches qui exécutent activement des tests utilisent du calcul.
L'expiration des branches permet de maintenir les coûts bas au fil du temps. Le copy-on-write est particulièrement avantageux lorsque les branches ont une durée de vie courte. Une branche conserve la version des données qui existait lors de sa création. Comme DMS continue de mettre à jour la branche principale avec les modifications de production, une branche qui persiste continue de retenir ces pages plus anciennes, de sorte que le stockage qu'elle utilise augmente avec le temps. Définir un TTL sur chaque branche CI signifie que chaque branche est supprimée automatiquement une fois sa PR terminée. Cela maintient le stockage proche de la taille de la base de production plus les modifications provenant des PR actives.
Les namespaces PR ne sont pas les seules charges de travail qui ont besoin d'une copie jetable de la production. Les agents d'IA qui écrivent du SQL, exécutent des migrations ou modifient des données ont également besoin d'un endroit sûr pour essayer des choses, et le branching en copy-on-write leur en offre un.
Une branche par tâche d'agent. Chaque exécution d'agent obtient sa propre branche de la principale. L'agent peut lire de vraies données, y écrire et même modifier le schéma sans toucher à la production ni au travail d'aucun autre agent. Créer une branche prend des secondes quelle que soit la taille de la base de données, il est donc pratique d'en créer une pour chaque tâche.
De nombreux agents en parallèle. Le stockage ne grandit qu'avec les pages modifiées par chaque branche, et le calcul de chaque branche s'adapte à zéro lorsqu'il est inactif. Exécuter des dizaines d'agents côte à côte coûte à peu près le prix des modifications qu'ils effectuent, et non des dizaines de copies de la base de données.
Examiner les modifications ou les rejeter. Les équipes peuvent vérifier ce qu'un agent a modifié sur sa branche avant d'appliquer quoi que ce soit à la production. Si le résultat ne convient pas, elles suppriment la branche et il n'y a rien à annuler.
Les TTL nettoient après les agents. Les agents peuvent créer de nombreuses branches éphémères. Une expiration sur chaque branche supprime automatiquement celles qui sont abandonnées, de la même manière que les TTL nettoient les branches CI.
Identité délimitée pour les agents. Tout comme les pipelines CI, les agents peuvent s'exécuter en tant que principaux de service avec des jetons OAuth à courte durée de vie, de sorte que chaque requête exécutée par un agent peut être tracée dans les journaux d'audit.
Prêt à attribuer à chaque demande de tirage (pull request) sa propre base de données de type production ? Commencez avec Lakebase et lisez la documentation sur la création de branches pour configurer l'expiration des branches dans votre pipeline CI.
(Cet article de blog a été traduit à l'aide d'outils basés sur l'intelligence artificielle) Article original
Abonnez-vous à notre blog et recevez les derniers articles directement dans votre boîte mail.