州プロバイダー

Stateプロバイダは、Vonage Cloud Runtimeプラットフォーム上でデータの保存と取得を可能にします。キー・バリュー・ストレージ、ハッシュ・マップ、順序付きリスト、全文検索をサポートしており、これらはすべてRedisによってバックアップされています。

初期設定

警告だ: を行う。 違う コール vcr.createSession() をグローバル/モジュールスコープで実行して、State を初期化します。これはランダムで一時的なセッションIDを作成します。このIDの下に保存されたデータは、他のレプリカや他のリクエストからは見えなくなります。参照 セッション・スコープ の下にある。

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

パイソン

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)

オプションの名前空間プレフィックスは、キーの衝突を避けるために第2引数として渡すことができる:

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

ステート・スコープ

重要だ: ステート・データは、ステート・インスタンスの作成に使用されたセッションにスコープされる。ランダム・セッション(vcr.createSession())、データはその正確なセッションIDでのみアクセス可能です。そのIDを永続化し共有しない限り、他のリクエストやレプリカはそのデータにアクセスできません。セッションは常に慎重に選択してください。

スコープ アクセス方法 視認性
セッションの状態 new State(session) 特定のセッションに隔離される。セッションのTTLが切れると削除される。
インスタンス状態 vcr.getInstanceState() デプロイされたインスタンスのすべてのレプリカで共有される。
Accountの状態 vcr.getAccountState() Vonageアカウントのすべてのアプリケーションで共有されます。

各スコープを使用するタイミング

  • インスタンス状態 - 共有カウンタ、キャッシュ、コンフィギュレーション、またはすべてのレプリカが読み書きする必要があるデータに使用します。これは、ほとんどの共有状態に対する正しいデフォルトです。
  • 決定論的IDによるセッション状態 - 特定の会話、ユーザー、またはワークフローにスコープされたデータに使用します。でセッションを作成します。 vcr.createSessionWithId(id) リクエストコンテキストのIDを使って(例. conversation_uuid送信者の電話番号、ユーザーID)。
  • Accountの状態 - 共有ブロックリストやコンフィギュレーションなど、アプリケーション横断的なデータに使用します。
// 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();

参照 ステートレス・アーキテクチャ セッションのスコーピングとよくある落とし穴に関する詳しいガイダンスについては、こちらをご覧ください。

キー・バリュー演算

方法 署名 リターン 説明
set set<T>(key, value) Promise<string> (OK) キーの下に値を格納する
get get<T>(key) Promise<T> キーの値を取得する
delete delete(key) Promise<string> ("1"/"0") キーを削除する。削除された場合は "1"、見つからない場合は "0 "を返す。
increment increment(key, value) Promise<string> 数字キーを value
decrement decrement(key, value) Promise<string> 数値キーをアトミックにデクリメントする。 value
expire expire(key, seconds, option?) Promise<string> ("1"/"0") キーのTTLを秒単位で設定する

期限切れオプション

オプション option パラメータ expire を受け付ける。 EXPIRE_OPTION 列挙値:

オプション 説明
NX キーに有効期限がない場合のみ、有効期限を設定する。
XX キーにすでに有効期限がある場合のみ、有効期限を設定する。
GT 新しい有効期限が現在の有効期限より大きい場合のみ、有効期限を設定する。
LT 新しい有効期限が現在の有効期限より短い場合のみ、有効期限を設定する。
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"

ハッシュマップ操作

名前付きハッシュテーブル内のフィールドを操作する。

方法 署名 リターン 説明
mapSet mapSet(table, keyValuePairs) Promise<string> ハッシュテーブルに1つ以上のキーと値のペアを設定する。
mapGetValue mapGetValue(table, key) Promise<string> 単一のフィールド値を取得する
mapGetMultiple mapGetMultiple(table, keys) Promise<string[]> 複数のフィールド値をキーで取得する
mapGetAll mapGetAll(table) Promise<Record<string, string>> すべてのフィールドと値を取得する
mapGetValues mapGetValues(table) Promise<string[]> すべての値を取得する(キーなし)
mapDelete mapDelete(table, keys) Promise<string> 1つ以上のフィールドを削除する
mapExists mapExists(table, key) Promise<string> ("1"/"0") フィールドが存在するかチェックする
mapIncrement mapIncrement(table, key, value) Promise<string> アトミックにフィールド値をインクリメントする
mapLength mapLength(table) Promise<string> テーブルのフィールド数を取得する
mapScan mapScan(table, cursor, pattern?, count?) Promise<[string, string[]]> カーソルでフィールドを反復する
// 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']);

ハッシュテーブルのスキャン

mapScan カーソルを使ってフィールドを反復処理する。で開始する。 "0" になるまで続ける。 "0" 再び

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

リスト操作

Redisリストでバックアップされた順序付きリストストレージ。

方法 署名 リターン 説明
listAppend listAppend<T>(list, value) Promise<string> リストの最後に値を追加する
listPrepend listPrepend<T>(list, value) Promise<string> リストの先頭に値を追加する
listEndPop listEndPop<T>(list, count?) Promise<T[]> 末尾の値を削除して返す(デフォルト:1)
listStartPop listStartPop<T>(list, count?) Promise<T[]> 値を最初から削除して返す(デフォルト:1)
listRemove listRemove<T>(list, value, count?) Promise<string> 値の出現回数を削除する。正 count0:すべて
listTrim listTrim(list, startPos, endPos) Promise<string> (OK) リストを指定された範囲にトリムする
listInsert listInsert<T>(list, before, pivot, value) Promise<string> の前に値を挿入する。true)または(falseピボット値
listIndex listIndex<T>(list, position) Promise<T> ある位置の値を取得する
listSet listSet<T>(list, position, value) Promise<string> (OK) ある位置に値を設定する
listLength listLength(list) Promise<string> リストの要素数を取得する
listRange listRange<T>(list, startPos?, endPos?) Promise<T[]> 値の範囲を取得 (デフォルト: リスト全体)
// 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);

全文検索

Stateに格納されたハッシュマップデータに対して検索可能なインデックスを作成する。

方法 署名 リターン 説明
createIndex createIndex(name, options) Promise<string> 検索インデックスの作成
search search(index, query, options?) Promise<string> インデックスを検索する
dropIndex dropIndex(index, deleteDocs?) Promise<boolean> インデックスを削除する。パス true インデックスされた文書も削除する

インデックスの作成

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

検索

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

インデックスの削除

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