Le même code, avec ou sans réseau : le mode embarqué 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.
Une chaîne est tout l'interrupteur
Un client Zaris est toujours configuré à partir d'une chaîne de connexion. La chaîne porte le transport, les points de terminaison d'amorçage, le nom du store et les options. Il y a deux formes, et la différence entre elles est la différence entre un cluster et un store embarqué :
zaris://inproc/orders # embedded — store runs inside this process
zariss://zaris.mycorp.com:7863/orders # remote — connect to a cluster over TLS
Le jeton d'hôte inproc est réservé. Quand le client le voit, il n'y a aucun point de terminaison à composer, aucun transport à négocier, aucune poignée de main TLS et aucun jeton — le store est servi depuis la mémoire dans le processus courant. Tout le reste de votre code reste identique :
// Embedded: no server to start, no port, no network.
IZarisClient store = await ZarisClient.ConnectAsync("zaris://inproc/orders");
// Later, in production — the ONLY line that changed:
IZarisClient store = await ZarisClient.ConnectAsync("zariss://zaris.mycorp.com:7863/orders?ca=/etc/zaris/ca.crt");
Dans ASP.NET Core, c'est la même histoire via l'injection de dépendances — la chaîne de connexion provient d'ordinaire de la configuration, donc le basculement est une valeur de config, pas un changement de code :
// Program.cs — one registration, driven entirely by the connection string.
builder.Services.AddClustronZaris("orders", builder.Configuration.GetConnectionString("orders")!);
Mettez zaris://inproc/orders dans appsettings.Development.json et zariss://.../orders en production, et le même binaire tourne embarqué sur l'ordinateur portable d'un développeur et en cluster dans le cloud, sans rien recompiler.
Pourquoi « le même client » est le propos
La raison pour laquelle cela compte réside dans le type que vous récupérez. ConnectAsync renvoie un IZarisClient — ou, pour la surface de fonctionnalités complète, IZaris — quel que soit le mode. Le mode embarqué est servi par une classe appelée ZarisInProcClient, et elle implémente exactement les mêmes interfaces que le client de cluster :
public interface IZaris : IZarisClient
{
ILeasesClient Leases { get; }
ILocksClient Locks { get; }
ICountersClient Counters { get; }
IScanClient Scan { get; }
IWatchClient Watch { get; }
IPubSubClient PubSub { get; }
IHashClient Hashes { get; }
IListClient Lists { get; }
ISetClient Sets { get; }
ISortedSetClient SortedSets { get; }
IStreamClient Streams { get; }
}
Chacune de ces surfaces est présente en mode embarqué, adossée au même moteur de store que le cluster utilise. Get/Put/Delete clé-valeur, en masse et par lot, transactions multi-éléments, TTL et expiration, comparaison-et-échange optimiste, Watch pour les lectures réactives, Streams natifs avec groupes de consommateurs, les structures de données répliquées — tout cela se résout contre le store embarqué, pas un mock.
Ce moteur partagé est ce qui fait du store embarqué une cible de test fidèle. Une boucle de comparaison-et-échange qui dépend d'un conflit rapporté avec précision, un abonnement Watch qui doit se déclencher quand une clé change, une transaction qui doit valider atomiquement ou pas du tout — chacun se comporte embarqué comme il se comporte en cluster, parce que c'est littéralement le même chemin de code qui l'évalue. Un KvStatus.Conflict embarqué signifie ce qu'il signifie en production. Un bouchon de dictionnaire fait main ne peut pas le promettre.
Ce qu'est réellement l'hôte embarqué
Il vaut la peine d'être précis sur ce que zaris://inproc démarre, car les limites honnêtes en découlent directement. L'hôte embarqué est une topologie à nœud unique fixe qui possède chaque segment. Il n'y a pas de carte de partitions, parce qu'il n'y a qu'un seul participant ; il n'y a pas de réplication, parce qu'il n'y a pas de seconde copie ; il n'y a pas de transport, parce qu'il n'y a pas de second processus à atteindre.
Ce qui tourne bel et bien, c'est la vraie mécanique que vous voudriez dans un test. Le store provisionne son segment d'emblée, câble le service de bail, le service de watch et les métriques locales, et démarre le balayeur de TTL à sa cadence normale — de sorte qu'une clé que vous ExpireAsync avec un TTL de 50 ms disparaît réellement, sur une minuterie, exactement comme elle le ferait dans un cluster. L'expiration, la comptabilité d'éviction, le versionnage et les notifications de watch sont tous actifs. C'est un store complet qui se trouve avoir une taille de cluster de un.
Au sein d'un même processus, les clients sont partagés par nom de store. Deux appels ConnectAsync("zaris://inproc/orders") dans le même processus se résolvent vers le même store embarqué, de sorte qu'une valeur que l'un écrit, l'autre la lit — pratique quand votre test lance un producteur et un consommateur et attend qu'ils se voient. Des noms de store différents obtiennent des stores différents ; des processus différents obtiennent des stores embarqués entièrement séparés (plus sur cette limite ci-dessous).
Là où le mode embarqué gagne son pain
Tests unitaires et d'intégration qui exercent le vrai store. C'est l'usage phare. Votre dépôt, votre couche de cache, votre coordinateur de saga — testez-les contre zaris://inproc/test-{Guid}, obtenez un store frais et isolé par test, et exercez la sémantique véritable : vrai versionnage pour vos boucles CAS, vraie expiration TTL sur une vraie minuterie, vraie atomicité de transaction, vrais événements de watch. Aucun conteneur Docker à démarrer en CI, aucun port à allouer, aucune instabilité de démontage. Le store est créé à la première connexion et meurt avec le processus.
Applications à processus unique qui pourraient grandir plus tard. Une application de bureau, un travailleur d'arrière-plan, un outil en ligne de commande, ou un petit service qui a besoin aujourd'hui d'un store structuré rapide mais pourrait devenir un déploiement en cluster demain. Commencez embarqué. Le jour où vous avez besoin de partager ces données entre instances, vous changez la chaîne de connexion pour pointer vers un cluster — et parce que vous avez codé contre IZaris tout du long, rien d'autre ne bouge.
Intégrations livrées embarquées par défaut. Le fournisseur de session ASP.NET Core de Zaris en est un exemple concret : câbler Zaris comme votre store de session est un seul appel, et dans son échantillon il tourne entièrement embarqué —
builder.Services.AddZarisSession(options =>
{
options.ConnectionString = "zaris://inproc/sessions";
});
— ce qui signifie qu'un développeur peut F5 l'application et avoir des sessions fonctionnelles avec zéro infrastructure, puis passer à zariss://.../sessions pour un déploiement multi-instance sans toucher au code de session.
Pour utiliser le mode embarqué, référencez le paquet Clustron.Zaris.InProc aux côtés du client (il cible .NET 8). Le paquet s'enregistre lui-même : le client découvre et charge la fabrique en cours de processus à la première utilisation, de sorte que le simple fait de le référencer suffit. Dans un assembly de test où vous préférez être explicite, new InProcBootstrap().Register(); le câble de façon déterministe.
Là où il s'arrête honnêtement
Le mode embarqué est un vrai store, mais c'est un store à nœud unique, en mémoire, et prétendre autre chose serait le baratin marketing que ce blog cherche à éviter. Trois limites dures, droit sorties de la conception :
Aucune durabilité. Le store embarqué garde les données dans la mémoire du processus et nulle part ailleurs. Il n'y a ni réplication ni journal sur disque dans ce mode — quand le processus se termine, les données sont parties. C'est exactement ce que vous voulez pour un test (table rase à chaque exécution) et exactement ce sur quoi vous ne devez pas compter pour quoi que ce soit qui doive survivre à un redémarrage. Le mode embarqué n'est pas une base de données avec laquelle vous adossez vos données de production.
Aucune haute disponibilité, aucune montée en charge. Un cluster de un tolère la perte de zéro nœud et se met à l'échelle à la mémoire et au CPU d'un seul processus. Toutes les raisons pour lesquelles vous recourriez au Zaris distribué — survivre à une panne de nœud sans perte de données, dimensionner les réplicas pour la durabilité, mettre à l'échelle le débit sur les cœurs et les machines — ne s'appliquent tout simplement pas à un hôte embarqué, parce qu'il n'y en a qu'un. Quand vous avez besoin de ces propriétés, vous avez besoin du cluster.
Aucun partage inter-processus. Le registre en cours de processus partage un store par nom au sein d'un processus. Deux processus différents qui ouvrent tous deux zaris://inproc/orders obtiennent deux stores indépendants qui ne savent rien l'un de l'autre — il n'y a pas de socket entre eux, par définition. Dès l'instant où vos données doivent être visibles pour un second processus ou une seconde machine, le mode embarqué est le mauvais outil et un cluster est le bon.
Aucune de ces limites n'est un bug à corriger ; elles sont la définition d'« embarqué ». La valeur n'est pas que le store en cours de processus soit secrètement distribué — c'est que le jour où vous avez besoin de la distribution, la seule chose que vous changez est la chaîne qui dit où le store vit.
La même promesse, tenue deux fois
L'argumentaire est petit et concret. Vous écrivez votre application contre une seule surface de client. Dans un test, ou sur un portable, ou à l'intérieur d'un outil à processus unique, cette surface est servie par un vrai store Zaris tournant dans votre propre mémoire — rapide à créer, fidèle dans sa sémantique, et disparu quand le processus se termine. En production, le code identique parle à un cluster distribué sur TLS. Le comportement que vos tests ont figé est le comportement que vous livrez, parce que ce n'a jamais été une implémentation différente — seulement un endroit différent où le store vit.
C'est toute l'idée derrière zaris://inproc : l'écart entre « le store dans mon test » et « le store en production » a la largeur d'une chaîne de connexion, et c'est à vous de décider, par environnement, de quel côté vous vous trouvez.