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