CPU / TCP En développement
Passer de 1 à 3 puis à 10 nœuds
Commencez avec un shard et conservez la même API client. Le chemin CPU/TCP exige actuellement une coordination opérationnelle. Démarrer un serveur ne le fait pas rejoindre un cluster : chaque ShardedClient doit recevoir un instantané complet des routes.
L’exemple utilise 1, 3 puis 10 serveurs TCP localhost dans un seul processus. Il valide des contrats logiciels, pas dix machines physiques, la disponibilité en production, la capacité GPU ou les performances. RDMA, GDS, BlueField-3 et les vrais moteurs restent NOT_RUN_HARDWARE.
Topologie
| Étape | ID de shard stables | Adresses d’exemple | Condition sur l’epoch |
|---|---|---|---|
| 1 nœud | 0 | 10.0.0.11:9735 | Epoch initial réel E |
| 3 nœuds | 0–2 | 10.0.0.11–13:9735 | Chaque nœud dépasse le maximum précédent |
| 10 nœuds | 0–9 | 10.0.0.11–20:9735 | Chaque nœud dépasse le maximum précédent |
Remplacez ces adresses privées par vos points d’accès. Chaque serveur utilise un répertoire indépendant et un ID de shard unique et stable configuré côté client. Le serveur n’a ni --shard-id, ni --join, découverte de seeds, quorum ou facteur de réplication. Le hachage rendezvous de la CacheIdentity complète choisit un propriétaire unique.
Exemple local
Après les prérequis du démarrage rapide, utilisez la révision source liée ci-dessous. L’exemple crée des stores temporaires, arrête et joint les anciens serveurs, augmente tous les epochs, remplace toutes les routes, refuse les anciennes sessions et vérifie Lookup / Put / Commit / Get / libération Drained sur chaque shard. Il supprime ses répertoires temporaires.
cargo run -p gpucache-transport --example scaling_smoke
cargo test -p gpucache-transport --example scaling_smokeExtrait d’une exécution réussie. La sortie complète vérifie chaque shard des trois étapes : ID 0, 0–2 puis 0–9. Il s’agit de contrôles de correction, pas de mesures de performances.
status=SCALING_SMOKE_OK
topology=SINGLE_PROCESS_LOCALHOST
stage_shards=1 store_epoch=1 local_revision=1
stage_shards=3 store_epoch=2 local_revision=2
stage_shards=10 store_epoch=3 local_revision=3
per_shard_roundtrips=14
stale_sessions=REJECTED
automatic_payload_migration=NONE
durability_replication=NONE
hardware=NOT_RUN_HARDWARE
performance=NOT_A_PERFORMANCE_RESULTDémarrer un nœud
Cette commande lance un FileStore indépendant sur loopback. Le fingerprint public est une valeur de test : utilisez vos propres identifiants et politiques hors test local. Configurez ShardedClient avec l’ID 0, l’adresse et l’epoch réel du handshake TCP. L’API lease/session conserve le propriétaire choisi pour Put et Commit.
cargo run -p gpucache-transport --bin gpucache-tcp -- serve \
--data-dir ./data/shard-0 --bind 127.0.0.1:9735 \
--tenant 1 --trust 1 \
--fingerprint 0000000000000000000000000000000000000000000000000000000000000001Pour une machine distante, utilisez uniquement un réseau isolé de confiance, par exemple --bind 10.0.0.11:9735 --allow-remote. Cette option n’active ni TLS ni authentification cryptographique du pair. Le fingerprint est un secret de type bearer sur TCP en clair. La CLI lance le serveur ; l’application construit le client.
Configurer le client avant la première étape
Lors de la création du client, choisissez les limites de la topologie finale. Le code applicatif doit déjà fournir le contexte authentifié et les points d’accès initiaux vérifiés. replace_routes ne modifie pas ClientLimits. Cette configuration autorise dix shards avec une connexion chacun pendant les trois étapes :
use gpucache_transport::{ClientLimits, ShardedClient};
let limits = ClientLimits {
max_shards: 10,
max_connections: 10,
max_connections_per_shard: 1,
max_frame_bytes: 2 * 1024 * 1024,
..ClientLimits::default()
};
let client = ShardedClient::new(auth, endpoints, limits)?;La limite par défaut est de 4 connexions par shard. Réduire seulement max_connections à 3 rendrait la configuration invalide. Chaque serveur accepte par défaut 16 connexions, partagées entre tous les clients ; chaque processus client possède son propre pool. Planifiez leur utilisation cumulée.
Le serveur CLI limite le corps des trames à 2 MiB, valeur reprise par max_frame_bytes ci-dessus. Ces limites ne sont pas négociées : alignez client et serveur. L’exemple autonome scaling_smoke utilise, lui, les valeurs par défaut concordantes de la bibliothèque : 1 MiB des deux côtés.
Un nouveau client commence à la révision locale 1, indépendamment des epochs réels des stores. Après la transition coordonnée des serveurs, lisez sa révision actuelle et appelez client.replace_routes(revision, endpoints) avec l’instantané complet. La révision appartient à chaque client ; ce n’est ni un epoch de store ni un terme de consensus partagé.
De 1 à 3 nœuds
- Préparez les nouveaux shards d’ID 1 et 2 : répertoires indépendants, mêmes politiques tenant/trust et adresses joignables. Conservez les limites client choisies avant la première étape et vérifiez les quotas serveur, pins, octets et retries.
- Suspendez tous les clients et les nouvelles opérations Lookup/Get/Put/Commit. Terminez les transferts et libérez les pins avec Drained après confirmation. L’expiration d’un lease ne prouve pas la fin du transfert. Terminez ou annulez/isolez les écritures en cours ; ne réutilisez pas leurs leases.
- Arrêtez et joignez les anciens serveurs et workers, ou appliquez une isolation externe vérifiable empêchant toute lecture/écriture. replace_routes modifie uniquement le client local, sans isoler le serveur distant. Sans preuve, ne publiez pas de routes.
- Préparez chaque répertoire en accès exclusif afin que le prochain handshake annonce un epoch supérieur au maximum précédent. Redémarrez les anciens stores et initialisez/rouvrez les nouveaux si nécessaire. Vérifiez tous les epochs réels avant exposition.
- Créez l’instantané complet des ID 0, 1 et 2 avec leurs adresses et epochs réels. Pour chaque client suspendu, lisez let revision = client.stats().revision; puis appelez client.replace_routes(revision, endpoints). Traitez les refus et ne publiez pas seulement les ajouts. Reprenez lorsque tous les clients requis ont accepté le même instantané complet.
- Après la transition, obtenez une nouvelle session pour chaque nouveau travail logique. Dans un epoch valide, conservez les Commit/Get/Release d’une génération sur son shard et son epoch d’origine ; ne redirigez pas les opérations suivantes fondées sur la génération. Abandonnez le travail isolé sans réécrire son epoch. Les identités redistribuées ne migrent pas automatiquement : en cas de miss/erreur, recalculez et repeuplez explicitement. La restauration froide facultative reste hors du chemin chaud.
Un FileStore neuf démarre à 1 ; chaque réouverture d’un répertoire existant ajoute 1. Il n’existe ni --epoch ni RPC d’administration d’epoch. Si le maximum précédent est 1, tous les nœuds, même nouveaux, doivent atteindre au moins 2 ; s’il est 7, tous doivent atteindre au moins 8. L’exemple réalise des réouvertures exclusives et bornées. Une orchestration équivalente est nécessaire en exploitation. Ne modifiez jamais MANIFEST à la main.
De 3 à 10 nœuds
Répétez pour les ID 0–9 : pause → drain → arrêt/isolation → préparation des epochs → instantané complet → validation → reprise. Après tous à 1 puis tous à 2, l’exemple utilise 3 ; en exploitation, prenez le maximum observé, pas le nombre de nœuds. Utilisez les limites prévues à la création du client ; remplacer les routes ne les modifie pas. Planifiez les limites tenant, retries, pin/region, trames, mémoire et disque par shard.
Le client contacte directement le propriétaire. ShardDirectory ne contient que des métadonnées bornées. La perte d’un propriétaire produit un miss, pas une bascule vers un réplica. Trois nœuds ne signifient pas quorum ; dix ne prouvent pas la prise en charge de 10 000 GPU. Mesurez capacité et répartition avec votre charge.
Validation et retour arrière
- Consignez le commit, les ID stables, adresses, epochs réels et versions complètes des instantanés clients. Refusez les ID ou adresses en double.
- Sur chaque shard, vérifiez Lookup → Put → Commit → Get, les octets et la libération Drained ; un autre tenant/identity ne doit jamais produire de hit. La génération est locale au shard : conservez aussi ID et epoch.
- Vérifiez le refus des anciens sessions/leases et l’impossibilité de committer une écriture ancienne après transition. Arrêtez un propriétaire de test et vérifiez miss/timeout → recalcul.
- Observez statistiques client, drain/quarantine/fence, octets de descripteurs et octets réseau séparés, quotas, retries et connexions. La régression CPU ne remplace pas une qualification multi-machine.
- En cas d’échec, gardez les clients suspendus. Corrigez l’instantané ou revenez à l’ancien ensemble avec un nouvel arrêt/isolation et des epochs plus élevés. Ne restaurez jamais un ancien epoch, instantané ou lease. Les données chaudes peuvent être recalculées.
python3 scripts/ci/vm_regression.py --profile cpu --offline \
--output artifacts/scaling-cpuSources
Le guide du dépôt et l’exemple exécutable font référence. Il n’y a pas de gestionnaire de cluster prêt à l’emploi, de migration en ligne, de garantie de disponibilité pendant les mises à jour progressives ni de qualification de production multi-machine.