L'effet négligé de la taille des données

Un merci spécial à Özgür Karakaya avec qui j'ai co-écrit cet article de blog.
PDP (Product Detail Page) dans Trendyol est une équipe chargée de fournir des informations sur des produits spécifiques. Ces informations incluent le prix, la taille, la couleur d'un produit ou d'autres informations pertinentes que les clients souhaitent connaître avant d'effectuer un achat. Les informations peuvent être présentées de deux manières différentes, la première se présente sous la forme d'une page de détail de produit unique, principalement lorsque vous cliquez sur un produit et que vous accédez à une page où elle affiche les informations spécifiques sur le produit. L'équipe est également chargée de fournir des données détaillées sur les produits aux autres vues du site Web, c'est-à-dire votre liste de favoris, les produits recommandés et le paiement. Les deux manières étaient gérées par un service appelé Product Detail API soutenu par la base de données NoSQL couchbase servant des requêtes uniques (page de détail de produit unique) ainsi que des requêtes en masse (liste de favoris, etc.).
Avant l'événement Black Friday de 2021, l'équipe s'était fixé pour objectif de traiter 8 millions de requêtes par minute pour les requêtes groupées. L'un des problèmes était que le service fonctionnait mal sous la forme d'une utilisation accrue de la mémoire et d'un nombre considérable d'erreurs lorsqu'il recevait un débit élevé. Le défi consistait à trouver un moyen d'optimiser le service.
Voici donc le voyage.
Remue-méninges et analyse
Au cours de la phase d'analyse, l'équipe PDP a constaté que, au niveau du domaine, les clients qui envoient des demandes groupées n'utilisent pas tous les champs du document de base de données. Le premier point d'optimisation était d'introduire un nouveau modèle de document sans ces champs inutiles. Les avantages de cette restructuration sont :
- optimisation de la taille des données
- focus sur le domaine de contenu en vrac
Vous trouverez ci-dessous un schéma de l'architecture existante et nouvelle à l'époque :

Les cases extérieures en pointillés définissent les limites d'un service. Notez que dans cette première illustration, un service était responsable de la gestion de l'ensemble du pipeline d'opérations. L'optimisation elle-même consistait à diviser le pipeline de conversion en deux processus distincts, comme le montre la deuxième architecture : le processus de conversion indexée et le service de contenu en masse. Ce dernier est un service de lecture qui gère les requêtes HTTP. Le premier s'est avéré être un processus piloté par les événements qui lit les données de ProductContent CB (Source Of Truth), les traite partiellement, puis les stocke dans un compartiment Counchbase pour que ce dernier les utilise. Mais plus là-dessus plus tard.
Dans le cadre de la nouvelle architecture proposée, le principal gain est que le processus d'exécution sera optimisé, ce qui à son tour réduit le temps de réponse du service.
Dans ce blog, nous nous concentrerons sur le processus Index-time. Pour plus d'informations sur "Bulk Content Service", consultez cet article d' Enes Turgut
PS J'utilise ici le terme d'exécution plutôt vaguement. Dans le contexte de ce blog, nous définissons le runtime comme la séquence d'opérations déclenchées par une requête HTTP entrante.
Solutions existantes
Jusqu'à présent, l'équipe avait opté pour la nouvelle architecture. Il est maintenant temps de le mettre en œuvre. Ce que nous essayons de réaliser est une solution de conversion de couchbase à couchbase en temps quasi réel. Couchbase offre déjà une réplication à travers le cluster, cependant, la forme des données doit être modifiée, de sorte que la réplication normale n'est évidemment pas la voie à suivre. Ne serait-il pas génial s'il y avait un moyen de manipuler les données pendant le processus de réplication ? En parcourant la documentation de Couchbase, nous sommes tombés sur Couchbase Eventing Service. Avons-nous touché le jackpot ?
Couchbase Event :
Le Couchbase Eventing Service est un cadre permettant d'agir sur les modifications apportées aux données en temps réel. Les événements sont des modifications apportées aux données dans le cluster Couchbase
Couchbase Eventing nous a semblé être un bon choix car vous pouvez :
- Propager les modifications à d'autres systèmes
- Enrichir un document en temps réel
- Ne nécessite aucun entretien
Pas de cagnotte.
Notre solution (convertisseur de données de contenu en vrac)
Inspiré du connecteur CB Elasticsearch .
Le connecteur Couchbase Elasticsearch réplique vos documents de Couchbase Server vers Elasticsearch en temps quasi réel. Le connecteur utilise le protocole de changement de base de données (DCP) hautes performances pour recevoir des notifications lorsque les documents changent dans Couchbase.
Remplacez Elasticsearch par Couchbase et vous obtenez exactement ce dont nous avons besoin. Tout ce que nous avons à faire est d'accepter les mutations de documents (mises à jour) de la file d'attente DCP, de les traiter, puis de les écrire dans un nouveau compartiment.
Gardez à l'esprit que la solution doit pouvoir :
- Échelle à mesure que le nombre de documents et de mutations augmente.
- Effectuez le processus de bout en bout (acceptation des messages de la file d'attente, conversion du modèle de données et écriture dans un nouveau compartiment) en temps quasi réel.
- Manipulez les documents existants et pas seulement les nouveaux lorsque de nouveaux champs sont ajoutés ou supprimés du modèle de document.
- Fournir un mécanisme pour régénérer les documents à partir de zéro en cas de corruption grave des données.
Eh bien, il y a beaucoup à déballer ici, résumons d'abord :

L'architecture ci-dessus montre la solution finale. En fin de compte, Product Detail Service traitera les demandes uniques, tandis que Bulk Content Service traitera les demandes en masse. Les services sont soutenus par différentes sources de couchbase. Alors que ProductContent CB représente la source de vérité, ProductSummary CB en représente une version résumée prétraitée. La conversion sera gérée dans Bulk Content Data Converter en temps quasi réel via l'acceptation des mutations de documents de la file d'attente DCP. Chaque mutation de document représente l' état du document après la mise à jour. C'est une autre façon de dire que le convertisseur obtiendra automatiquement l'intégralité du document mis à jour à partir de ProductContent CBà chaque mise à jour.
Afin de comprendre comment cette solution évolue exactement, il convient de consulter le fichier de configuration du convertisseur :
{
"group": {
"name": "v20220909",
"membership": {
"memberNumber": 1,
"totalMembers": n
},
"limit": {
"bytes": "5m",
"actions": 100,
"timeout": "1m",
"concurrency": 2
},
"version": 1,
"ignoreDelete": false
},
"dcp": {
"sourceConfig": {
"hosts": ["source_couchbase_host"],
"network": "auto",
"bucket": "bucketName",
"metadataBucket": "metadataBucketName",
"scope": "",
"collections": [],
"compression": true,
"flowControlBuffer": "16m",
"persistencePollingInterval": "100ms"
},
"targetConfig": {
"hosts": ["taget_couchbase_host"],
"bucket": "targetBucketName"
}
}
}
Un autre élément important de la configuration est group.name qui représente le groupe de consommateurs où un décalage est stocké, pensez comme les groupes de consommateurs Kafka. La modification de cette configuration réinitialise l'index de décalage, ce qui signifie que l'état complet de la base de données sera envoyé via la file d'attente DCP. Ceci est particulièrement pratique lorsqu'une mise à jour du modèle de document est nécessaire, l'ajout ou la suppression de nouveaux champs du compartiment cible nécessite une mise à jour complète de tous les documents stockés, cela inclut les documents qui autrement n'auraient pas été normalement mis à jour dans le compartiment cible simplement car aucune mutation ne se serait produite sur les documents sources. Il peut également fonctionner comme un mécanisme de régénération de base de données complet en cas de corruption grave des données.
Pour plus d'informations sur la configuration, veuillez consulter le lien de documentation .
Un autre point d'optimisation consistait à réduire la taille du document cible en ayant des abréviations ou des caractères uniques comme nœuds JSON. Les experts du domaine et les PM ne sont pas censés donner un sens à ce document et c'est pourquoi nous pouvons nous en tirer avec de tels hacks diaboliques. Ci-dessous un exemple de document :

Et au 31 octobre, le résultat est…

Ce hack simple nous a permis de réduire la taille du bucket de 2,92 To à 595 Go ! Une réduction de taille impressionnante d'environ 80 % !
Il faut également avoir remarqué que le nombre de documents dans la base de données cible est réduit d'environ 12 millions. La raison en est que nous avons exclu les produits en rupture de stock qui n'ont pas été mis à jour depuis 12 mois. Dans le compartiment source, nous pouvons toujours avoir besoin de ces documents, mais cela n'aurait aucun sens de les avoir ici, d'où la différence de nombre.
Suivre une performance
Jusqu'à présent, nous avons la solution en place et en cours d'exécution. Mais comment savoir exactement s'il est suffisamment performant ? que se passe-t-il si c'est, hypothétiquement, en train de faire la conversion mais en retard, disons, de 2 secondes? Ce serait un désastre total ! une telle cohérence éventuelle fuira jusqu'à l'interface utilisateur du site Web et affectera gravement l'expérience de l'utilisateur. Le déclenchement manuel d'un changement sur la base de données de production n'est pas possible et ne devrait pas être le moyen de tester en premier lieu. Une approche systématique est nécessaire pour identifier la source du décalage s'il se produit. Le décalage se produit-il en raison d'une congestion dans la file d'attente DCP ? Ou est-ce parce que le convertisseur a une sorte de bug ?
Pour répondre à ces questions, nous avons introduit trois métriques comme indiqué dans la figure ci-dessous :

Le document source contient l'horodatage de la dernière mise à jour, nous appelons cela LMD (Last Modified Date). Une fois qu'une mutation est reçue par le convertisseur, le temps « d'attente dans la file d'attente DCP » peut être calculé facilement en soustrayant LMD de time.now() . Les métriques sont ensuite exposées à Prometheus comme indiqué dans les graphiques ci-dessous :

Limites:
Vous vous souvenez quand j'ai dit plus tôt que la mise à l'échelle consistait simplement à augmenter le nombre total de convertisseurs ? Eh bien, cela s'accompagne d'une petite mise en garde : la configuration de la solution est un peu câblée à l'infrastructure avec laquelle elle communique ; Le rééquilibrage automatique du consommateur sur la file d'attente DCP et la distribution des consommateurs sur les vBuckets sont câblés à la configuration statique du consommateur au démarrage. Pour changer cela, il faut changer le fichier de configuration lui-même, ce qui nécessite un nouveau déploiement. De plus, chaque consommateur a besoin de son propre fichier de configuration statique, ce qui signifie que chaque consommateur doit - dans la terminologie Kubernetes - avoir ses propres fichiers de ressources de déploiement. Chez Trendyol, nous utilisons largement ArgoCD avec Kustomize pour gérer nos déploiements, la mise à l'échelle de la solution nécessite l'ajout - dans la terminologie ArgoCD - d'un nouvel ApplicationSet.
Cependant, jusqu'à présent, notre balance de 8 consommateurs gère la grande charge avec une efficacité en temps quasi réel. Mais à mesure que le compartiment source s'agrandit et que le nombre de mutations augmente, il est nécessaire de reconfigurer manuellement les consommateurs.
Utilisation des autres équipes internes
Confiant dans le connecteur CB-to-CB développé, l'équipe PDP a décidé de le présenter à d'autres équipes au sein de Trendyol. L'équipe a reçu de nombreux commentaires positifs, et avant que nous ne le sachions, l'équipe de recherche a décidé de bifurquer le projet et de l'utiliser dans leur domaine où ils stockent les événements de domaine dans une base de données couchbase et utilisent le connecteur CB-to-CB pour générer un autre modèle de document. pour une utilisation ultérieure. Une telle adoption de solutions développées en interne nous incite tous à aller de l'avant et à continuer à améliorer et à affiner notre utilisation de la technologie.
Postulez ici pour nos postes vacants !