État fournisseur

Le fournisseur State vous permet de stocker et d'extraire des données sur la plateforme Vonage Cloud Runtime. Il prend en charge le stockage de valeurs clés, les cartes de hachage, les listes ordonnées et la recherche en texte intégral, le tout soutenu par Redis.

Initialisation

Avertissement : Faire pas appel vcr.createSession() au niveau global/module pour initialiser l'état. Cela crée un identifiant de session aléatoire et éphémère - les données stockées sous cet identifiant seront invisibles pour les autres répliques et les autres requêtes. Voir Définition du champ d'application de la session ci-dessous.

Node.js :

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

// WRONG — random session, data siloed to this replica
// const session = vcr.createSession();
// const state = new State(session);

// CORRECT — shared state across all replicas
const state = vcr.getInstanceState();

// CORRECT — per-conversation state with a deterministic ID
app.post('/onCall', async (req, res) => {
  const session = vcr.createSessionWithId(req.body.conversation_uuid);
  const state = new State(session);
  // ...
});

Python :

from vonage_cloud_runtime.vcr import VCR
from vonage_cloud_runtime.providers.state.state import State

vcr = VCR()

# WRONG — random session, data siloed to this replica
# session = vcr.createSession()
# state = State(session)

# CORRECT — shared state across all replicas
state = vcr.getInstanceState()

# CORRECT — per-conversation state with a deterministic ID
session = vcr.createSessionWithId(conversation_uuid)
state = State(session)

Un préfixe d'espace de noms facultatif peut être transmis comme deuxième argument pour éviter les collisions de clés :

const state = new State(session, 'user:123:');

Portée de l'État

Important : Les données de l'État sont liées à la session utilisée pour créer l'instance de l'État. Si vous utilisez une session aléatoire (vcr.createSession()), les données ne sont accessibles qu'avec cet identifiant de session exact. À moins que vous ne persistiez et ne partagiez cet identifiant, aucune autre requête ou réplique ne peut accéder aux données. Choisissez toujours votre session délibérément.

Champ d'application Comment accéder Visibilité
État de la session new State(session) Isolé à la session spécifique. Supprimé à l'expiration du TTL de la session.
État de l'instance vcr.getInstanceState() Partagé entre toutes les répliques de l'instance déployée.
État du compte vcr.getAccountState() Partagé entre toutes les Applications du compte Vonage.

Quand utiliser chaque champ d'application :

  • État de l'instance - À utiliser pour les compteurs partagés, les caches, la configuration ou toute autre donnée que toutes les répliques doivent lire/écrire. Il s'agit de la valeur par défaut correcte pour la plupart des données partagées.
  • État de la session avec ID déterministe - À utiliser pour les données liées à une conversation, un utilisateur ou un flux de travail spécifique. Créer la session avec vcr.createSessionWithId(id) à l'aide d'un identifiant provenant du contexte de la demande (par ex, conversation_uuidnuméro de téléphone de l'expéditeur, identifiant de l'utilisateur).
  • État du compte - À utiliser pour les données inter-applications telles que les listes de blocs partagées ou la configuration.
// Session state — per-conversation, using a deterministic ID from a callback
const session = vcr.createSessionWithId(req.body.conversation_uuid);
const conversationState = new State(session);

// Instance state — shared across all replicas
const instanceState = vcr.getInstanceState();

// Account state — shared across all applications
const accountState = vcr.getAccountState();

Voir Architecture sans état pour obtenir des conseils détaillés sur la définition de la portée de la session et les pièges les plus courants.

Opérations clé-valeur

Méthode Signature Retours Description
set set<T>(key, value) Promise<string> ("OK") Stocker une valeur sous une clé
get get<T>(key) Promise<T> Récupérer la valeur d'une clé
delete delete(key) Promise<string> ("1"/"0") Supprime une clé. Renvoie "1" si la clé est supprimée, "0" si elle n'est pas trouvée.
increment increment(key, value) Promise<string> Incrémentation atomique d'une touche numérique par value
decrement decrement(key, value) Promise<string> Décrémenter atomiquement une touche numérique de value
expire expire(key, seconds, option?) Promise<string> ("1"/"0") Définir un TTL sur une clé en secondes

Options d'expiration

L'option option paramètre sur expire accepte un EXPIRE_OPTION valeur de l'énumération :

Option Description
NX Fixer l'expiration seulement si la clé n'a pas d'expiration existante
XX Fixer l'expiration seulement si la clé a déjà une expiration
GT Fixer l'échéance uniquement si la nouvelle échéance est supérieure à l'actuelle
LT Fixer l'échéance uniquement si la nouvelle échéance est inférieure à l'actuelle
import { vcr, State, EXPIRE_OPTION } from '@vonage/vcr-sdk';

const state = vcr.getInstanceState();

await state.set('counter', 0);
const value = await state.get('counter');       // 0

await state.increment('counter', 5);            // "5"
await state.decrement('counter', 2);            // "3"

await state.expire('counter', 3600);            // expires in 1 hour
await state.expire('counter', 600, EXPIRE_OPTION.LT); // only if < current TTL

await state.delete('counter');                  // "1"

Opérations HashMap

Opérer sur les champs d'une table de hachage nommée.

Méthode Signature Retours Description
mapSet mapSet(table, keyValuePairs) Promise<string> Définir une ou plusieurs paires clé-valeur dans une table de hachage
mapGetValue mapGetValue(table, key) Promise<string> Obtenir la valeur d'un seul champ
mapGetMultiple mapGetMultiple(table, keys) Promise<string[]> Obtenir les valeurs de plusieurs champs par clé
mapGetAll mapGetAll(table) Promise<Record<string, string>> Obtenir tous les champs et toutes les valeurs
mapGetValues mapGetValues(table) Promise<string[]> Obtenir toutes les valeurs (sans les clés)
mapDelete mapDelete(table, keys) Promise<string> Supprimer un ou plusieurs champs
mapExists mapExists(table, key) Promise<string> ("1"/"0") Vérifier si un champ existe
mapIncrement mapIncrement(table, key, value) Promise<string> Incrémentation atomique de la valeur d'un champ
mapLength mapLength(table) Promise<string> Obtenir le nombre de champs dans la table
mapScan mapScan(table, cursor, pattern?, count?) Promise<[string, string[]]> Interroger les champs à l'aide d'un curseur
// Store a user profile as a hash map
await state.mapSet('user:123', {
  name: 'Alice',
  email: 'alice@example.com',
  loginCount: '0',
});

const name = await state.mapGetValue('user:123', 'name');       // "Alice"
const profile = await state.mapGetAll('user:123');               // { name, email, loginCount }
const values = await state.mapGetValues('user:123');             // ["Alice", "alice@example.com", "0"]
const [email, login] = await state.mapGetMultiple('user:123', ['email', 'loginCount']);

await state.mapIncrement('user:123', 'loginCount', 1);          // "1"
const exists = await state.mapExists('user:123', 'name');       // "1"
const length = await state.mapLength('user:123');               // "3"

await state.mapDelete('user:123', ['email']);

Analyse d'une table de hachage

mapScan parcourt les champs à l'aide d'un curseur. Commencer à "0" et continuer jusqu'à ce que le curseur retourné soit "0" encore une fois :

let cursor = '0';

do {
  const [nextCursor, fields] = await state.mapScan('user:123', cursor, 'name*', 10);
  cursor = nextCursor;
  console.log(fields); // alternating [field, value, field, value, ...]
} while (cursor !== '0');

Opérations de liste

Stockage de listes ordonnées soutenu par des listes Redis.

Méthode Signature Retours Description
listAppend listAppend<T>(list, value) Promise<string> Ajouter une valeur à la fin de la liste
listPrepend listPrepend<T>(list, value) Promise<string> Ajouter une valeur au début de la liste
listEndPop listEndPop<T>(list, count?) Promise<T[]> Retirer et renvoyer les valeurs de la fin (par défaut : 1)
listStartPop listStartPop<T>(list, count?) Promise<T[]> Retirer et renvoyer les valeurs depuis le début (par défaut : 1)
listRemove listRemove<T>(list, value, count?) Promise<string> Supprimer les occurrences d'une valeur. Positif count: à partir de la tête ; négatif : à partir de la queue ; 0 : tous
listTrim listTrim(list, startPos, endPos) Promise<string> ("OK") Découper la liste en fonction de la plage spécifiée
listInsert listInsert<T>(list, before, pivot, value) Promise<string> Insérer une valeur avant (true) ou après (false) une valeur pivot
listIndex listIndex<T>(list, position) Promise<T> Obtenir la valeur à une position donnée
listSet listSet<T>(list, position, value) Promise<string> ("OK") Fixer la valeur à une position
listLength listLength(list) Promise<string> Obtenir le nombre d'éléments de la liste
listRange listRange<T>(list, startPos?, endPos?) Promise<T[]> Obtenir une plage de valeurs (par défaut : liste complète)
// Build an activity log
await state.listAppend('activity', { action: 'login', ts: Date.now() });
await state.listAppend('activity', { action: 'purchase', ts: Date.now() });
await state.listPrepend('activity', { action: 'signup', ts: Date.now() });

const length = await state.listLength('activity');          // "3"
const all = await state.listRange('activity');              // all items
const last10 = await state.listRange('activity', -10, -1); // last 10 items

// Keep only the most recent 100 entries
await state.listTrim('activity', -100, -1);

// Remove and process items
const [item] = await state.listStartPop('activity');
const [last] = await state.listEndPop('activity');

// Insert before a known value
await state.listInsert('activity', true, { action: 'login' }, { action: 'pre-login' });

// Get and update by position
const first = await state.listIndex('activity', 0);
await state.listSet('activity', 0, { action: 'updated', ts: Date.now() });

// Remove all occurrences of a specific value
await state.listRemove('activity', { action: 'login' }, 0);

Recherche en texte intégral

Créer des index consultables sur les données des cartes de hachage stockées dans l'État.

Méthode Signature Retours Description
createIndex createIndex(name, options) Promise<string> Créer un index de recherche
search search(index, query, options?) Promise<string> Rechercher un index
dropIndex dropIndex(index, deleteDocs?) Promise<boolean> Supprimer un index. Passer true pour supprimer également les documents indexés

Création d'un index

await state.createIndex('users-index', {
  on: 'HASH',
  prefix: {
    count: 1,
    prefixes: ['user:'],
  },
  schema: [
    { fieldName: 'name', type: 'TEXT', sortable: true },
    { fieldName: 'email', type: 'TEXT' },
    { fieldName: 'loginCount', type: 'NUMERIC', sortable: true },
  ],
});

Recherche

// Full-text search
const results = await state.search('users-index', '@name:Alice');

// With options
const results = await state.search('users-index', '@name:Alice', {
  limit: { offset: 0, num: 10 },
  sortBy: { field: 'loginCount', order: 'DESC' },
  withScores: true,
});

Abandon d'un index

// Drop index only (keep the underlying data)
await state.dropIndex('users-index');

// Drop index and delete all indexed documents
await state.dropIndex('users-index', true);