Arquitectura sin Estado
VCR ejecuta varias réplicas de cada aplicación y puede ampliarse aún más cuando hay carga. Las réplicas también pueden reducirse a cero cuando están inactivas. Esto tiene importantes implicaciones para la forma de gestionar el estado.
Por qué el estado en memoria no es seguro
Cualquier dato almacenado en la memoria (variables globales, objetos a nivel de módulo, cachés del proceso) es:
- Limitado a una sola réplica
- Datos perdidos al reiniciar o al poner a cero la báscula
- Invisible para otras réplicas que gestionan solicitudes simultáneas
Nunca des por sentado que dos solicitudes llegarán a la misma réplica. Aunque hoy sea así, esto no seguirá siendo así a medida que aumente la carga.
Equivocada:
// Replica-local — invisible to other replicas and lost on restart
const cache = {};
cache['userId'] = { name: 'Alice' };
let requestCount = 0;
requestCount++;
Correcto:
import { vcr } from '@vonage/vcr-sdk';
const state = vcr.getInstanceState();
await state.set('userId', { name: 'Alice' });
await state.increment('requestCount', 1);
Elegir el ámbito estatal adecuado
| Ámbito de aplicación | Método de acceso | Visibilidad |
|---|---|---|
| Sesión | new State(session) | Una sola sesión |
| Instancia | vcr.getInstanceState() | Todas las réplicas de esta instancia desplegada |
| Account | vcr.getAccountState() | Todas las aplicaciones de la cuenta de Vonage |
vcr.getInstanceState() es el valor predeterminado correcto para el estado compartido. Se trata de una envoltura práctica que asigna el estado al ID de instancia actual, de modo que todas las réplicas de la misma implementación comparten el mismo almacén.
vcr.getAccountState() es para datos entre aplicaciones, como una lista de bloqueo compartida o una configuración que varias aplicaciones VCR necesitan leer.
Estado de la sesión (new State(session)) resulta adecuado para datos que corresponden a una única interacción del usuario, como el contexto de una conversación o el estado de una llamada.
Patrones habituales en memoria que deben sustituirse
| Patrón | Sustitución |
|---|---|
const cache = {} a nivel de módulo | vcr.getInstanceState() |
| Objeto Singleton que contiene el estado de la solicitud | vcr.getInstanceState() o vcr.createSessionWithId(userId) |
global.something = ... | vcr.getInstanceState() |
| Contador incrementado por petición | state.increment('counter', 1) |
Ejemplos de códigos
Node.js — contador compartido entre réplicas:
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 - estado de sesión por usuario:
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 - estado compartido entre réplicas:
Mejores prácticas para la organización de sesiones
Crítico: Uso indebido vcr.createSession() es una de las fuentes más comunes de errores en las aplicaciones VCR. Comprender el alcance de las sesiones es esencial para crear aplicaciones que escalen correctamente.
El problema con vcr.createSession() a escala mundial
vcr.createSession() genera un aleatorio, efímero ID de sesión cada vez que se invoca. Si lo invocas en el ámbito del módulo o global y utilizas la sesión resultante con new State(session):
- Cada réplica crea su propia sesión aleatoria al iniciarse
- Los datos escritos por una réplica son invisible a todos los demás
- Al reiniciar o escalar a cero, el identificador de sesión se pierde y los datos quedan huérfanos permanentemente.
- Esto va totalmente en contra del objetivo de utilizar el proveedor estatal para los datos compartidos.
Anti-patrón - NO hagas esto:
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);
});
Patrones correctos
Para el estado compartido en todas las réplicas, utilice el estado de instancia
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);
});
Para el estado por conversación/por usuario: utilice identificadores de sesión deterministas.
Sesiones de alcance para solicitar contexto utilizando IDs de devoluciones de llamada. Esto garantiza que cualquier réplica que gestione una solicitud posterior para la misma conversación/usuario pueda acceder al mismo estado.
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);
});
La idea clave: el ID de sesión debe ser determinista y derivable del contexto de la solicitud. Algunos ejemplos de identificadores de sesión adecuados son:
| Fuente | ID de ejemplo | Caso práctico |
|---|---|---|
| Retrollamada de voz | conversation_uuid | Estado del IVR por llamada |
| Devolución de llamada por SMS/MMS | número de teléfono del remitente | Historial de conversaciones por remitente |
| Usuario autenticado | ID de usuario o asunto JWT | Preferencias de cada usuario |
| Correlación personalizada | ID del pedido, ID del billete | Estado por flujo de trabajo |
Para registrarse en la suscripción, utilice «Global Session»
vcr.getGlobalSession() devuelve una sesión determinista compartida por todas las réplicas. Utilícela solo para registrar suscripciones de proveedores (voz, mensajes), no para almacenar el estado de la aplicación.
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 }
);
Resumen
| Necesidad | Método | Ámbito de aplicación |
|---|---|---|
| Contadores compartidos, cachés, configuración | vcr.getInstanceState() | Todas las réplicas |
| Estado por usuario/por conversación | vcr.createSessionWithId(id) + new State(session) | Determinista, con ámbito de solicitud |
| Datos compartidos entre aplicaciones | vcr.getAccountState() | Todas las aplicaciones en Account |
| Registro de suscripción | vcr.getGlobalSession() | Singleton a nivel de instancia |
| Nunca a escala mundial para el Estado | vcr.createSession() | Aleatorio, inalcanzable por otras réplicas |