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