Prestador estadual

O provedor State permite que você armazene e recupere dados na plataforma Vonage Cloud Runtime. Ele oferece suporte a armazenamento chave-valor, mapas de hash, listas ordenadas e pesquisa de texto completo — tudo com o Redis como base.

Inicialização

Aviso: Faça não chamada vcr.createSession() no escopo global/módulo para inicializar o State. Isso cria um ID de sessão aleatório e efêmero — os dados armazenados nele ficarão invisíveis para outras réplicas e outras solicitações. Consulte Escopo da sessão abaixo.

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)

Um prefixo de namespace opcional pode ser passado como segundo argumento para evitar colisões de chaves:

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

Análise do âmbito estadual

Importante: Os dados de estado estão restritos à sessão usada para criar a instância de State. Se você usar uma sessão aleatória (vcr.createSession()), os dados só podem ser acessados com esse ID de sessão específico. A menos que você armazene e compartilhe esse ID, nenhuma outra solicitação ou réplica poderá acessar os dados. Escolha sempre sua sessão com cuidado.

Âmbito Como acessar Visibilidade
Estado da sessão new State(session) Restrito à sessão específica. Excluído quando o TTL da sessão expirar.
Estado da instância vcr.getInstanceState() Compartilhado entre todas as réplicas da instância implantada.
Status da conta vcr.getAccountState() Compartilhado entre todas as aplicações da conta da Vonage.

Quando usar cada escopo:

  • Estado da instância — Use para contadores compartilhados, caches, configuração ou quaisquer dados que todas as réplicas precisem ler ou gravar. Essa é a configuração padrão correta para a maioria dos estados compartilhados.
  • Estado da sessão com ID determinístico — Use para dados restritos a uma conversa, usuário ou fluxo de trabalho específico. Crie a sessão com vcr.createSessionWithId(id) usando um ID do contexto da solicitação (por exemplo, conversation_uuid, número de telefone do remetente, ID do usuário).
  • Status da conta — Utilize para dados comuns a várias aplicações, como listas de bloqueio compartilhadas ou configurações.
// 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();

Veja Arquitetura sem estado para obter orientações detalhadas sobre a definição do escopo da sessão e as armadilhas mais comuns.

Operações de chave-valor

Método Assinatura Devoluções Descrição
set set<T>(key, value) Promise<string> ("OK") Armazenar um valor associado a uma chave
get get<T>(key) Promise<T> Recuperar o valor de uma chave
delete delete(key) Promise<string> ("1"/"0") Excluir uma chave. Retorna “1” se for excluída, “0” se não for encontrada
increment increment(key, value) Promise<string> Incrementar uma chave numérica atomicamente em value
decrement decrement(key, value) Promise<string> Decrementar atomicamente uma chave numérica em value
expire expire(key, seconds, option?) Promise<string> ("1"/"0") Definir um TTL para uma chave em segundos

Opções de validade

O opcional option parâmetro ativado expire aceita um EXPIRE_OPTION valor da enumeração:

Opção Descrição
NX Definir a data de validade somente se a chave não tiver uma data de validade definida
XX Definir a data de validade somente se a chave já tiver uma data de validade
GT Definir a data de validade somente se a nova data for posterior à atual
LT Definir a data de validade somente se a nova data for anterior à atual
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"

Operações com HashMap

Realizar operações nos campos de uma tabela hash nomeada.

Método Assinatura Devoluções Descrição
mapSet mapSet(table, keyValuePairs) Promise<string> Definir um ou mais pares chave-valor em uma tabela hash
mapGetValue mapGetValue(table, key) Promise<string> Obter o valor de um único campo
mapGetMultiple mapGetMultiple(table, keys) Promise<string[]> Obter vários valores de campo por chave
mapGetAll mapGetAll(table) Promise<Record<string, string>> Obter todos os campos e valores
mapGetValues mapGetValues(table) Promise<string[]> Obter todos os valores (sem chaves)
mapDelete mapDelete(table, keys) Promise<string> Excluir um ou mais campos
mapExists mapExists(table, key) Promise<string> ("1"/"0") Verificar se um campo existe
mapIncrement mapIncrement(table, key, value) Promise<string> Incrementar o valor de um campo de forma atômica
mapLength mapLength(table) Promise<string> Obter o número de campos da tabela
mapScan mapScan(table, cursor, pattern?, count?) Promise<[string, string[]]> Percorrer campos com um 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']);

Varredura de uma tabela hash

mapScan percorre os campos usando um cursor. Começa em "0" e continuar até que o cursor retornado seja "0" novamente:

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

Operações com listas

Armazenamento de lista ordenada com suporte em listas do Redis.

Método Assinatura Devoluções Descrição
listAppend listAppend<T>(list, value) Promise<string> Adicionar um valor ao final da lista
listPrepend listPrepend<T>(list, value) Promise<string> Adicionar um valor ao início da lista
listEndPop listEndPop<T>(list, count?) Promise<T[]> Remover e retornar valores a partir do final (padrão: 1)
listStartPop listStartPop<T>(list, count?) Promise<T[]> Remover e retornar valores a partir do início (padrão: 1)
listRemove listRemove<T>(list, value, count?) Promise<string> Remover as ocorrências de um valor. Positivo count: a partir da cabeça; negativo: a partir da cauda; 0: todos
listTrim listTrim(list, startPos, endPos) Promise<string> ("OK") Limitar a lista ao intervalo especificado
listInsert listInsert<T>(list, before, pivot, value) Promise<string> Insira um valor antes de (true) ou depois de (false) um valor de referência
listIndex listIndex<T>(list, position) Promise<T> Obter o valor em uma posição
listSet listSet<T>(list, position, value) Promise<string> ("OK") Definir o valor em uma posição
listLength listLength(list) Promise<string> Obter o número de elementos da lista
listRange listRange<T>(list, startPos?, endPos?) Promise<T[]> Obter um intervalo de valores (padrão: 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);

Pesquisa de texto completo

Crie índices pesquisáveis sobre os dados do mapa de hash armazenados em State.

Método Assinatura Devoluções Descrição
createIndex createIndex(name, options) Promise<string> Criar um índice de pesquisa
search search(index, query, options?) Promise<string> Pesquisar em um índice
dropIndex dropIndex(index, deleteDocs?) Promise<boolean> Excluir um índice. Passar true para excluir também os documentos indexados

Criação de um í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 },
  ],
});

Pesquisando

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

Excluir um í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);