Aller au contenu principal

19 articles tagués avec « .NET »

Développement de systèmes distribués et d'infrastructure en .NET.

Voir tous les tags

Un saut vers le propriétaire : comment le client Zaris route une clé

· 13 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Comment le client Zaris route une clé vers son propriétaire

Il y a deux façons honnêtes de construire un client de cache distribué. La première est de choisir n'importe quel nœud, de lui envoyer la requête, et de laisser le cluster la relayer en interne vers celui qui possède la clé — simple côté client, mais chaque requête qui atterrit sur le mauvais nœud paie un saut réseau supplémentaire à l'intérieur du cluster. La seconde est de rendre le client assez intelligent pour savoir quel nœud possède la clé avant d'envoyer quoi que ce soit, de sorte que la première socket qu'il touche soit celle qui peut réellement répondre. Zaris prend la seconde voie. Un GET quitte votre processus déjà adressé au nœud qui détient la clé — pas de proxy, pas de saut de relais, pas de coordinateur au milieu.

Cet article est la vue de l'intérieur de son fonctionnement : la fonction qui transforme une clé en propriétaire, la table de correspondance que le client garde en mémoire, et — la partie facile à rater — comment le client reste correct quand la possession bouge pendant que votre trafic est en vol.

Le plancher de latence : une microseconde de travail dans dix de plomberie

· 12 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Une microseconde de travail dans dix microsecondes de plomberie

Voici un nombre qui recadre la façon dont vous pensez un store clé/valeur. Quand nous isolons le store en mémoire de Zaris et lui envoyons des opérations sans rien d'autre sur le chemin — pas de socket, pas de sérialisation, pas de routage — un seul thread sert les lectures à plus d'un million par seconde, et monte proprement à plus de huit millions sur huit threads. Le store, autrement dit, fait le travail réel d'un GET en moins de deux microsecondes, et il n'est emphatiquement pas le goulet d'étranglement.

Et pourtant un GET complet sur une vraie connexion TCP, sur la même machine, plafonne à quelques centaines de milliers par seconde. Le store peut faire le travail cinq fois plus vite que le serveur ne peut le délivrer. La question de performance intéressante au sujet de Zaris n'est donc pas « à quelle vitesse va le store ? ». C'est l'inverse : où part tout le temps entre la socket et le store ? Cet article en est la réponse, tracée opération par opération, car l'écart entre 1,7 microseconde de logique et 10 à 13 microsecondes de CPU serveur par opération n'est pas du bruit — c'est le plancher de latence, et il est presque entièrement de la plomberie.

Le plafond de mémoire : pourquoi un nœud Zaris ne croît jamais sans borne

· 10 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Un nœud Zaris maintenant l'usage mémoire dans une bande calme sous son plafond, évinçant à 80 % jusqu'à 70 % pour que les écritures ne franchissent jamais la limite

Voici une question qui semble simple et ne l'est pas : qu'arrive-t-il quand un store en mémoire tombe à court de mémoire ?

La réponse naïve est « il plante » — le processus grimpe jusqu'à ce que le système d'exploitation le tue. La réponse un peu moins naïve est « il refuse les écritures ». Zaris ne fait ni l'un ni l'autre par défaut. Un nœud Zaris est un store en mémoire, mais il n'est délibérément pas illimité : chaque nœud est un buffer borné avec un plafond dur, et quand les données qu'il possède approchent ce plafond, le nœud évince ses données les plus froides pour faire de la place. Les écritures continuent de réussir ; la limite tient ; le processus ne gonfle pas. Le nœud se comporte comme un cache, parce qu'en dessous, c'en est un.

Le plafond du worker pool : quand un nœud plafonne à 25 % de CPU

· 9 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Un nœud bloqué près de 25 % de CPU avec un débit plat parce que le worker pool de dispatch était le plafond

Voici un résultat de benchmark qui ressemble à une contradiction. Un nœud Zaris servant une charge majoritairement en lecture plafonne à un peu moins d'un demi-million d'ops/s et refuse de grimper — ajoutez plus de connexions client, plus de générateurs de charge, et le nombre ne bouge pas. Le diagnostic évident est « il est saturé ». Sauf que le graphe de CPU dit le contraire : le nœud est posé à environ un quart de son CPU disponible. Trois cœurs sur quatre sont inactifs, et le débit est plat.

Ce n'est pas un nœud limité par les cœurs, et — si vous l'avez écarté avec soin — ce n'est pas non plus un benchmark limité par le client. C'est une troisième chose, et c'est un classique : le worker pool de dispatch des requêtes était plus étroit que le travail ne le voulait, donc les requêtes faisaient la queue pendant que les cœurs se tournaient les pouces.

Cache prêt à l'emploi pour ASP.NET Core : IDistributedCache et HybridCache sur Zaris

· 11 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Cache distribué prêt à l'emploi pour ASP.NET Core sur Zaris

La plupart des raisons de recourir à un cache distribué dans une application .NET ne mentionnent jamais un cache par son nom. Vous ajoutez du cache de sortie et il réclame un IDistributedCache. Vous activez les sessions et l'intergiciel réclame un store de secours. Vous activez le partage des clés de protection des données entre instances et, là encore, il réclame un endroit où mettre des octets que chaque nœud peut voir. Le code de l'application est écrit contre une abstraction ; la seule question est quelle implémentation vous enregistrez au démarrage.

Zaris répond à cette question avec trois petits paquets NuGet, chacun ciblant une couture ASP.NET Core spécifique : un IDistributedCache nu, un HybridCache à deux niveaux et un store de session. Vous enregistrez une ligne au démarrage et le reste de votre application — y compris les bibliothèques tierces qui ne connaissent que l'abstraction — met en cache sur un cluster Zaris sans un seul changement dans le code qui utilise le cache. Cet article passe en revue les trois, et est délibéré sur l'endroit où chacun s'arrête honnêtement.

Le plan de contrôle dans votre shell : piloter un cluster Zaris depuis PowerShell

· 12 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Provisionnement et pilotage d'un cluster Zaris depuis PowerShell

L'essentiel de ce que vous lisez sur un store de données concerne le chemin de données — lectures, écritures, protocole de communication, sa rapidité. Mais avant que tout cela ne compte, quelqu'un doit monter la chose : décider combien de partitions elle a, combien de copies de chacune, où vivent les ports, la démarrer, confirmer qu'elle est montée en bonne santé et la surveiller pendant qu'elle tourne. C'est le plan de contrôle, et dans Zaris il vit dans PowerShell.

Ceci est une visite guidée de l'opérateur au jour zéro. Nous allons créer un espace de travail, provisionner un store, le démarrer, puis relire son état en direct — le tout depuis le module AdminShell. Aucune console web requise, rien édité à la main. Et parce que tout l'intérêt d'une histoire d'opérateur est la confiance, nous serons explicites sur les choix que New-ZrStore met devant vous et sur ce que chacun coûte réellement.

Le même code, avec ou sans réseau : le mode embarqué de Zaris

· 10 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Mode embarqué en cours de processus de Zaris

La plupart des stores de données distribués vous obligent à choisir, tôt et définitivement, entre deux mondes. Il y a le « vrai » store — un cluster que vous déployez, vers lequel vous pointez un client sur le réseau, et auquel vous parlez via un protocole de communication. Et il y a le « faux » store auquel vous recourez dans les tests unitaires : un dictionnaire en mémoire, un mock, un bouchon fait main qui implémente juste assez de l'interface pour que le test compile. Les deux ne partagent rien. Votre test exerce le comportement du bouchon, pas celui du store, et l'écart entre eux est exactement là où se cachent les bugs.

Zaris supprime ce choix. Le même client .NET typé qui parle à un cluster distribué sur TLS exécute aussi le store embarqué à l'intérieur de votre processus, sans socket, sans carte de partitions et sans déploiement — et c'est le même moteur de store, pas un substitut. Vous sélectionnez lequel vous obtenez avec une seule chaîne de connexion. Cet article traite de la façon dont cela fonctionne, des cas pour lesquels il est véritablement bon, et de la ligne dure où le mode embarqué s'arrête et où un cluster commence.

Les tentatives que vous n'écrivez pas : survivre au changement de propriété dans le client Zaris

· 11 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Nouvelle tentative et redirection transparentes lors d'un changement de propriété

Voici un fait concernant tout store clé-valeur partitionné que les schémas léchés ont tendance à passer sous silence : le nœud qui possède votre clé va se déplacer. Un nœud tombe en panne et ses partitions basculent vers leurs réplicas. Vous montez en charge et les partitions migrent vers le nouveau membre. Un primaire préféré revient et la propriété lui est rendue. Chacun de ces événements signifie que le nœud auquel votre client parlait tranquillement il y a une milliseconde est, à l'instant présent, le mauvais nœud pour un certain ensemble de clés.

L'expérience client naïve de ce moment est désagréable : une connexion réinitialisée, une « erreur serveur » générique ou — pire — une écriture qui atterrit sur un nœud qui n'est plus le propriétaire actif et qui, discrètement, ne mène nulle part. Le client Zaris .NET est conçu pour que rien de tout cela n'atteigne votre code. Un changement de propriété devient un statut typé, le client redirige vers le nouveau propriétaire, et la nouvelle tentative est bornée à la fois par un budget de tentatives et par une échéance en temps réel. Dans le cas courant, votre PutAsync prend simplement quelques millisecondes de plus et renvoie Ok. Cet article traite de la mécanique qui rend cela vrai et — tout aussi important — des cas qu'elle choisit délibérément de ne pas masquer.

Chemins chauds sans allocation : comment le client .NET de Zaris utilise ValueTask

· 7 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Le client .NET de Zaris utilise ValueTask sur ses opérations de chemin chaud

La plupart du temps, Task<T> est exactement le bon type de retour pour une méthode asynchrone, et vous ne devriez pas y réfléchir à deux fois. Mais il y a un endroit précis où il vous coûte discrètement : un chemin chaud qui se termine de façon synchrone, appelé des millions de fois.

Voici le mécanisme. Task<T> est un type référence. Chaque appel qui en renvoie un alloue un objet Task sur le tas — même lorsque l'opération se termine de façon synchrone. Un succès de cache servi directement depuis un tampon local, un awaitable déjà terminé : rien de tout cela ne requiert une vraie asynchronie, mais vous payez quand même un objet sur le tas pour porter le résultat. Une allocation n'est rien. Au rythme d'appel d'un client à haut débit — une boucle serrée effectuant des millions de petits Get et Put — c'est un flux régulier de petites allocations sur le tas, et ce flux alimente le ramasse-miettes. Plus de collectes gen-0, plus de CPU dépensé en ramasse-miettes au lieu de votre travail, plus de gigue dans vos latences de queue.

Le client .NET de Zaris s'attaque à cela exactement là où ça compte et nulle part ailleurs. Cet article porte sur cette décision : quelles méthodes ont changé, pourquoi, les règles que vous devez suivre pour les utiliser correctement, et — délibérément — quelles méthodes nous avons laissées intactes.

Grandes valeurs, petites pauses : le découpage des gros objets dans Zaris

· 7 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Le découpage des gros objets maintient les grandes valeurs hors du LOH

Il y a un nombre que tout ingénieur performance .NET finit par mémoriser : 85 000 octets. Toute allocation unique au-delà d'environ cette taille ne va pas sur le tas normal des petits objets — elle va sur le Large Object Heap (LOH). Le LOH est une autre bête. Il n'est collecté que lors d'un ramasse-miettes gen-2 (complet), et par défaut il n'est pas compacté. Cette combinaison convient à un gros tampon que vous allouez une fois et conservez. Elle ne convient pas à un store en mémoire à faible latence qui écrit et réécrit à répétition de grandes valeurs.

Imaginez la charge de travail : gros blobs sérialisés, grands documents JSON, images, charges utiles de protocole — stockés, mis à jour, remplacés, encore et encore. Chacune de ces grandes écritures dépose un objet neuf sur le LOH. Comme le LOH n'est pas compacté, les emplacements libérés ne fusionnent pas en espace contigu ; ils se fragmentent. Et comme en récupérer la moindre partie implique un ramasse-miettes gen-2 complet, les pauses s'allongent et surviennent plus souvent précisément à mesure que l'ensemble de travail grossit. Pour un store dont tout l'argumentaire est la faible latence, c'est le mauvais type de surprise.

Zaris propose une fonctionnalité optionnelle pour contourner entièrement le LOH pour ces valeurs. Elle est désactivée par défaut, et cet article porte sur ce qu'elle fait, comment elle fonctionne, et — tout aussi important — quand vous devriez et ne devriez pas l'activer.