mcpField
V
vtex-io-application-performance

vtex-io-application-performance

github.com/vtex開発471.2k 回インストール2 週間前に更新
リポジトリ

vtex-io-application-performance について

Node や .NET の VTEX IO バックエンド開発者向けに、性能と耐障害性を扱うスキルです。キャッシュ、呼び出しの重複排除、並列 I/O、タイムアウト、テナント分離を案内します。

主な機能

  1. 01プロセス内・共有キャッシュの設計
  2. 02古いデータの返却とバックグラウンド更新
  3. 03独立した上流呼び出しの並列化
  4. 04タイムアウト設定と段階的縮退
  5. 05アカウントとワークスペース単位のキー分離
  6. 06VBase の状態と書き込みの管理

活用シーン

  1. 01上流への重複リクエストの削減
  2. 02リゾルバーの直列呼び出し連鎖の短縮
  3. 03変更頻度の低いアプリ設定のキャッシュ
  4. 04プロセスキャッシュでのテナント間混在の防止

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/vtex/skills --skill vtex-io-application-performance

インストール後、Claude Code で/vtex-io-application-performanceを入力して読み込まれたか確認します。

詳細

VTEX IO アプリケーションのパフォーマンス

このスキルを使用する場面

VTEX IO のバックエンド(通常は @vtex/api/Koa スタイルのミドルウェアを使用する Node、または .NET サービス)を、パフォーマンスとレジリエンスのために最適化するときにこのスキルを使用します。対象となるのは、キャッシュ、処理の重複排除、並列 I/O、効率的な設定の読み込みです。「キャッシュを追加する」だけではありません。

  • ホットキー用のインメモリ LRU(Pod ごと)を追加する
  • Pod 間で共有キャッシュを実現するために VBase に保存する。必要に応じて stale-while-revalidate(古い値を返し、バックグラウンドで更新する)も使用する
  • AppSettings(または同様の設定)をすべてのリクエストで読み込むのではなく、起動時に一度、または TTL に基づいて更新する
  • 独立したクライアント呼び出しを、逐次処理ではなく並列化する(Promise.all)
  • ctx.clients(例:vbase)をクライアントヘルパーやリゾルバーに渡し、キャッシュをテスト可能かつ明示的にする

次の用途にはこのスキルを使用しないでください。

  • エッジでの /_v/private とパブリックなパスの選択、または Cache-Control → vtex-io-service-paths-and-cdn
  • GraphQL の @cacheControl におけるフィールドのセマンティクスのみ → vtex-io-graphql-api

判断ルール

  • レイヤー 1 — LRU(プロセス内) — 最速ですが、コールドスタート時に失われ、レプリカ間で共有されません。頻繁に使用するキー(組織、コストセンター、小規模な設定データ)には、上限サイズと TTL を設定して使用します。
  • レイヤー 2 — VBase — ポッド間で共有されます。他の IO リソースと同様、プラットフォームデータはアカウント/ワークスペースごとに分割されます。クライアントが同時更新の安全性をサポートしている場合は、hash または trySaveIfhashMatches と組み合わせます(クライアントを参照)。
  • Stale-while-revalidate — VBase のヒット時に鮮度の期限が切れていた場合、古いデータをすぐに返し、非同期で再検証します(オリジンから取得 → VBase と LRU に書き込み)。毎回オリジンの応答を待つ場合と比べて、テールレイテンシが低減します。
  • TTL のみ — よりシンプルな方法です。TTL の期限が切れるまでキャッシュし、その後はオリジンからの取得をブロッキングして行います。古いデータを許容できない場合や、オリジンへの負荷が小さい場合に適しています。
  • AppSettings — 値がアカウント全体に適用され、ほとんど変わらない場合は、一度だけ読み込む(または一定間隔で更新する)か、モジュールメモリに保持します。ワークスペースに依存する場合や、管理者による変更をすぐに反映する必要がある場合は、リクエストごとに読み込むか、短い TTL のキャッシュを使用します。保護策なしに、ログやグローバル状態にシークレットをキャッシュしないでください。
  • コンテキスト — リクエスト内の重複処理を防ぐ(例:「このリクエストですでに組織を読み込み済み」)には、ctx.state を使用します。グローバルなモジュールキャッシュは、不変のアプリデータ、またはTTL で更新されるアプリデータにのみ使用します。同じポッドが複数のテナントに対応する場合、アカウントとワークスペースの情報は**ctx.vtex** に含まれるため、メモリ内キャッシュのキーには必ず含めてください。
  • 並列リクエスト — リゾルバーで独立した上流呼び出しが必要な場合は並列に実行し、出力が互いに依存する場合にのみまとめます。
  • すべての外部呼び出しにタイムアウトを設定 — すべての ctx.clients 呼び出しと外部 HTTP リクエストには、明示的なタイムアウトを必ず設定します。クライアントごとの動作を調整するには、@vtex/api のクライアントオプション(timeout、retries、exponentialTimeoutCoefficient)を使用します。無制限の待機は、分散システムにおける連鎖障害の最大の原因です。
  • グレースフルデグラデーション — 上流の応答が遅い、または停止している場合は、ビジネス上許容される範囲で、応答をブロックするのではなくフェイルオープンにします(キャッシュまたはデフォルトデータを返す、任意の追加処理をスキップする)。慢性的に障害が発生する依存先には、サーキットブレーカーパターンの導入を検討してください。
  • トランザクションデータのキャッシュは慎重に — 決済レスポンスと進行中のトランザクション状態は、決してキャッシュしてはいけません。その他のトランザクションデータ(注文フォーム、カートのシミュレーション、セッション状態)も、原則としてキャッシュしません。ただし、利用側が一時的な古さを許容できる場合は、短時間のキャッシュ(TTL < 5 min)が許容されることがあります。キャッシュする前に、ユースケースを明確に確認してください。トランザクションデータに長い TTL を設定すると、価格の陳腐化、在庫の誤表示、状態の不整合につながります。
  • リゾルバーチェーンの重複排除 — リゾルバーチェーンで同じクライアントメソッドを複数回呼び出す場合(例:リゾルバーとその内部ヘルパーの両方で getCostCenter を呼び出す)、重複を排除します。呼び出しは一度だけにして結果を渡すか、ctx.state に保存します。7 回以上の呼び出しを直列に連ねるウォーターフォールは、並列 3 回+直列 1 回にできるにもかかわらず、パフォーマンスを最も大きく損なう原因です。
  • 段階的な Promise.all — 独立した呼び出しを並列フェーズにまとめます。フェーズ 1:Promise.all([getOrderForm(), getCostCenter(), getSession()])。フェーズ 2(フェーズ 1 に依存):getSkuMetadata()。フェーズ 3(フェーズ 2 に依存):generatePrice()。相互に依存する呼び出しが 2 つだけなのに、6 回の呼び出しを順番に await しないでください。
  • ミューテーションの一括処理 — 複数の値を設定する場合(例:カートの商品ごとに setManualPrice を呼び出す場合)は、逐次ループではなく Promise.all を使用します。ループ内の await は、それぞれ完全な往復待ち時間を追加します。
VBase の高度なパターン
  • 複合キーではなく、エンティティごとのキーを使う — 複合データ(例:allSkus:{sortedCartSkuIds})ではなく、個々のエンティティ(例:sku:{region}:{skuId})をキャッシュします。エンティティごとにキーを設定すると、商品が追加・削除されたときのキャッシュヒット率が大幅に向上します。
  • 最小限の DTO — 完全な API レスポンス(商品あたり約 10~50 KB)ではなく、コンシューマーが必要とするフィールドのみを保存します(例:約 50 バイトの { skuId, mappedId, isSpecialItem })。これにより、VBase のストレージ、シリアライズ時間、転送サイズを削減できます。
  • 関連 SKU の事前キャッシュ — 検索 API が 4 つの SKU バリエーションを持つ商品を返した場合、要求された SKU が 1 つだけでも、4 つすべての SKU を個別にキャッシュします。関連 SKU に対する次のリクエストは、API 呼び出しではなく VBase のキャッシュヒットになります。
  • vbase をパラメーターとして渡す — クライアントから他のクライアントに直接アクセスすることはできません。VBase が必要なクライアントメソッドやユーティリティには、パラメーターとして ctx.clients.vbase を渡します。これにより、コードのテストがしやすくなり、依存関係も明示されます。
  • VBase の状態マシン — 長時間実行される操作(スキャン、インポート、バッチ処理)では、VBase を状態ストアとして使用し、current-operation.json(ロックと進捗状況)、ハートビートの延長、チェックポイントと再開、TTL に基づくロックの有効期限切れを利用して、ゾンビロックを防ぎます。
service.json のチューニング
  • timeout — プラットフォームがリクエストを終了するまでの最大秒数です。最も時間がかかると想定される操作に基づいて設定します。リゾルバーが応答の遅い上流サービスを呼び出す場合は、デフォルトのままにしないでください。
  • memory — ワーカーあたりのメモリ(MB)です。LRU キャッシュや大きなペイロードが原因で OOM が発生する場合は増やします。過剰に割り当てる前に、実際の使用量を監視してください。
  • workers — レプリカあたりの同時リクエストハンドラー数です。ワーカーを増やすと、より多くのリクエストを同時に処理できますが、それぞれがメモリ予算とプロセス内 LRU を共有します。
  • minReplicas / maxReplicas — 負荷に応じた水平スケーリングを制御します。注意:アプリに ttl の期間(最大 60 分)リクエストがない場合、すべてのポッドがゼロまでスケールダウンされます。minReplicas が制御するのはトラフィックがある間の下限のみであり、ポッドを無期限に稼働させるものではありません。コールドスタートを考慮して設計してください。アイドル状態の後の最初のリクエストでは新しいポッドが起動するため、LRU のウォームアップコストを低く保ち、アイドル期間をまたいでインメモリ状態が保持されることを前提にしないでください。
テナントとインメモリキャッシュ

ポッドはアカウント間で共有される場合がありますが、プラットフォームによってすべてのリクエストが常に特定の {account, workspace} にスコープされます。ctx.vtex.account と ctx.vtex.workspace はランタイムによって設定され、開発者が設定するものではありません。すべてのプラットフォーム API(VBase、アプリバケット、Master Data など)は、アカウント/ワークスペースごとにデータを自動的に分離するため、そのレスポンスは設計上テナント間で安全です。リスクがあるのは、LRU キャッシュ、モジュールレベルの Map、グローバル変数など、プロセス内の状態だけです。これらはユーザーが管理するもので、プラットフォームによって分離されないため、ctx.vtex.account と ctx.vtex.workspace(およびエンティティ ID)を明示的にキーに含める必要があります。これにより、同じポッドで連続して処理される異なるアカウントからの2 つのリクエストが、互いのエントリーを読み取れないようにします。

厳守事項

制約:テナントキーなしで、機密データやテナント固有のデータをモジュールレベルのキャッシュに保存しないでください

グローバルまたはモジュールレベルのマップに、PII、トークン、または認可に関わるデータを、account と workspace(および分離に必要なその他のディメンション)なしで、ユーザー IDまたはメールアドレスだけをキーにして保存してはいけません。

重要な理由 — Pod はマルチテナントです。同じプロセスが複数のアカウントを順に処理する場合があります。VBaseなどの API は現在のアカウント/ワークスペースのスコープで動作しますが、インメモリの Map は皆さんの責任です。キーに**account/workspaceがないと、ウォームキャッシュからテナントをまたいで**読み取るリスクがあります。

検出方法 — userId または email だけをキーとするモジュールスコープの Map、または値がテナント固有であるにもかかわらず、キャッシュキーに ctx.vtex.account/ctx.vtex.workspace が含まれていないケースです。

正しい方法 — ctx.vtex.account、ctx.vtex.workspace、エンティティ ID からキーを作成してください。アプリトークンをプレーンなキャッシュ値として VBase/LRU に保存してはいけません。ctx.clientsとプラットフォームの認証を優先してください。

// Pseudocode: in-memory key must mirror tenant scope (same pod, many accounts)
function cacheKey(ctx: Context, subjectId: string) {
  return `${ctx.vtex.account}:${ctx.vtex.workspace}:${subjectId}`;
}

誤った方法 — globalUserCache.set(email, profile) のように、メールアドレスだけをキーにし、account/workspaceのセグメントを含めない方法です。後でアカウントスコープの VBase 読み取りを行う場合でも、こちらのマップはプラットフォームによって分割されていないため、共有 Pod では安全ではありません。

制約:金融または冪等性が重要な処理で、VBase への書き込みを fire-and-forget にしないでください

VBase を冪等性ストア(例:トランザクション状態を保存する決済コネクター)またはデータ整合性ストアとして使用する場合、書き込みは必ず await してください。fire-and-forget の書き込みは、障害が検知されないリスクがあります。たとえば、上流処理(例:課金)が成功したにもかかわらず VBase のレコードが失われると、次の再試行で重複が発生します。

決済コネクターでは、最初の防御策として、アクワイアラー/ゲートウェイの冪等性機能を使用します。**VTEX paymentId**を冪等性キーとして外部決済プロバイダーに渡してください(例:Idempotency-Key HTTP ヘッダーを使用)。これにより、VBase の状態が失われても、プロバイダー自身が重複課金を拒否します。VBase はローカルの状態追跡や、VTEX Gateway の再試行に対して正しいレスポンスを返すために引き続き不可欠ですが、外部冪等性キーによって、プロバイダーレベルで最悪の事態(2 重課金)を防げます。

重要な理由 — VTEX Gateway は同じ paymentId で決済呼び出しを再試行します。認可の成功後に VBase の書き込みがサイレントに失敗し、アクワイアラーに冪等性キーが設定されていないと、コネクターは別の決済リクエストを送信し、重複課金が発生します。アクワイアラーの冪等性キーが設定されていれば、プロバイダーは重複を拒否します。設定されていない場合、VBase だけが防止策となります。

検出方法 — 決済、精算、返金、または保存値が再実行を防ぐ唯一の記録となる処理で、VBase の saveJSON または saveOrUpdate 呼び出しに await がないケースです。また、外部決済呼び出しに冪等性キーのヘッダーが含まれているかも確認してください。

正しい方法 — アクワイアラーの冪等性キーを使用し、VBase への書き込みも await してください。

const { paymentId } = authorization

// Pass VTEX paymentId as idempotency key to the acquirer
const response = await ctx.clients.paymentProvider.authorize(paymentData, {
  headers: { 'Idempotency-Key': paymentId },
})

// Await VBase write for local state tracking
await ctx.clients.vbase.saveJSON<Transaction>('transactions', paymentId, transactionData)
return Authorizations.approve(authorization, { ... })

誤った方法 — アクワイアラーの冪等性キーを使用せず、VBase への書き込みを fire-and-forget にする方法です。

const { paymentId } = authorization
// No idempotency key to the acquirer + no await on VBase
const response = await ctx.clients.paymentProvider.authorize(paymentData)
ctx.clients.vbase.saveJSON('transactions', paymentId, transactionData)
return Authorizations.approve(authorization, { ... })
制約:トランザクションデータやほぼリアルタイムのデータをキャッシュする場合は慎重に判断してください

決済ステータスとアクティブなトランザクションの状態は、絶対にキャッシュしてはいけません。これらは厳密にリアルタイムである必要があります。注文フォーム、カートのシミュレーション、セッション状態など、その他のトランザクションデータは、デフォルトではキャッシュしませんが、ユースケースで短時間の古さが許容される場合は、短時間のキャッシュ(例:1~5 分)を使用できます。これはケースバイケースの判断です。キャッシュする前に、選択した TTL の間、利用側が古いデータを許容できることを明示的に確認してください。

重要な理由 — キャッシュされた注文フォームを返すと、存在しない商品や古い価格が表示されることがあります。決済レスポンスをキャッシュすると、以前のトランザクションのステータスが返される可能性があります。ただし、同じデータが数秒以内に繰り返し要求される、読み取り中心のシナリオ(例:商品一覧ページの複数のコンポーネントがシミュレーションデータを取得する場合)では、短時間のキャッシュによって、ユーザーへの影響を実質的に与えることなく、上流 API の負荷を大幅に軽減できます。

検出方法 — orderForm:{id}、cartSim:{hash}、paymentResponse:{id}、session:{token} のような LRU または VBase のキーを、TTL の明示的な判断や根拠なしに、リードスルーキャッシュに使用しているケースです。トランザクションデータに対する長い TTL(5 分超)は、ほぼ常に誤りです。決済レスポンスやアクティブなトランザクションの状態にキャッシュを使用することは、すべて誤りです。

適切な方法 — 参照データ(常にキャッシュ)とトランザクションデータ(デフォルトではキャッシュせず、明確な根拠がある場合に限り短い TTL を使用)を分けます。

// Reference data: cached (changes rarely)
const costCenter = await getCostCenterCached(ctx, costCenterId);
const sellerList = await getSellerListCached(ctx);

// Transactional data: default live, but short cache is acceptable if justified
const orderForm = await ctx.clients.checkout.orderForm();
const simulation = await ctx.clients.checkout.simulation(payload);

// Acceptable: short-lived cache for a read-heavy page (e.g. simulation results
// reused across multiple components within the same page render, TTL < 60s)
// Only after confirming staleness is acceptable for this specific consumer.

不適切な方法 — トランザクションデータを長期間キャッシュすること、または決済状態をキャッシュすること。

// Never cache payment responses or transaction status
const paymentCache = lru({ max: 1000, ttl: 300_000 });

// Avoid: long TTL on order form without explicit justification
const orderFormCache = lru({ max: 500, ttl: 600_000 }); // 10 min is too long
制約:キャッシュの更新が遅い、または完了時間が予測できない場合でも、購入処理をブロックしないでください

プラットフォームの SLA で高速なレスポンスが求められる場合、stale-while-revalidate やオリジンへの呼び出しによって、チェックアウトに不可欠なミドルウェアに予測できない遅延を追加してはいけません。

重要な理由 — 任意の情報補完によってチェックアウトをブロックすると、コンバージョンと信頼性が損なわれます。

検出方法 — カートまたは決済のリゾルバーが、VBase の更新または外部 API の呼び出しを待機してからレスポンスを返す一方、タイムアウトやフォールバックがありません。

適切な方法 — 古いデータまたはデフォルト値を返し、更新をキューに登録し、ビジネスルールが許す場合はフェイルオープンにします。

不適切な方法 — ホットパスでタイムアウトなしに await fetchHeavyPartner() を実行すること。

推奨パターン

  1. データを分類します。参照データ(組織、コストセンター、設定、セラー一覧 → キャッシュ可能)、トランザクションデータ(注文フォーム、カートのシミュレーション、決済 → キャッシュしない)、ユーザー専用データ(暗号化とキー設定なしで共有キャッシュに保存しない)。
  2. 読み取り中心の参照データには、LRU のみ、VBase のみ、またはLRU → VBase → オリジン(2 層)を選択します。
  3. リクエスト内で重複を排除します。リゾルバーチェーンで同じローダーが 2 回呼び出される可能性がある場合は、ctx.state フラグを設定します。
  4. 独立した ctx.clients 呼び出しを、フェーズごとの Promise.all グループで並列化します。
  5. 多種類のデータ(SKU、ユーザー、組織レコード)には、最小限の DTO を使ったエンティティごとの VBase キーを使用します。
  6. TTL と無効化の方法(誰が書き込むか、いつ更新が実行されるか)を文書化します。
リゾルバーチェーンの最適化(変更前/変更後)
// BEFORE: 7 sequential awaits, 2 duplicate calls
const settings = await getAppSettings(ctx); // 1
const session = await getSessions(ctx); // 2
const costCenter = await getCostCenter(ctx, ccId); // 3
const orderForm = await getOrderForm(ctx); // 4
const skus = await getSkuById(ctx, skuIds); // 5
const price = await generatePrice(ctx, costCenter); // 6 (calls getCostCenter AGAIN + getSession AGAIN)
for (const item of items) {
  await setManualPrice(ctx, item); // 7, 8, 9... (sequential loop)
}

// AFTER: 3 phases, no duplicates, parallel mutations
const settings = await getAppSettings(ctx);
const [session, costCenter, orderForm] = await Promise.all([
  getSessions(ctx),
  getCostCenter(ctx, ccId),
  getOrderForm(ctx),
]);
const skus = await getSkuMetadataBatch(ctx, skuIds); // per-entity VBase cache
const price = await generatePrice(ctx, costCenter, session); // reuse, no re-fetch
await Promise.all(items.map((item) => setManualPrice(ctx, item)));
エンティティごとの VBase キャッシュ
interface SkuMetadata {
  skuId: string;
  mappedSku: string | null;
  isSpecialItem: boolean;
}

async function getSkuMetadataBatch(
  ctx: Context,
  skuIds: string[],
): Promise<Map<string, SkuMetadata>> {
  const { vbase, search } = ctx.clients;
  const results = new Map<string, SkuMetadata>();

  // Phase 1: check VBase for each SKU in parallel
  const lookups = await Promise.allSettled(
    skuIds.map((id) => vbase.getJSON<SkuMetadata>("sku-metadata", `sku:${id}`)),
  );

  const missing: string[] = [];
  lookups.forEach((result, i) => {
    if (result.status === "fulfilled" && result.value) {
      results.set(skuIds[i], result.value);
    } else {
      missing.push(skuIds[i]);
    }
  });

  if (missing.length === 0) return results;

  // Phase 2: fetch only missing SKUs from API
  const products = await search.getSkuById(missing);

  // Phase 3: cache ALL sibling SKUs (prewarm)
  for (const product of products) {
    for (const sku of product.items) {
      const metadata: SkuMetadata = {
        skuId: sku.itemId,
        mappedSku: extractMapping(sku),
        isSpecialItem: checkSpecial(sku),
      };
      results.set(sku.itemId, metadata);
      // Fire-and-forget for prewarming (not idempotency-critical)
      vbase
        .saveJSON("sku-metadata", `sku:${sku.itemId}`, metadata)
        .catch(() => {});
    }
  }

  return results;
}

よくある障害パターン

  • LRU が無制限 — max 件数を設定せずにメモリが増え続け、Pod が OOM になります。
  • LRU を使わない VBase — ホットキーに対してリクエストのたびに VBase にアクセスし、レイテンシとコストが増加します。
  • キーにテナントを含めないインメモリーキャッシュ — 同じ Pod がアカウント A の後に B に応答すると、モジュールキャッシュから古いまたは誤った行が返されます。
  • 逐次的な await — 独立した 3 つの Janus 呼び出しを 1 つずつ await すると、合計レイテンシは最大値ではなく、すべてのレイテンシの合計になります。
  • リゾルバーチェーンでの重複呼び出し — getCostCenter がリゾルバーとヘルパーの内部で呼び出され、getSession が同じフロー内で 2 回呼び出されます。重複するたびに、完全なラウンドトリップが追加されます。
  • Blob 形式の VBase キー — sortedCartSkuIds をキーにすると、10 個の商品が入ったカートに 1 個追加するたびに、1 回の検索ではなく全件を再取得する必要があります。
  • トランザクションデータに対する長寿命キャッシュ — 注文フォームやカートシミュレーションを、明確な根拠なく長い TTL でキャッシュします。決済レスポンスやトランザクション状態は、いかなる場合もキャッシュします。
  • クリティカルパスでのファイア・アンド・フォーゲット書き込み — 冪等性ストアへの VBase 書き込みを await せず、失敗が検知されないままリトライ時に重複が発生します。
  • 明示的なタイムアウトがない — 上流呼び出しでデフォルトまたは無限のタイムアウトに依存し、1 つの依存先の遅延でリクエストチェーン全体が停滞します。
  • グローバルな可変シングルトン — モジュールレベルの可変オブジェクト(トークンキャッシュのメタデータなど)が並行リクエストによって変更され、競合状態や誤動作を引き起こします。
  • AppSettings をリアルタイムデータとして扱う — TTL が切れるまで管理者による変更が反映されず、通知経路もありません。
  • ホットパスでの console.log — テンプレートリテラルでレスポンスオブジェクト全体をログ出力すると、[object Object] が出力されます。機密データをマスキングしたうえで、JSON.stringify と ctx.vtex.logger を使用してください。

レビューチェックリスト

  • インメモリーキャッシュのキーには、値がテナント固有の場合、account + workspace(およびエンティティ ID)が含まれていますか?
  • LRU の上限(最大エントリー数)が設定され、TTL が定義されていますか?
  • VBase では、stale-while-revalidate または TTL のみの動作が明示されていますか?
  • 安全に実行できる場合、独立した上流呼び出しは(Promise.all フェーズを使って)並列化されていますか?
  • リゾルバーチェーン内で、同じクライアントメソッドへの重複呼び出しがありませんか?
  • AppSettings(または同様のもの)は、適切な頻度(一度だけかリクエストごとか)で読み込まれていますか?
  • チェックアウトまたは決済のパスは、任意の更新処理によってブロックされないようになっていますか?
  • すべての外部呼び出しに、@vtex/api クライアントオプションまたは同等の方法で明示的なタイムアウトが設定されていますか?
  • 信頼性の低い上流サービスに対し、縮退経路(キャッシュ済みのフォールバック、スキップ、サーキットブレーカー)が用意されていますか?
  • 金融処理または冪等性が重要なパスでは、VBase への書き込みを await していますか?
  • 決済/トランザクションの状態は常に最新のデータを取得していますか?その他のトランザクションデータ(注文フォーム、カートシミュレーション)にキャッシュを使う場合、短い期間に限定し、明確な根拠がありますか?
  • SKU のようにカーディナリティが高いデータに対し、VBase のキーは Blob 形式ではなくエンティティごとになっていますか?
  • service.json のリソース制限(timeout、memory、workers、replicas)は、ワークロードに合わせて調整されていますか?
  • ログ出力には console.log ではなく ctx.vtex.logger を使い、機密データをマスキングしていますか?

関連するスキル

参考資料

コメント