De nombreuses entreprises technologiques, dont DoorDash, Amazon et Netflix, accueillent les utilisateurs avec une page d'exploration pour les aider à faire leurs achats. Ces pages d'exploration présentent souvent une grande quantité de contenu, ce qui complique la tâche du système d'arrière-plan qui doit les servir à grande échelle.
La page d'exploration de DoorDash montre un mélange de restaurants et de produits alimentaires que nous recommandons à chaque utilisateur en fonction de son activité passée. Dans nos efforts pour améliorer l'expérience de l'utilisateur, nous avons augmenté la complexité du service de ces pages en incluant des carrousels et des listes de catégories pour offrir une sélection pertinente et visuellement attrayante d'options alimentaires à proximité.
Notre croissance au cours des dernières années a clairement montré que le système que nous utilisions pour servir les pages d'exploration n'était pas évolutif, car il faisait des appels répétés et dupliqués à des services en aval. La mise en œuvre d'un système plus agile et plus évolutif a impliqué la création d'un nouveau modèle de conception de pipeline pour servir le contenu de nos pages d'exploration.
Problèmes d'affichage de notre page d'exploration
Chez DoorDash, notre page d'exploration fournit une liste de restaurants et de magasins recommandés en fonction de l'historique d'engagement de l'utilisateur et de sa localisation. Nous affichons des éléments tels que des carrousels, des bannières et des tuiles de collection pour que les utilisateurs puissent faire défiler et explorer les options qui pourraient leur plaire.
Nous utilisons un microservice appelé "Feed Service" pour alimenter notre page d'exploration, qui sert de point d'entrée pour les requêtes pendant toute la durée de la session du consommateur. Le service de flux orchestre les réponses aux demandes en récupérant des données auprès de différents fournisseurs de contenu, en ajoutant le contexte et en construisant des modules d'affichage personnalisés sous la forme d'une réponse de type flux avant de renvoyer les données aux clients.
Cependant, l'ancien système du Service d'alimentation en ligne présentait plusieurs limites, ce qui rendait difficile l'extension de la page d'exploration à un plus grand nombre de restaurants, de magasins et de carrousels.
Appels inefficaces vers d'autres systèmes
Notre page d'exploration faisait inutilement appel à des services en aval pour obtenir les informations dont elle avait besoin pour afficher les résultats aux utilisateurs. Pour chaque carrousel que nous construisions, le système répétait le même flux de recherche, de classement et d'hydratation du contenu, ce qui entraînait une duplication des appels au contenu. Au fur et à mesure que le nombre de carrousels que nous servions augmentait, ce système inefficace ne pouvait pas évoluer.
Limitations du classement inter-carrousel
Le processus de classement, qui détermine l'ordre dans lequel les restaurants et les magasins sélectionnés sont affichés sur la page d'exploration, a été exécuté dans le même service, appelé service de recherche, que le processus de recherche, ce qui signifie que le classement ne pouvait être effectué que parmi les magasins ou les restaurants recherchés. Étant donné que nous avons réparti le flux de recherche pour chaque carrousel, le classement ne pouvait se faire qu'à l'intérieur du carrousel. Cette approche nous a empêchés d'organiser les carrousels de la manière la plus optimisée possible pour les utilisateurs, et nous a également empêchés d'afficher davantage de carrousels lorsque nous ne pouvions pas utiliser le classement pour sélectionner les carrousels les plus pertinents.
Modularisation minimale
Comme indiqué ci-dessus, chaque flux de découverte peut être décomposé en étapes de recherche, de classement et d'hydratation du contenu. Mais ces étapes ne sont pas extraites ou distillées à partir d'un service existant. Par exemple, la fonctionnalité de génération de candidats est mise en œuvre séparément dans plusieurs applications dont les fonctionnalités se chevauchent fortement. L'absence de modularisation dans ce système a rendu le développement continu proportionnel à la complexité de la logique existante, car toute mise à jour de la génération de candidats devait être dupliquée dans toutes les instances.
Modularisation avec un modèle de conception de pipeline
Nous avons converti les chemins d'accès existants dans le service d'alimentation en passant d'un mode très impératif à un mode déclaratif avec des abstractions. Nous avons structuré le système selon un modèle de conception de pipeline (ou workflow) en regroupant les fonctionnalités communes dans un même module et en incluant un opérateur, tel qu'un travail ou un nœud, dans le pipeline. Par exemple, nous abstrayons les concepts de recherche de candidats et d'extraction de magasins du service de recherche en tant que spécification d'un opérateur de génération de candidats. De même, nous pouvons avoir plus d'opérateurs pour le classement, l'hydratation du contenu et le post-traitement. Les opérateurs individuels disposent d'un support normalisé au niveau du cadre pour les garde-fous, l'observabilité et la propagation du contexte.
Exécution de travaux avec un pipeline basé sur DAG
Nous utilisons un noyau d'exécution développé par DoorDash, appelé Workflow, qui répartit les threads et les coroutines en fonction des dépendances du graphe acyclique dirigé (DAG) et exécute les tâches proprement dites. Comme indiqué plus haut, chaque tâche dans le pipeline représente un module de fonctionnalités communes, qui sert d'abstraction supérieure et peut être.. :
- Évolution par une mise en œuvre plus complexe.
- Étendue par d'autres applications d'exploration qui partagent des flux de travail similaires.
Comme le montre la figure 1 ci-dessous, le processus de génération du contenu d'une nouvelle page d'exploration peut être décomposé en plusieurs tâches :
- Récupération des candidats : Récupérer les sources de données des services externes qui fournissent le contenu de la page, tels que le service de recherche pour les magasins et le service de promotion pour les métadonnées des carrousels. Dans ce cas, nous ne récupérons les sources de données qu'une seule fois pour le contenu de l'ensemble de la page d'exploration afin d'éviter les appels en double.
- Regroupement de contenu : Regroupement du contenu en un ensemble de collections qui peuvent être utilisées ultérieurement pour le classement et la présentation, comme le regroupement de magasins basé sur l'association de carrousels ou de listes de magasins sur la page d'exploration.
- Classement : Classer les entités au sein de chaque collection groupée. Cette étape implique la résolution de l'ID de modèle correct, la génération des valeurs des caractéristiques et l'appel au service de prédiction de l'apprentissage automatique pour calculer les scores de chaque candidat classé.
- Décorateur d'expérience : Pour l'ensemble unique de magasins dans toutes les collections, nous devons les hydrater à partir de sources de données externes pour plus d'informations liées à l'expérience utilisateur, y compris l'heure d'arrivée prévue, les frais de livraison, l'URL des images et les évaluations pour les magasins affichés.
- Processeur de mise en page : Ce processeur collecte toutes les données extraites et produit des espaces réservés pour différents styles de présentation, notamment la page d'exploration, les modèles de données de formulaire pour les carrousels, les listes de magasins et les bannières.
- Post-traitement : Classement et post-traitement de tous les éléments, tels que les carrousels et les listes de magasins, sur la page d'exploration qui sont traités jusqu'à présent de manière programmatique afin d'optimiser l'expérience de l'utilisateur.
Séparer le classement de la recherche
La transition du classement du service de recherche vers le service de flux fait de la fonction de recherche une pure dépendance au rappel, tandis que la fonction de flux reste responsable de la précision de la personnalisation. Ce changement signifie que nous sommes désormais en mesure d'effectuer un classement personnalisé à la fois au sein des éléments de la collection, tels que les carrousels et les listes de magasins, et entre eux. Chaque utilisateur verra une page d'exploration entièrement personnalisée avec des éléments classés, ainsi que des éléments individuels montrant les restaurants et les magasins classés.
L'intégration du module de classement dans le service de flux nous permet de mettre en œuvre des fonctionnalités plus complexes dans un service distinct qui régit toute la logique commerciale relative aux recommandations et à la personnalisation. Utilisé de cette manière, le module de classement devient une abstraction légère qui rend le service de flux plus évolutif.
Améliorer l'observabilité
Nous pouvons introduire la télémétrie du système au sommet de notre pipeline, en plus des données de télémétrie du consommateur existantes provenant des applications de l'utilisateur final, comme le montre la figure 2 ci-dessous. La télémétrie capture automatiquement le contexte et les résultats des composants du flux de travail, ce qui permet une collecte standardisée de détails de haute fidélité, nous permettant essentiellement de savoir ce qui s'est passé et pourquoi au sein du système. Les ingénieurs et les parties prenantes fonctionnelles pourront accéder à ces données via une interface en libre-service, ce qui leur permettra de comprendre en profondeur la qualité de nos algorithmes de personnalisation.
Résultats
Ce projet a été couronné de succès à bien des égards : il a permis de mettre en place une architecture souple pour que DoorDash puisse évoluer dans les années à venir, de créer des produits et des fonctions plus personnalisés et de jeter les bases de nouvelles applications de type "découverte".
Réduire les ressources informatiques
Nous avons constaté une amélioration considérable des paramètres du système dans tous les services en aval. Nous avons notamment observé
- Réduction de 35 % de la latence p95 pour le point d'arrivée de l'alimentation de la page d'exploration et réduction de 60 % de l'utilisation de l'unité centrale pour le service d'alimentation.
- Réduction de 80 % du nombre de requêtes par seconde et de 50 % de l'utilisation de l'unité centrale du service de recherche.
- Une réduction globale de l'utilisation des cœurs de l'unité centrale estimée à 4 500.
Débloquer le classement cross-carousel
Le nouveau système nous a permis d'expérimenter des algorithmes qui classent tous les éléments de la page d'exploration, y compris les carrousels, les listes de magasins, les carreaux de collection et les bannières, afin de s'assurer que la page d'exploration ne contient pas d'erreurs :
- Le contenu le plus pertinent est placé en tête de liste.
- Le contenu moins pertinent peut être supprimé des listes et autres éléments d'affichage, ce qui permet de réduire la taille de la page.
Construire les bases d'autres applications
Nous avons étendu le modèle de conception du flux de travail à d'autres applications liées à l'exploration utilisant une séquence d'opérations similaire, telles que les filtres de recherche et de cuisine, les pages des magasins de proximité et les pages des plateformes d'offres. Comme chaque module est une abstraction, chaque application peut soit avoir sa propre implémentation du module, soit partager l'implémentation généralisée. Ce changement a permis d'améliorer la productivité de notre développement et de faciliter la maintenance du code.
Conclusion
En résumé, comme beaucoup d'entreprises technologiques, DoorDash doit relever le défi de la mise à l'échelle de sa page d'exploration pour recommander le meilleur contenu aux utilisateurs. Cependant, notre système précédent, basé sur le service de flux, présentait plusieurs limites. Nous avons résolu nos problèmes de mise à l'échelle en introduisant un modèle de conception de pipeline qui modularise chaque opérateur commun, ce qui a permis d'améliorer considérablement l'efficacité tant en termes de système que de développement.
Bien que le nouveau système ait été un succès, ce ne sera en aucun cas la dernière itération de notre amélioration continue pour optimiser l'expérience d'exploration de DoorDash. Il y aura d'autres itérations pour affiner chaque module du système afin qu'il devienne plus efficace et plus flexible, de sorte que le service Feed puisse devenir plus léger et évolutif pour la croissance rapide de DoorDash dans les années à venir.
Les équipes d'ingénieurs confrontées à des problèmes de mise à l'échelle peuvent trouver une solution dans le modèle de conception du pipeline. Il permet la modularisation des composants d'un flux de travail, créant ainsi un système plus souple dont les fonctions peuvent être utilisées dans de multiples applications et fonctionnalités. Il peut également conduire à des gains d'efficacité significatifs grâce à l'élimination du code et des processus redondants.
Remerciements
Nous remercions Jimmy Zhou, Rui Hu, Sonic Wang, Ashwin Kachhara, Xisheng Yao et Eric Gu pour leur implication et leur contribution à ce projet, et nous remercions tout particulièrement Yimin Wei pour la construction du moteur d'exécution des flux de travail.