Architecture sans état
VCR exécute plusieurs répliques de chaque application et peut encore évoluer en fonction de la charge. Les répliques peuvent également être réduites à zéro lorsqu'elles sont inactives. Cela a des conséquences importantes sur la façon dont vous gérez l'état.
Pourquoi l'état en mémoire n'est pas sûr
Toutes les données stockées dans la mémoire (variables globales, objets au niveau du module, caches dans le processus) le sont :
- Local à une seule réplique
- Perdu lors d'un redémarrage ou d'une mise à zéro
- Invisible pour les autres répliques qui traitent des demandes concurrentes
Ne partez jamais du principe que deux demandes atteindront la même réplique. Si c'est le cas aujourd'hui, ce ne sera plus le cas lorsque la charge augmentera.
C'est faux :
// Replica-local — invisible to other replicas and lost on restart
const cache = {};
cache['userId'] = { name: 'Alice' };
let requestCount = 0;
requestCount++;
Correct :
import { vcr } from '@vonage/vcr-sdk';
const state = vcr.getInstanceState();
await state.set('userId', { name: 'Alice' });
await state.increment('requestCount', 1);
Choisir le bon champ d'application national
| Champ d'application | Méthode d'accès | Visibilité |
|---|---|---|
| Session | new State(session) | Une seule session |
| Instance | vcr.getInstanceState() | Toutes les répliques de cette instance déployée |
| Account | vcr.getAccountState() | Toutes les Applications du compte Vonage |
vcr.getInstanceState() est la valeur par défaut correcte pour l'état partagé. Il s'agit d'une enveloppe de commodité qui attribue à l'état l'ID de l'instance actuelle, de sorte que toutes les répliques d'un même déploiement partagent le même magasin.
vcr.getAccountState() est destiné aux données inter-applications, telles qu'une liste de blocage partagée ou une configuration que plusieurs applications de magnétoscope doivent lire.
État de la session (new State(session)) est approprié pour les données qui appartiennent à une seule interaction avec l'utilisateur, comme le contexte de la conversation ou l'état de l'appel.
Modèles courants de mémoire à remplacer
| Modèle | Remplacement |
|---|---|
const cache = {} au niveau du module | vcr.getInstanceState() |
| Objet singleton contenant l'état de la demande | vcr.getInstanceState() ou vcr.createSessionWithId(userId) |
global.something = ... | vcr.getInstanceState() |
| Compteur incrémenté par demande | state.increment('counter', 1) |
Exemples de codes
Node.js - compteur partagé entre les répliques :
import { vcr } from '@vonage/vcr-sdk';
const state = vcr.getInstanceState();
// Increment atomically — safe across replicas
await state.increment('requestCount', 1);
const count = await state.get('requestCount');
Node.js - état de la session par utilisateur :
import { vcr, State } from '@vonage/vcr-sdk';
app.post('/message', async (req, res) => {
// Each user gets an isolated state namespace
const session = vcr.createSessionWithId(req.body.userId);
const state = new State(session);
await state.set('lastSeen', new Date().toISOString());
res.sendStatus(200);
});
Python - état partagé entre les répliques :
from vonage_cloud_runtime.vcr import VCR
vcr = VCR()
state = vcr.getInstanceState()
await state.set('key', 'value')
value = await state.get('key')
Meilleures pratiques en matière de définition de la portée de la session
Critique : Mauvaise utilisation vcr.createSession() est l'une des sources les plus courantes de bogues dans les applications de magnétoscope. Il est essentiel de comprendre le cadrage des sessions pour construire des applications qui s'adaptent correctement.
Le problème de la vcr.createSession() à Global Scope
vcr.createSession() génère un aléatoire, éphémère à chaque fois qu'il est appelé. Si vous l'appelez au niveau du module/global et que vous utilisez la session résultante avec la commande new State(session):
- Chaque réplique crée sa propre session aléatoire au démarrage
- Les données écrites par une réplique sont invisible à tous les autres
- Lors d'un redémarrage ou d'une mise à zéro, l'identifiant de session est perdu et les données deviennent définitivement orphelines.
- Cela va complètement à l'encontre de l'objectif d'utiliser le fournisseur d'État pour les données partagées.
Anti-modèle - NE PAS faire cela :
import { vcr, State } from '@vonage/vcr-sdk';
// WRONG: random session at global scope
const session = vcr.createSession();
const state = new State(session);
app.post('/message', async (req, res) => {
// This state is only accessible by this exact replica, using this exact session ID.
// Other replicas have their own random session and cannot see this data.
await state.set('messageCount', (await state.get('messageCount') || 0) + 1);
res.sendStatus(200);
});
Modèles corrects
Pour un état partagé entre toutes les répliques, utilisez l'état de l'instance.
import { vcr } from '@vonage/vcr-sdk';
// Shared across all replicas of this instance — no session needed
const state = vcr.getInstanceState();
app.post('/message', async (req, res) => {
await state.increment('messageCount', 1);
res.sendStatus(200);
});
Pour l'état par conversation/par utilisateur - utiliser des identifiants de session déterministes
Étendre les sessions au contexte de la demande en utilisant les identifiants des rappels. Cela permet de s'assurer que toute réplique traitant une demande ultérieure pour la même conversation/le même utilisateur peut accéder au même état.
import { vcr, State } from '@vonage/vcr-sdk';
// Voice: scope to the conversation UUID from the callback
app.post('/onCall', async (req, res) => {
const session = vcr.createSessionWithId(req.body.conversation_uuid);
const state = new State(session);
await state.set('step', 'greeting');
await state.set('startTime', Date.now());
res.json([{ action: 'talk', text: 'Welcome!' }]);
});
// SMS: scope to the sender's phone number
app.post('/onMessage', async (req, res) => {
const session = vcr.createSessionWithId(req.body.from);
const state = new State(session);
await state.increment('messageCount', 1);
await state.set('lastMessage', req.body.text);
res.sendStatus(200);
});
L'idée clé : l'identifiant de session doit être déterministe et dérivable du contexte de la demande. Les bons identifiants de session sont les suivants :
| Source | Exemple d'ID | Cas d'utilisation |
|---|---|---|
| Rappel vocal | conversation_uuid | État de l'IVR par appel |
| Rappel de SMS/MMS | numéro de téléphone de l'expéditeur | Historique des conversations par expéditeur |
| Utilisateur authentifié | ID de l'utilisateur ou sujet JWT | Préférences par utilisateur |
| Corrélation personnalisée | ID de la commande, ID du billet | État par flux de travail |
Pour l'enregistrement d'un abonnement - utiliser la session globale
vcr.getGlobalSession() renvoie une session déterministe partagée par toutes les répliques. L'utiliser seulement pour enregistrer les abonnements des fournisseurs (voix, messages), et non pour stocker l'état de l'application.
import { vcr, Voice, Messages } from '@vonage/vcr-sdk';
// Correct: global session for subscription registration
const globalSession = vcr.getGlobalSession();
const voice = new Voice(globalSession);
const messages = new Messages(globalSession);
await voice.onCall('onCall');
await messages.onMessage('onMessage',
{ type: 'sms', number: process.env.VONAGE_NUMBER },
{ type: 'sms', number: undefined }
);
Résumé
| Besoin | Méthode | Champ d'application |
|---|---|---|
| Compteurs partagés, caches, configuration | vcr.getInstanceState() | Toutes les répliques |
| État par utilisateur/par conversation | vcr.createSessionWithId(id) + new State(session) | Déterministe, à l'échelle de la demande |
| Données partagées entre applications | vcr.getAccountState() | Toutes les applications dans le Account |
| Enregistrement de l'abonnement | vcr.getGlobalSession() | Singleton à l'échelle de l'instance |
| Jamais à l'échelle mondiale pour l'État | vcr.createSession() | Aléatoire, inaccessible aux autres répliques |