次の方法で共有


ポリシー フラグメントの中央メタデータ キャッシュ

適用対象: すべての API Management レベル

複数のポリシー フラグメントが共通の構成データなどの共有メタデータにアクセスする必要がある場合は、クロスリクエスト キャッシュ アプローチを使用してパフォーマンスを最適化します。 各フラグメントでメタデータを繰り返し解析するのではなく、1 回だけ解析するキャッシュを使用するアプローチでは、データの一貫性を確保しながらパフォーマンスが大幅に向上します。 この方法では、キャッシュが空の場合、最初の要求でメタデータが 1 回解析され 、キャッシュの有効期限が切れるか、キャッシュのバージョンが変更されるまで、後続のすべての要求のキャッシュ から取得 されます。

この方法には、共有メタデータを格納するためのフラグメントと、メタデータの解析とキャッシュ用の 2 つのフラグメントが必要です。

1. メタデータ フラグメント

メタデータ フラグメントは、パイプライン内の他のフラグメントによってアクセスされる共有メタデータの信頼の単一ソースとして機能します。

  • 一元化された JSON ストレージ: すべてのメタデータを JSON として格納します。
  • キャッシュ設定: バージョン管理と期間 (Time to Live、または TTL) を含むキャッシュ設定が含まれます。

2. フラグメントの解析とキャッシュ

解析とキャッシュのフラグメントは、次の動作を実装します。

  • 単一解析操作: キャッシュが空の場合、 JObject.Parse() を使用して、各パイプライン要求の開始時にメタデータ フラグメントに格納されている JSON を 1 回解析します。
  • クロスリクエスト キャッシュ: 解析されたメタデータ セクションを、複数の要求にわたって組み込みの JObject ポリシーを使用して、として格納および取得します。
  • キャッシュ優先アクセス: 後続の要求は、解析された JObject をキャッシュから直接取得し、再解析することなくすべてのフラグメントにすぐにアクセスできます。
  • キャッシュの無効化: メタデータ バージョンが変更されたとき、またはキャッシュ期間 (TTL) の有効期限が切れると、キャッシュが更新されます。

実装の詳細

このパターンを実装するには、受信フェーズの開始時に、両方のフラグメントを製品または API ポリシー定義に挿入します。 メタデータ フラグメントは最初に挿入し、その後に解析とキャッシュのフラグメントを挿入する必要があります。 例えば次が挙げられます。

<policies>
    <inbound>
        <base />
        <include-fragment fragment-id="metadata-fragment" />
        <include-fragment fragment-id="parse-cache-fragment" />
    </inbound>
</policies>

メタデータ フラグメントの例

metadata-fragment.xml フラグメントは、共有 JSON メタデータを という名前のmetadata-configに格納します。

<!-- Single source of truth for all shared metadata -->
<fragment fragment-id="metadata-fragment">
  <set-variable name="metadata-config" value="@{return @"{
    'cache-settings': {
      'config-version': '1.0',
      'ttl-seconds': 3600,
      'feature-flags': {
        'enable-cross-request-cache': true,
        'cache-bypass-header': 'X-Config-Cache-Bypass'
      }
    },
    'logging': {
      'level': 'INFO',
      'enabled': true
    },
    'rate-limits': {
      'premium': { 'requests-per-minute': 1000 },
      'standard': { 'requests-per-minute': 100 },
      'basic': { 'requests-per-minute': 20 }
    }
  }";}" />
</fragment>

フラグメントの解析とキャッシュの例

parse-cache-fragment.xml フラグメントは、metadata-config コンテキスト変数に格納されている JSON を 1 回解析し、結果のJObjectにアクセスできるようにします。 metadata-config変数は、metadata-fragment.xmlによって既に設定されている必要があります。

<fragment fragment-id="parse-cache-fragment">
  <!-- Extract cache settings from metadata-config to determine cache version and TTL -->
  <set-variable name="cache-config-temp" value="@{
    try {
      var configStr = context.Variables.GetValueOrDefault<string>("metadata-config", "{}");
      if (string.IsNullOrEmpty(configStr) || configStr == "{}") {
        return "{\"version\":\"1.0\",\"enabled\":true,\"ttl\":3600}";
      }
      
      var tempConfig = JObject.Parse(configStr);
      var cacheSettings = tempConfig["cache-settings"] as JObject;
      
      var result = new JObject();
      result["version"] = cacheSettings?["config-version"]?.ToString() ?? "1.0";
      result["enabled"] = cacheSettings?["feature-flags"]?["enable-cross-request-cache"]?.Value<bool>() ?? true;
      result["ttl"] = cacheSettings?["ttl-seconds"]?.Value<int>() ?? 3600;
      
      return result.ToString(Newtonsoft.Json.Formatting.None);
    } catch {
      return "{\"version\":\"1.0\",\"enabled\":true,\"ttl\":3600}";
    }
  }" />
  
  <!-- Parse cache configuration -->
  <set-variable name="cache-settings-parsed" value="@{
    return JObject.Parse(context.Variables.GetValueOrDefault<string>("cache-config-temp", "{}"));
  }" />
  
  <!-- Build cache key with version from cache settings -->
  <set-variable name="cache-key" value="@{
    var settings = context.Variables.GetValueOrDefault<JObject>("cache-settings-parsed");
    var version = settings?["version"]?.ToString() ?? "1.0";
    return "metadata-config-parsed-v" + version;
  }" />
  
  <!-- Try to get from APIM cache -->
  <cache-lookup-value key="@(context.Variables.GetValueOrDefault<string>("cache-key"))" variable-name="cached-config" />
  
  <choose>
    <when condition="@(context.Variables.ContainsKey("cached-config"))">
      <!-- Cache found - Use cached configuration -->
      <set-variable name="config-cache-result" value="@(true)" />
      
      <!-- Restore cached config-parsed -->
      <set-variable name="config-parsed" value="@(context.Variables.GetValueOrDefault<JObject>("cached-config"))" />
      
      <!-- Extract sections from cached metadata JObject -->
      <set-variable name="config-logging" value="@{
        var config = context.Variables.GetValueOrDefault<JObject>("config-parsed");
        return config["logging"] as JObject ?? new JObject();
      }" />
      
      <set-variable name="config-rate-limits" value="@{
        var config = context.Variables.GetValueOrDefault<JObject>("config-parsed");
        return config["rate-limits"] as JObject ?? new JObject();
      }" />
    </when>
    <otherwise>
      <!-- Cache miss - Parse and store in cache -->
      <set-variable name="config-cache-result" value="@(false)" />
      
      <!-- Parse metadata-config JSON -->
      <set-variable name="config-parsed" value="@{
        var configStr = context.Variables.GetValueOrDefault<string>("metadata-config", "{}");
        return JObject.Parse(configStr);
      }" />
      
      <!-- Extract commonly used sections for direct access -->
      <set-variable name="config-logging" value="@{
        var config = context.Variables.GetValueOrDefault<JObject>("config-parsed");
        return config["logging"] as JObject ?? new JObject();
      }" />
      
      <set-variable name="config-rate-limits" value="@{
        var config = context.Variables.GetValueOrDefault<JObject>("config-parsed");
        return config["rate-limits"] as JObject ?? new JObject();
      }" />
      
      <!-- Store parsed metadata JObject in cache -->
      <cache-store-value key="@(context.Variables.GetValueOrDefault<string>("cache-key"))" 
                         value="@(context.Variables.GetValueOrDefault<JObject>("config-parsed"))" 
                         duration="@(context.Variables.GetValueOrDefault<JObject>("cache-settings-parsed")?["ttl"]?.Value<int>() ?? 3600)" />
    </otherwise>
  </choose>
</fragment>

他のフラグメントでのメタデータの使用

他のフラグメントは、解析されたメタデータ セクションに直接アクセスできるようになりました。 例えば次が挙げられます。

<fragment fragment-id="request-logging-fragment">
  <!-- Access logging metadata JObject without reparsing -->
  <set-variable name="config-logging" value="@{
    return context.Variables.GetValueOrDefault<JObject>("config-logging", new JObject()); 
  }" />
</fragment>

キャッシュの設定と無効化

parse-cache-fragment.xml フラグメントは、キャッシュの動作と無効化を決定するために、metadata-fragment.xml フラグメントに格納されているキャッシュ設定を使用します。 たとえば、次のように設定を変更できます。

<!-- Example: Updated cache settings in the metadata fragment -->
'cache-settings': {
  'config-version': '1.0.1',     <!-- Change version to invalidate cache -->
  'ttl-seconds': 7200,           <!-- Increase TTL to 2 hours -->
  'feature-flags': {
    'enable-cross-request-cache': true,
    'cache-bypass-header': 'X-Config-Cache-Bypass'
  }
}

キャッシュの無効化のしくみ:parse-cache-fragment.xml フラグメントは、config-version値 (たとえば、metadata-config-v1.0.1) を使用してキャッシュ キーを構築します。 バージョンが 1.0.2 に変更されると、新しいキャッシュ キーが作成されます (metadata-config-v1.0.2)。 新しいキーのキャッシュされたデータは存在しないため、フラグメントは新しいメタデータ JSON を解析します。

キャッシュの更新を強制するには:config-version フラグメント内のmetadata-fragment.xmlを更新します。 キャッシュの設定は、キャッシュ参照が行われる前にすべての要求で解析されるため、キャッシュ構成の変更は直ちに有効になります。

テストとデバッグ

キャッシュ結果追跡

parse-cache-fragment.xml フラグメントは、config-cache-result変数を設定します。 この変数は、デバッグ用のログ記録と応答ヘッダーに役立ちます。

<!-- Add cache status to response headers for debugging -->
<set-header name="X-Config-Cache-Result" exists-action="override">
  <value>@(context.Variables.GetValueOrDefault<bool>("config-cache-result", false).ToString())</value>
</set-header>

キャッシュ バイパス

キャッシュを無効にするには、キャッシュ バイパス ヘッダーを使用します。

curl -H "X-Config-Cache-Bypass: true" https://your-gateway.com/api

parse-cache-fragment.xml フラグメントは、キャッシュ設定の解析後にバイパス ヘッダーをチェックします。

<!-- Check if cache bypass is requested -->
<set-variable name="cache-bypass-requested" value="@{
  var settings = context.Variables.GetValueOrDefault<JObject>("cache-settings-parsed");
  var bypassHeader = settings?["bypass-header"]?.ToString() ?? "X-Config-Cache-Bypass";
  return context.Request.Headers.GetValueOrDefault(bypassHeader, "").ToLower() == "true";
}" />

その後、キャッシュ決定ロジックでバイパス チェックが使用されます。

<when condition="@{
  var settings = context.Variables.GetValueOrDefault<JObject>("cache-settings-parsed");
  var enabled = settings?["enabled"]?.Value<bool>() ?? false;
  var bypass = context.Variables.GetValueOrDefault<bool>("cache-bypass-requested", false);
  return enabled && !bypass;
}">
  <!-- Cross-request caching is enabled and not bypassed -->
</when>

ベスト プラクティス

エラー ログと既定値を使用して JSON 解析エラーを処理する

フラグメントエラーを防ぎ、フォールバック動作を提供するために、JSON 解析操作のエラー処理を実装します。 JObject.Parse() の操作を try-catch ブロックで意味のある既定値とともにラップします。

<set-variable name="config-parsed" value="@{
  try {
    var configJson = context.Variables.GetValueOrDefault<string>("metadata-config", "{}");
    return JObject.Parse(configJson);
  } catch (Exception ex) {
    // Return default configuration on parse failure
    return JObject.Parse(@"{
      'logging': { 'level': 'ERROR', 'enabled': false },
      'rate-limits': { 'default': { 'requests-per-minute': 10 } }
    }");
  }
}" />

<!-- Log parse error using trace policy -->
<choose>
  <when condition="@(context.Variables.ContainsKey("parse-error"))">
    <trace source="config-parse" severity="error">
      <message>@("JSON parse failed: " + context.Variables.GetValueOrDefault<string>("parse-error"))</message>
    </trace>
  </when>
</choose>