課題と背景、いつこのパターンを使うか
Salesforceが導入された後でも、Excelファイルを使用した業務データのやり取りは業務ワークフローとして多くの現場で採用されていると思います。 例えば、標準レポートで表現できない集計表やグラフデータなどをExcelファイルで取りまとめて、参照するなど、シナリオはさまざまに存在します。
今回紹介する弊社のクラウド連携サービスSkyOnDemandを使ったデータ連携パターンは、Salesforceに格納されているデータはもちろん基幹システムや別アプリケーションのデータをEXCELファイルに書きだして使いたい。できれば、Salesforceの画面で、抽出条件を絞り込んで、その条件に合うデータだけをEXCELに書き込んでダウンロードして参照したい場合の実装パターンです。
ファイルダウンロードといえば、一般的には、リクエストされた条件を元に抽出されたデータをダウンロードするという一連につながった同期処理が一般的ですが、データ量が多かったり、集計が複雑で抽出に時間がかかると、リクエストタイムアウトや、ガバナ制限に抵触する可能性もあります。
この課題を回避する方法として、抽出のリクエスト処理と集計&ファイル書込み処理を分けて非同期処理で実装する方法を紹介します。
解決策
実装方法1
非同期処理を実装する方法として、まず「@futureのApex Calloutを使った非同期リクエストで処理を行う」方法が一般的です。
- Salesforceの画面上のダウンロードボタンクリックを契機にApexクラスで、EXCEL出力リクエストオブジェクトに「Start」ステータスのデータを挿入します。
- そのデータのSalesforceIdをパラメータにして、SkyOnDemandにApex HTTPを使用した@future非同期コールアウトを行います。
- SkyOnDemandはリクエストを受け取るとSalesforceのEXCEL出力リクエストオブジェクトのステータス項目を「Processing」に更新しておきます。
- EXCELテンプレートは、あらかじめSalesforceのファイルテンプレートオブジェクト配下に添付しておき、SkyOnDemandから何らかのキーで取り出せるようにしておきます。
- その取り出したテンプレートに対して、ビジネスデータを書き出し、EXCEL出力リクエストの配下のAttachmentオブジェクトに添付します。
- 問題なく処理が完了すれば、EXCEL出力リクエストのステータス項目を「Completed」に更新し、エラーの場合は、「Error」に更新しておきます。
実装方法2
また、こういう方法で実装することも可能です。同期のリクエストだけど、SkyOnDemandの処理を開始するトリガ-ファイルだけを作ってレスポンスをもらい、内部的に非同期処理にする方法です。
- Salesforceの画面上のダウンロードボタンクリックを契機にApexクラスで、EXCEL出力リクエストオブジェクトに「Start」ステータスのデータを挿入します。
- EXCEL出力リクエストオブジェクトのSalesforceIdをパラメータにして、SkyOnDemandにApex HTTPを使用したコールアウトを行います。
- SkyOnDemandはリクエストを受け取ると、SalesforceIdを書いたトリガーファイルを書き出し、レスポンスを返します。
- ファイルトリガーが発火し、SkyOnDemandはトリガーファイルに書かれているSalesforceIdを読み出し、それをKEYにSalesforceのEXCEL出力リクエストオブジェクトのステータス項目を「Processing」に更新しておきます。
- EXCELテンプレートは、あらかじめSalesforceのファイルテンプレートオブジェクト配下に添付しておき、SkyOnDemandから何らかのキーで取り出せるようにしておきます。
- その取り出したテンプレートに対して、ビジネスデータを書き出し、EXCEL出力リクエストの配下のAttachmentオブジェクトに添付します。
- 問題なく処理が完了すれば、EXCEL出力リクエストのステータス項目を「Completed」に更新し、エラーの場合は、「Error」に更新しておきます。
事例
弊社では、実際にこれに近い実装方法で、20個近くあるオブジェクトのデータや1000万件近くのデータを集計して、結果をEXCELに抽出するような事例もございます。 また、SkyOnDemandに標準装備されているテンポラリデータベースを使って集計すれば、複雑なテーブル結合を要するものであったり、SOQLの集計関数の利用時に一番引っかかりやすいガバナ制限(Query Rowsの制限)を回避するのに非常に便利です。
問題と検討事項
ボタンアクションの挙動
これは実装の一例にすぎません。EXCEL帳票が出来上がるまで時間を要するので、後で一覧からダウンロードができればOKというリクエストもあれば、少なくともSkyOnDemand側でリクエストを受け取ったかどうかを一連の流れで把握したいというリクエスト、または、1日に何万件というダウンロードを必要する場合など要件は、様々あると思います。
以下の制限を考慮しながら、画面デザインやその挙動をお客様と合意して開発しましょう。
メソッドを非同期実行する際の主な制限
- 1回のトランザクション内で実行は最大10回まで
- 戻り値を取れない
- 実行順序は保証されない
- VisualforceコントローラのgetMethodNameまたはsetMethodNameメソッド内でも、コンストラクタ内でも使用できない
- futureアノテーションを持つメソッドから、futureアノテーションを持つ別のメソッドを呼び出せない。トリガーを経由する場合も同様
- 1日(24時間)でのCallout数は、250,000 か、組織内のユーザライセンス数 × 200 のいずれか大きい方の値
- キューにはいって実行されるまで10分ぐらいかかることがある。
メソッドを同期実行する際の主な制限
- 1回のリクエスト中のすべてのコールアウト処理のタイムアウトは、120秒(デフォルトは10秒)
- 5 秒以上要する処理が 10 個まで同時に実行可能となり、11 個目の処理は"ConcurrentPerOrgApex"エラーとなる
- 同じホストの URL へのコールアウト要求の最大同時要求数は、20個
- Calloutの前後両方にDMLを設置したり逆にDML前後両方にcalloutするとエラーとなる(Actionfunctionなどを使って画面上で回避可能)
※Summer14時点での制限です。
過去のリクエストデータの扱い
EXCEL出力リクエストオブジェクトは、ダウンロードの度に増幅し続けていくので、組織のデータ量や、ビジネス要件に合わせて、定期的にパージ処理を行っておきます。 バッチApexで実装可能ですが、SkyOnDemandだと実装は容易で、「ゴミ箱を空にする」機能や、Bulk APIを使って大量のデータを削除することも可能です。 さらに「Log Manager for Salesforce」アプリケーション(無償提供)を利用することで、この削除バッチ処理の実行状況をSalesforce上で確認することができて、追加設定を行うと、Salesforceから再実行(Re-Run)することも可能です。
最後に
機能要件に対する実現方法を提案する際に、「今まで当たり前だと思っていたものを考えなおしてみる」というのは、非常に大切です。
マクロからWebサービスAPIを呼ぶ方式をやめて、SkyOnDemandでEXCELにデータを書き込む実装方法に変えたり、思い切ってEXCELをやめて、SkyVisualEditorを使って、画面にしてみるという提案も時には有効かと思います。
Salesforceコンサルタントとして、大事なのは、お客様にそのような選択肢を与えてあげることかと思いますので、是非選択肢の1つとして、SkyOnDemandを検討してみてください。