Dans la recherche Glean, nous cherchons en permanence à renvoyer les résultats les plus pertinents. Lorsque nous fusionnons des résultats provenant de nombreuses sources — des fils Slack aux tickets Jira en passant par des documents O365 — il existe de multiples dimensions sur lesquelles expérimenter nos fonctions de classement.
Pour aider à construire ces fonctionnalités, nous voulions des releases itératives fréquentes pour notre équipe ranking. Mais des cycles très rapides peuvent être complexes dans le contexte d’un service avec état. En particulier, nos principaux Index Servers doivent précharger en mémoire des volumes importants de données d’index lors de leur redémarrage. Dans de nombreux cas, ce préchargement pouvait prendre plus de 15 minutes, ce qui imposait soit une interruption de service planifiée, soit des mesures d’atténuation assez lourdes.
Concrètement, avec cette contrainte, nous avons constaté que nous ne déployions pas les mises à jour client aussi souvent que nous le souhaitions. Et nos ingénieurs ont constaté que leur cadence de développement était inutilement ralentie par des temps de redémarrage serveur très longs.
Comment avons-nous résolu ce problème et évolué vers des déploiements incrémentaux plus rapides ?
- Nous avons compris qu’une grande partie du problème disparaissait si nous pouvions éviter de redémarrer notre Index Server pour recevoir des mises à jour de code dans le cas le plus courant.
- Heureusement, ce serveur était écrit en Java, qui dispose d’un mécanisme de chargement dynamique de code. Après nous être penchés sur des ClassLoaders personnalisés, nous avons prototypé le chargement d’une archive Java JAR depuis Cloud Storage et l’utilisation des classes dans notre processus, sans redémarrage.
- Même avec la capacité de charger du code depuis Cloud Storage sans redémarrer, Java n’autorise qu’une seule implémentation pour un nom de classe donné. Cela dit, nous avons réalisé que nous pouvions ajouter le tag de release aux noms de package des classes. Par exemple, pour la classe com.glean.ranking.TermInfo, nous pouvions charger autant de versions que nécessaire sur le serveur si nous écrivions un outil capable de réécrire les noms et d’y inclure le tag de release : com.glean.ranking.release123.TermInfo, com.glean.ranking.release124.TermInfo, etc. Nous avons utilisé des bibliothèques open source (par ex. ObjectWeb ASM) pour aider à construire cet outil de remapping. Au final, nous avions un script permettant de remapper les artefacts de build pour un tag de release donné et de les envoyer dans le cloud en quelques secondes.
- Une fois la capacité de charger dynamiquement plusieurs versions de code depuis Cloud Storage en place, il restait à faire passer ces tags de release à travers le système, afin que la release ou l’expérience correcte soit utilisée pour une requête donnée.  ;

Nous avons rapidement constaté les avantages de cette approche :
- Des releases plus fréquentes et moins disruptives : comme les redémarrages serveur n’étaient généralement plus nécessaires, nous avons commencé à déployer des changements de ranking plus souvent — chaque nuit en interne, et chaque semaine pour les clients.
- Expérimentation : nous avons également commencé à utiliser ce mécanisme pour les expérimentations des développeurs plutôt que de coordonner l’utilisation d’un cluster de développement et d’attendre un redémarrage serveur, ils pouvaient simplement envoyer un fichier Jar et voir rapidement les résultats.
- Versioning robuste : les erreurs pouvaient être associées à des tags de release spécifiques, et les différentes expériences étaient isolées les unes des autres.
- Support du rollback : le retour en arrière d’une release problématique pouvait se faire instantanément via un changement de configuration en ligne.
Comme pour toute implémentation, quelques ajustements ont été nécessaires pour que le mécanisme fonctionne correctement. Par exemple :
- Déterminer quels ensembles de packages remapper (la version initiale ne remappait pas certains packages protobuf, et nous avons vite compris que c’était une erreur !)
- Mettre en cache / charger de manière appropriée pour éviter les scans du fichier Jar en double
- Empêcher les récupérations parallèles pour les mêmes classes
Nous avons constaté que la première utilisation d’un tag de release donné ajoute une latence d’environ une seconde, en raison du temps de récupération depuis Cloud Storage et de la surcharge de décodage du Jar, mais que les utilisations suivantes sont mises en cache et deviennent indiscernables d’une implémentation classique. Ainsi, pour les releases en production, nous envoyons généralement une requête de warmup avant de basculer la configuration.
Cela dit, la capacité à charger dynamiquement de nouvelles releases et expériences sans redémarrer un service avec état nous apporte une grande flexibilité et nous permet d’itérer plus rapidement pour améliorer le service Glean.
Si construire ou utiliser un produit de recherche best-in-class vous intéresse, contactez-nous !









