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