Proveedor estatal

El proveedor State te permite almacenar y recuperar datos en la plataforma Vonage Cloud Runtime. Admite almacenamiento de valores clave, mapas hash, listas ordenadas y búsqueda de texto completo, todo ello respaldado por Redis.

Inicialización

Advertencia: Visite no llame a vcr.createSession() en el ámbito global/módulo para inicializar State. Esto crea un ID de sesión aleatorio y efímero - los datos almacenados bajo él serán invisibles para otras réplicas y otras peticiones. Véase Alcance de la sesión abajo.

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:

Se puede pasar un prefijo de espacio de nombres opcional como segundo argumento para evitar colisiones de claves:

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

Ámbito estatal

Importante: Los datos de estado se asignan a la sesión utilizada para crear la instancia de estado. Si utiliza una sesión aleatoria (vcr.createSession()), los datos sólo son accesibles con ese ID de sesión exacto. A menos que persista y comparta ese ID, ninguna otra solicitud o réplica podrá acceder a los datos. Elija siempre su sesión deliberadamente.

Alcance Cómo acceder Visibilidad
Estado de la sesión new State(session) Aislado a la sesión específica. Se elimina cuando expira el TTL de la sesión.
Estado de la instancia vcr.getInstanceState() Compartido por todas las réplicas de la instancia desplegada.
Estado de la Account vcr.getAccountState() Compartido en todas las aplicaciones de la cuenta de Vonage.

Cuándo utilizar cada ámbito:

  • Estado de la instancia - Utilícelo para contadores compartidos, cachés, configuración o cualquier dato que todas las réplicas necesiten leer/escribir. Este es el valor predeterminado correcto para la mayoría de los estados compartidos.
  • Estado de la sesión con ID determinista - Se utiliza para datos relativos a una conversación, un usuario o un flujo de trabajo específicos. Cree la sesión con vcr.createSessionWithId(id) utilizando un ID del contexto de la solicitud (por ejemplo, conversation_uuidnúmero de teléfono del remitente, ID de usuario).
  • Estado de la Account - Se utiliza para datos entre aplicaciones, como listas de bloqueo o configuraciones compartidas.
// 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();

Véase Arquitectura sin Estado para obtener información detallada sobre el alcance de las sesiones y los errores más comunes.

Operaciones clave-valor

Método Firma Devuelve Descripción
set set<T>(key, value) Promise<string> ("OK") Almacenar un valor bajo una clave
get get<T>(key) Promise<T> Recuperar el valor de una clave
delete delete(key) Promise<string> ("1"/"0") Borra una tecla. Devuelve "1" si se ha borrado, "0" si no se ha encontrado
increment increment(key, value) Promise<string> Incrementar atómicamente una tecla numérica en value
decrement decrement(key, value) Promise<string> Disminuir atómicamente una tecla numérica en value
expire expire(key, seconds, option?) Promise<string> ("1"/"0") Establecer el TTL de una clave en segundos

Opciones de expiración

La opción option parámetro en expire acepta un EXPIRE_OPTION valor enum:

Opción Descripción
NX Establecer caducidad sólo si la clave no tiene caducidad existente
XX Fijar caducidad sólo si la clave ya tiene caducidad
GT Fijar caducidad sólo si la nueva caducidad es mayor que la actual
LT Fijar caducidad sólo si la nueva caducidad es inferior a la actual
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"

Operaciones HashMap

Operar sobre los campos de una tabla hash con nombre.

Método Firma Devuelve Descripción
mapSet mapSet(table, keyValuePairs) Promise<string> Establecer uno o varios pares clave-valor en una tabla hash
mapGetValue mapGetValue(table, key) Promise<string> Obtener un único valor de campo
mapGetMultiple mapGetMultiple(table, keys) Promise<string[]> Obtener varios valores de campo por clave
mapGetAll mapGetAll(table) Promise<Record<string, string>> Obtener todos los campos y valores
mapGetValues mapGetValues(table) Promise<string[]> Obtener todos los valores (sin claves)
mapDelete mapDelete(table, keys) Promise<string> Borrar uno o varios campos
mapExists mapExists(table, key) Promise<string> ("1"/"0") Comprobar si existe un campo
mapIncrement mapIncrement(table, key, value) Promise<string> Incrementar atómicamente el valor de un campo
mapLength mapLength(table) Promise<string> Obtener el número de campos de la tabla
mapScan mapScan(table, cursor, pattern?, count?) Promise<[string, string[]]> Iterar campos con un cursor
// 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']);

Escanear una tabla Hash

mapScan itera sobre los campos utilizando un cursor. Comienza en "0" y continuar hasta que el cursor devuelto sea "0" otra vez:

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

Lista de operaciones

Almacenamiento de listas ordenadas respaldado por listas Redis.

Método Firma Devuelve Descripción
listAppend listAppend<T>(list, value) Promise<string> Añadir un valor al final de la lista
listPrepend listPrepend<T>(list, value) Promise<string> Añadir un valor al principio de la lista
listEndPop listEndPop<T>(list, count?) Promise<T[]> Eliminar y devolver valores del final (por defecto: 1)
listStartPop listStartPop<T>(list, count?) Promise<T[]> Eliminar y devolver valores desde el principio (por defecto: 1)
listRemove listRemove<T>(list, value, count?) Promise<string> Eliminar apariciones de un valor. Positivo countdesde la cabeza; negativo: desde la cola; 0: todos
listTrim listTrim(list, startPos, endPos) Promise<string> ("OK") Recorta la lista al rango especificado
listInsert listInsert<T>(list, before, pivot, value) Promise<string> Insertar un valor antes de (true) o después (false) un valor de pivote
listIndex listIndex<T>(list, position) Promise<T> Obtener el valor en una posición
listSet listSet<T>(list, position, value) Promise<string> ("OK") Fijar el valor en una posición
listLength listLength(list) Promise<string> Obtener el número de elementos de la lista
listRange listRange<T>(list, startPos?, endPos?) Promise<T[]> Obtener un rango de valores (por defecto: lista completa)
// 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);

Búsqueda de texto completo

Crear índices de búsqueda sobre los datos del mapa hash almacenados en State.

Método Firma Devuelve Descripción
createIndex createIndex(name, options) Promise<string> Crear un índice de búsqueda
search search(index, query, options?) Promise<string> Buscar en un índice
dropIndex dropIndex(index, deleteDocs?) Promise<boolean> Borrar un índice. Pasar true para eliminar también los documentos indexados

Creación de un índice

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

Buscar en

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

Eliminación de un índice

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