Zustandslose Architektur

VCR führt mehrere Replikate jeder Anwendung aus und kann unter Last weiter skalieren. Replikate können im Leerlauf auch auf Null heruntergefahren werden. Dies hat wichtige Auswirkungen darauf, wie Sie den Status verwalten.

Warum In-Memory-Status unsicher ist

Alle im Speicher abgelegten Daten (globale Variablen, Objekte auf Modulebene, prozessinterne Caches) sind:

  • Nur lokal für eine Replik
  • Bei Neustart oder Skalierung auf Null verloren
  • Unsichtbar für andere Replikate, die gleichzeitige Anfragen bearbeiten

Gehen Sie nie davon aus, dass zwei Anfragen auf dieselbe Replik treffen. Wenn dies heute der Fall ist, wird dies bei zunehmender Last nicht mehr der Fall sein.

Falsch:

// Replica-local — invisible to other replicas and lost on restart
const cache = {};
cache['userId'] = { name: 'Alice' };

let requestCount = 0;
requestCount++;

Richtig:

import { vcr } from '@vonage/vcr-sdk';

const state = vcr.getInstanceState();
await state.set('userId', { name: 'Alice' });
await state.increment('requestCount', 1);

Die Wahl des richtigen staatlichen Geltungsbereichs

Umfang Zugriffsmethode Sichtbarkeit
Sitzung new State(session) Nur eine Sitzung
Instanz vcr.getInstanceState() Alle Replikate dieser bereitgestellten Instanz
Account vcr.getAccountState() Alle Applications im Vonage Account

vcr.getInstanceState() ist der korrekte Standard für gemeinsam genutzte Zustände. Es handelt sich um einen praktischen Wrapper, der den Status auf die aktuelle Instanz-ID ausweitet, so dass alle Repliken desselben Deployments denselben Speicher nutzen.

vcr.getAccountState() ist für anwendungsübergreifende Daten gedacht, z. B. eine gemeinsame Blockliste oder Konfiguration, die mehrere VCR-Anwendungen lesen müssen.

Zustand der Sitzung (new State(session)) ist für Daten geeignet, die zu einer einzelnen Benutzerinteraktion gehören, wie z. B. Gesprächskontext oder Anrufstatus.

Häufig zu ersetzende In-Memory-Muster

Muster Ersatz
const cache = {} auf Modulebene vcr.getInstanceState()
Singleton-Objekt, das den Status der Anfrage enthält vcr.getInstanceState() oder vcr.createSessionWithId(userId)
global.something = ... vcr.getInstanceState()
Zähler wird pro Anfrage inkrementiert state.increment('counter', 1)

Code-Beispiele

Node.js - gemeinsamer Zähler für alle Replikate:

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 - Sitzungsstatus pro Benutzer:

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 - gemeinsamer Status für alle Replikate:

from vonage_cloud_runtime.vcr import VCR

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

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

Best Practices für den Sitzungsrahmen

Kritisch: Missbrauch von vcr.createSession() ist eine der häufigsten Ursachen für Fehler in VCR Applications. Das Verständnis von Session Scoping ist für die Erstellung von Anwendungen, die korrekt skalieren, unerlässlich.

Das Problem mit vcr.createSession() bei Global Scope

vcr.createSession() erzeugt eine zufällig, kurzlebig Sitzungs-ID jedes Mal, wenn sie aufgerufen wird. Wenn Sie es im Modul/Globalbereich aufrufen und die resultierende Sitzung mit new State(session):

  • Jede Replik erstellt beim Start ihre eigene zufällige Sitzung
  • Die von einer Replik geschriebenen Daten sind unsichtbar für alle anderen
  • Bei einem Neustart oder einer Skalierung auf Null geht die Sitzungs-ID verloren und die Daten werden dauerhaft verwaist.
  • Dies macht den Zweck der Nutzung des staatlichen Anbieters für gemeinsame Daten völlig zunichte

Anti-Muster - Tun Sie dies NICHT:

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);
});

Korrekte Muster

Für einen gemeinsamen Status für alle Replikate - Instanzstatus verwenden

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);
});

Für den Status pro Gespräch/Benutzer - deterministische Sitzungs-IDs verwenden

Zuordnung von Sitzungen zum Anfragekontext mit Hilfe von IDs aus Rückrufen. Dadurch wird sichergestellt, dass jede Replik, die eine nachfolgende Anfrage für dieselbe Konversation/denselben Benutzer bearbeitet, auf denselben Status zugreifen kann.

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);
});

Die wichtigste Erkenntnis: die Sitzungs-ID muss deterministisch und aus dem Anfragekontext ableitbar sein. Gute Sitzungs-IDs sind beispielsweise:

Quelle Beispiel-ID Anwendungsfall
Sprachrückruf conversation_uuid IVR-Status pro Anruf
SMS/MMS-Rückruf Absender-Telefonnummer Gesprächsverlauf pro Absender
Authentifizierter Benutzer Benutzer-ID oder JWT-Betreff Pro-Benutzer-Einstellungen
Benutzerdefinierte Korrelation Bestell-ID, Ticket-ID Status pro Arbeitsablauf

Für die Registrierung von Abonnements - verwenden Sie Global Session

vcr.getGlobalSession() gibt eine deterministische Sitzung zurück, die von allen Replikaten gemeinsam genutzt wird. Verwenden Sie nur zur Registrierung von Provider-Abonnements (Sprache, Nachrichten), nicht zur Speicherung des Anwendungsstatus.

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 }
);

Zusammenfassung

Bedarf Methode Umfang
Gemeinsame Zähler, Caches, Konfiguration vcr.getInstanceState() Alle Repliken
Status pro Benutzer/Konversation vcr.createSessionWithId(id) + new State(session) Deterministisch, anforderungsspezifisch
Anwendungsübergreifende gemeinsame Daten vcr.getAccountState() Alle Anwendungen im Account
Anmeldung zum Abonnement vcr.getGlobalSession() Instanzweites Singleton
Niemals in globalem Umfang für den Staat vcr.createSession() Zufällig, unerreichbar für andere Replikate