Arquitectura sin Estado

VCR ejecuta múltiples réplicas de cada aplicación, y puede escalar aún más bajo carga. Las réplicas también pueden reducirse a cero cuando están inactivas. Esto tiene importantes implicaciones para la gestión del estado.

Por qué el estado en memoria no es seguro

Cualquier dato almacenado en memoria (variables globales, objetos a nivel de módulo, cachés en proceso) lo está:

  • Local a una sola réplica
  • Pérdida al reiniciar o escala a cero
  • Invisible para otras réplicas que gestionan solicitudes simultáneas

Nunca des por sentado que dos peticiones van a coincidir en la misma réplica. Si lo hacen hoy, no será así cuando 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

Alcance 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)) es apropiado para datos que pertenecen a una única interacción del usuario, como el contexto de la conversación o el estado de la llamada.

Patrones comunes en memoria que hay que sustituir

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:

from vonage_cloud_runtime.vcr import VCR

vcr = VCR()
state = vcr.getInstanceState()

await state.set('key', 'value')
value = await state.get('key')

Mejores prácticas para la organización de sesiones

Crítico: Uso indebido de 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() en Global Scope

vcr.createSession() genera un aleatorio, efímero cada vez que se llama. Si se llama en el ámbito de módulo/global y se utiliza 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 anula por completo el objetivo de utilizar el proveedor estatal de 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. Los buenos identificadores de sesión son:

Fuente Ejemplo ID Caso práctico
Retrollamada de voz conversation_uuid Estado IVR por llamada
Devolución de llamada SMS/MMS número de teléfono del remitente Historial de conversaciones por remitente
Usuario autenticado ID de usuario o asunto JWT Preferencias por usuario
Correlación personalizada ID del pedido, ID del billete Estado por flujo de trabajo

Para el registro de suscripciones, utilice la sesión global

vcr.getGlobalSession() devuelve una sesión determinista compartida por todas las réplicas. Utilícela sólo 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

Necesita Método Alcance
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
Inscripción vcr.getGlobalSession() Singleton para toda la instancia
Nunca a escala mundial para el Estado vcr.createSession() Aleatorio, inalcanzable por otras réplicas