進行状況の追跡を始めよう
Trailhead のホーム
Trailhead のホーム

サンプルアプリケーションのウォークスルーとキャッシュ診断

学習の目的

この単元を完了すると、次のことができるようになります。

  • キャッシュデータの保存と更新のパターンを説明する。
  • キャッシュ値に使用するデータ構造を決定する。
  • キャッシュの使用状況を診断する。

サンプルアプリケーションのウォークスルー

組織キャッシュを使用して通貨換算レートを保存および取得する方法を実証するサンプルアプリケーションを見ていきましょう。換算レートは日中変動するため、このサンプルアプリケーションではリアルタイムのレートは返しません。換算レートの日次スナップショットのみを提供します。ここでは正確なリアルタイムの値ではなく日次の値にのみ関心があるので、通貨換算レートはキャッシュ対象としてよい選択です。換算レートを繰り返し取得する場合、キャッシュから取得することで時間を大幅に節約し、アプリケーションのパフォーマンスを高めることができます。

サンプルアプリケーションの概要

換算レートサンプルアプリケーションのベースは、Visualforce ページと、換算レートを取得するロジックが含まれる Apex コントローラです。最初にこのページがアクセスされたとき、外部 Web サービスへの API コールによってレートが取得されます。このページの次回以降の実行では、キャッシュのレートが 1 日以上前のものでなければキャッシュからレートが返されます。換算レートごとに、このページには基本通貨、対象通貨、換算レートが表示されます。例示が目的であるため、返されるのは小規模な通貨のセットです。

Visualforce ページに通貨換算レートが表示される

次の例は、Visualforce ページのマークアップです。このページは、ExchangeRates Apex コントローラに関連付けられています。

<apex:page controller="ExchangeRates" action="{!init}">
    
   <apex:pageBlock title="Rates">
      <apex:pageBlockTable value="{!Rates}" var="rate">
         <apex:column value="{!rate.Base_Currency__c}"/>
         <apex:column value="{!rate.To_Currency__c}"/>
         <apex:column value="{!rate.Rate__c }"/>
      </apex:pageBlockTable>
   </apex:pageBlock>
   
</apex:page>

サンプル Apex コントローラ

このサンプル Apex コントローラでは多くの処理を行います。換算レートを取得し、Salesforce とキャッシュに保存し、キャッシュからレートを取得します。以下は、サンプルコントローラが実行する操作の内訳と、そのソースコードです。

初回のサンプル実行では、次の操作が実行されます。

  • 外部エンドポイントへの API コールから換算レートが取得されます。
  • API コールから返された結果 (JSON 形式) が解析され、Salesforce の Exchange_Rate__c sObject に保存されます。
  • getCachedRates() メソッドが Exchange_Rate__c sObject の配列を組織キャッシュに保存します。

2 回目以降のサンプル実行では、次の操作が実行されます。

  • サンプルが保存されたデータの鮮度をチェックします。このために、SOQL クエリを実行して初回で返された Exchange_Rate__c レコードの createdDate 値を取得します。
  • 日付が 1 日以上前の場合、初回の実行と同様に API コールで換算レートが 取得されます。
  • 日付が 1 日以内の場合、レートは組織キャッシュから取得されます。キャッシュの欠落がある場合、レートは Exchange_Rate__c sObject から照会されて組織キャッシュに保存されます。
メモ

メモ

Apex コントローラは、ここには表示されていない RateLib というヘルパー Apex クラスを使用します。このヘルパークラスには、換算レートサービスへの外部 API コールを実行し、API コールからの JSON 形式の結果を解析し、Exchange_Rate__c レコードを保存するメソッドが含まれます。

public class ExchangeRates {
    private String currencies = 'EUR,GBP,CAD,PLN,INR,AUD,SGD,CHF,MYR,JPY,CNY';
    public String getCurrencies() { return currencies;}
    public Exchange_Rate__c[] rates {get; set;}

    //                                                                          
    // Checks if the data is old and gets new data from an external web service 
    // through a callout. Calls getCachedRates() to manage the cache.           
    // 
    public void init() {
        // Let's query the latest data from Salesforce
        Exchange_Rate__c[] latestRecords = ([SELECT CreatedDate FROM Exchange_Rate__c 
                        WHERE Base_Currency__c =:RateLib.baseCurrencies 
                              AND forList__c = true 
                        ORDER BY CreatedDate DESC
                        LIMIT 1]);
        
        // If what we have in Salesforce is old, get fresh data from the API
        if ( latestRecords == null  
            || latestRecords.size() == 0 
            || latestRecords[0].CreatedDate.date() < Datetime.now().date()) {
            // Do API request and parse value out
            String tempString = RateLib.getLoadRate(currencies);
            Map<String, String> apiStrings = RateLib.getParseValues(
                tempString, currencies);
            
            // Let's store the data in Salesforce
            RateLib.saveRates(apiStrings);

            // Remove the cache key so it gets refreshed in getCachedRates()
            Cache.Org.remove('Rates');
        }
        // Call method to manage the cache
        rates = getCachedRates();
    }

    //                                                                          
    // Main method for managing the org cache.                                  
    // - Returns exchange rates (Rates key) from the org cache.                 
    // - Checks for a cache miss.                                               
    // - If there is a cache miss, returns exchange rates from Salesforce       
    //    through a SOQL query, and updates the cached value.                   
    //
    public Exchange_Rate__c[] getCachedRates() {
        // Get the cached value for key named Rates
        Exchange_Rate__c[] rates = (Exchange_Rate__c[])Cache.Org.get(
            RateLib.cacheName+'Rates');
        
        // Is it a cache miss? 
        if(rates == null) {
            // There was a cache miss so get the data via SOQL
            rates = [SELECT Id, Base_Currency__c, To_Currency__c, Rate__c 
                        FROM Exchange_Rate__c 
                        WHERE Base_Currency__c =:RateLib.baseCurrencies 
                              AND forList__c = true
                              AND CreatedDate = TODAY];
            // Reload the cache
            Cache.Org.put(RateLib.cacheName+'Rates', rates);
        }
        return rates;
    }
}

Exchange Rates (換算レート) サンプルのソースをダウンロードして、Developer 組織で試す場合は、「リソース」セクションを参照してください。

キャッシュ管理のベストプラクティス

キャッシュ保存のパターン

ExchangeRates Apex クラスには、キャッシュの初期化と更新のロジックをカプセル化するメソッドが含まれています。データが古いか、見つからない場合、init() メソッドが API コールで新しい換算レートを取得し、Salesforce に保存します。getCachedRates() メソッドがキャッシュを内部的に管理します。キャッシュ値が見つからない場合、このメソッドは Salesforce からレートの配列を取得し、キャッシュに保存します。

サンプルアプリケーションでは外部データを使用するため、API コールを介して Web サービスからデータを取得します。また、キャッシュを更新するためのバックアップとしてデータを Salesforce レコードに保存します。外部データを使用しないアプリケーションは、SOQL を使用して Salesforce レコードを取得し、キャッシュします。この場合の方がキャッシュ管理のプロセスが簡単で、キャッシュメソッドの実装も簡潔になります。たとえば、アプリケーションが SOQL からのローカルデータだけを使用する場合、init() メソッドは不要で、getCachedRates() メソッドのみが必要です。

キャッシュを管理するロジックのすべてを 1 つのメソッドに含めることをお勧めします。これにより、キャッシュはアプリケーションの 1 か所でのみ操作されます。キャッシュを中央管理することで、無効なキャッシュ (キャッシュの欠落) へのアクセスや意図しないキャッシュ値の上書きからエラーが発生する可能性を下げることができます。

キャッシュ対象の決定

このサンプルは、sObject の配列をキャッシュに保存します。このアプローチで、保存するデータ構造として最適な選択肢はどれでしょうか? どの選択肢にもトレードオフがあります。sObject 全体ではなく、項目値など小さな単位のデータを保存すると、キャッシュ使用サイズを小さくすることができます。一方で、各キーで保存するデータが少なくなると、データと sObject を再構築するために複雑なロジックが必要になり、処理時間が長くなる可能性があります。また、1 つのキーに sObject の配列を保存した場合、使用されるキャッシュ空間は、個々のキーに保存された個々の sObjects の合計サイズよりも小さくなります。項目のリストではなく、より小さい項目をキャッシュすると、逐次化とキャッシュのコミット時間のオーバーヘッドにより、パフォーマンスが低下します。たとえば、レートのリスト (次のスニペットではレート変数で参照) を保存する代わりに、

Cache.Org.put('Rates', rates);

個々のレートをそれぞれが独自のキーを持つ項目として次のように保存できます。

Cache.Org.put('DollarToEuroRate', rateEUR);
Cache.Org.put('DollarToChineseYuan', rateCNY);
Cache.Org.put('DollarToJapaneseYen', rateJPY);
// etc.

キャッシュするデータ構造の決定は、アプリケーションがどのようにデータを処理するかに依存します。たとえば、アプリケーションが同じ基本通貨から通貨を換算し、少なくとも対象通貨ごとに換算レートを保存するとします。その場合、各通貨レートを個別のキーとして保存できます。ただし、Visualforce ページにレートを表示するには、データを sObject のリストとしてキャッシュしたほうが簡単です。sObject には作成日などのシステム項目が含まれているため、保存領域は増加する可能性がありますが、ロジック処理時間が短くなり、アプリケーションとキャッシュのパフォーマンスが向上します。キャッシュを使用するとき、最大の目標はアプリケーション実行時間の短縮であることを念頭に置いてください。

キャッシュ使用状況の診断

プラットフォームキャッシュを実装するための作業はすべて完了しましたが、キャッシュを最適に使用しているかどうかはどう調べればよいのでしょうか? パフォーマンスデータを確認する方法はいくつかあります。その 1 つは、[設定] で診断情報を参照する方法です (Salesforce Classic でのみ使用可能)。

診断ページにアクセスする前に、ユーザのキャッシュ診断権限を有効にします。

  1. [設定] から、[クイック検索] ボックスに「ユーザ」と入力し、[ユーザ] を選択します。
  2. ユーザの名前をクリックし、[編集] をクリックします。
  3. [キャッシュ診断] を選択し、[保存] をクリックします。

次に、パーティションの特定のキャッシュ種別の診断ページにアクセスします。

  1. [設定] から、[クイック検索] ボックスに「キャッシュ」と入力し、[プラットフォームキャッシュ] を選択します。
  2. 診断情報を確認するパーティションをクリックします。
  3. セッションキャッシュまたは組織キャッシュの下で、[診断] をクリックします。

    パーティションページには、キャッシュ種別ごとに診断ページへのリンクが含まれます。

組織キャッシュの [診断] をクリックすると、組織キャッシュの診断ページが新しいタブで開きます。このページには 2 つのグラフが表示されます。最初のグラフ (Org Cache Capacity and Usage (組織キャッシュ容量と使用率)) にはキャッシュの使用制限が表示されます。このケースでは、制限よりもはるかに低い 0.02% が使用されています。2 つ目のグラフ (By Content Contribution (コンテンツ配分別)) は、キー別のコンテンツ配分を示すドーナツグラフです。

メモ

メモ

サンプルでは組織キャッシュを使用しているため、組織キャッシュの診断ページのみを調べています。セッションキャッシュを使用するアプリケーションのセッションキャッシュにも類似の診断ページが提供されています。

次の画像は、4 つのキャッシュキーを含むコンテンツ配分グラフを示します。キャッシュキーの 1 つである Rates は、換算レートサンプルで使用されています。Rates キーが半分以上の空間を使用しています。

組織キャッシュの診断ページ

グラフの後にはキャッシュキーの詳細リストが表示されます。このリストには、キーに対応する各キャッシュ値のサイズと、キャッシュがアクセスされた回数が表示されます。Apex で remove() メソッドをコールする代わりに、このリストから手動でキャッシュを削除できます。[削除] ボタンを使用すると、コードを変更せずにシステム管理者がキャッシュを管理できます。

DollarToEuroRate キーは Rates キーよりもはるかに少ない空間を使用しています。DollarToEuroRate が保存するのは 1 つの値のみで、Rates が保存するのは sObject の配列であるため、これは予測どおりです。

診断ページの情報を使用して、キャッシュの使用を調整するかどうかを判断します。たとえば、キャッシュ値のアクセス件数が少ない場合、つまりほとんど使用されていない場合は、それが本当に必要かを検討します (特に大きい場合)。キャッシュ使用制限に近づいている場合は、キャッシュする対象を再検討します。不必要なキャッシュの削除や、キャッシュ容量の追加購入が必要になるかもしれません。

おめでとうございます。プラットフォームキャッシュを使用してアプリケーションのデータをキャッシュし、取得を高速化する方法を学習しました。キャッシュのベストプラクティスとキャッシュ使用状況の診断方法も学習しました。これでいつでもシマリスの冒険に参加して、貴重なリソースをキャッシュできるようになりました。