Outsystems Reactiveアプリ高速化手法

# 目次
[1.はじめに](#1-はじめに)
[2.想定読者](#2-想定読者)
[3.実行環境](#3-実行環境)
[4.改善対象の説明](#4-改善対象の説明)
[5.改善策](#5-改善策)
　[5.1.server action化](#51-server-action化)
　[5.2.並列化](#52-並列化)
　[5.3.非同期化](#53-非同期化)
　[5.4.サーバ通信回数削減](#54-サーバ通信回数削減)
[6.まとめ](#6-まとめ)

# 1. はじめに
Outsystemsで作ったReactiveアプリケーションのレスポンスタイムを改善する機会がありましたので、そこで独自に編み出した方法論を残しておきます。
今回の記事は、OutsystemsでSalesforceのデータを取得して更新する処理に関するものです。
同じ様な事象にお困りの方の一助になれば幸いです。

# 2. 想定読者
* 処理は正常に行われるが、レスポンスタイムが長い・遅い
* Traditional Webの開発経験はあるが、Reactive Webは初めて
* クライアント処理、サーバ処理って何？という方

# 3. 実行環境
* Outsystems 11.10.4 (Build 29633)
* Service Studio 11.12.7
* Parallel Processing 1.0.1

# 4. 改善対象の説明
改善しなければいけない対象ロジックを画像の通りとします。
画面のボタンをクリックした際に、Outsystems Reactiveアプリでは必ずスクリーンアクション(クライアントアクション)が呼ばれます。
ロジックの流れとしては、まずOutsystemsバンドルDBからメッセージを取得し、画面変数であるリストへAppendします。
その後、4回SalesforceからRESTでデータを取得し、最後に更新RESTを実行します。

実行すると、、、遅すぎる。
これが、eコマースサイトだと、売上に重大な影響を与えること必至です。

Outsystemsあるあるですが、開発者の腕の見せ所はここからです。
まず、どこに時間が掛かっているのか計測して把握します。
実測値は、Outsystems DBアクセスが1.77秒、Salesforce4回問合せが1.09秒、更新処理が2.19秒
合計が5.05秒！各処理も遅く、全体も遅い、絶望的ですね。
しかし、Outsystemsプラットフォームが遅いからではありません。やってはいけない設計がてんこ盛りの結果です。
次の章から、劇的に改善する方法を順次示していきます。
<img width="130" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2192642/6a60e3d4-0fbb-455a-c2ff-7ad1b09f8b05.png">
# 5. 改善策
## 5.1 server action化
手っ取り早く効果を得やすい方法として、「クライアントアクションをサーバアクションで置き換え」があります。
この処理の前提として、GetMessageの子要素は全てサーバアクションとします。
※クライアントアクションは切り出して、無理にサーバアクション化する必要はありません。
対処方法説明の前に、Outsystems用語の整理をします。
クライアントアクションとは、クライアント側で完結するアクションです。（元来は）
例えば、入力値のチェックやポップアップメッセージの表示等のサーバ通信を必要しない処理です。
サーバアクションとは、サーバと通信する処理を含んだアクションです。サーバにはOutsystems、外部のクラウドサービスも含みます。
例えば、Outsystems DBをSelectしたり、RESTを使ってSaaSと連携したりする処理を指しています。
画面に配置したボタンをクリックした際に、最初に呼ばれるのはクライアントアクションです。

サーバアクションで行うべき処理も、クライアントアクションで実行していないでしょうか。
意識しなくとも確かに作れて、動作・Publishできます。できますが、Outsystemsサーバで行う処理の度に内部的にはREST通信が発生してしまいます。
クライアントアクション内でOutsystemsDB問合せ・サーバアクション版のList Appendを10回実行すると、10回クライアントからRESTが発砲されているのです。
※標準処理であるList Appendも、実はクライアントアクション版とサーバアクション版が存在しています。
サーバアクションの中で、子要素としてサーバアクション版のList Appendを10回定義しても、クライアントからのREST通信は1回に集約されます。

そこで、画像のようにOutsystems DBへの問合せを含む処理はサーバアクション(GetMessageSV)として定義します。
意識して頂きたいのは、サーバアクションに含める事が出来るのはサーバアクションのみという事です。
※クラアントアクションからサーバアクションは呼べるが、その逆は不可
サーバアクション化策を講じることは、子アクションのクライアントアクションをサーバアクションに置き換える作業と言えます。
置き換えて実行してみると、対象部分の処理時間が1.17秒から0.19秒になりました。
84%カット！効果絶大ですね。
<img width="130" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2192642/d7bc92c1-7492-d3c7-2547-7e55627a31ee.png">
## 5.2 並列化
続いて、4回のSalesforce問合せ処理のパフォーマンスを改善します。
前提として、Salesforce側のロジックに手を加えられない場合とします。

REST通信を受ける側が、マルチタスクで処理を捌いてくれる場合に効果がある方法です。
順次実行処理されてしまうと、Outsystemsから同時にリクエストを投げる意味が無くなってしまいます。
今回の例の場合、4回の問合せに実行順序性は無く、またそれぞれのレスポンスは凡そ同じです。

並列化する処理には、どれか一つでも突出した時間を要するものが入っていないことが重要です。
並列実行の完了は、並列化した処理が全て完了したことを以て次の処理へ移るという特徴があるからです。

今回並列化に利用したものは、Forgeアプリ<a href="https://www.outsystems.com/forge/component-overview/2375/parallel-processing">Parallel Processing</a>です。
デモアプリも提供されていますので、利用方法はそちらを参照下さい。
結果は、1.09秒から0.41秒へ改善されました。
<img width="180" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2192642/77e262f6-0a82-5381-ba90-e064b4faeeec.png">

## 5.3 非同期化
処理の中に同期的に実行しなくても良い処理がある場合に効果を発揮するのが、非同期処理です。
例えば、決済確定後に暫く経ってから受付メールがくる場合がそれにあたります。
Outsystemsでは、クライアントアクション・サーバアクションともに非同期実行する方法が提供されています。
詳細は、jyunji_watanabeさんの記事「<a href="https://qiita.com/jyunji_watanabe/items/a26c2b9356c249e79193">Outsystemsで非同期処理を実装する方法(Promiseを使う方法)</a>」を参考にして下さい。

今回は、REST処理をクライアントアクションでラッピングし、それをsetTimeoutで実行する方法を取りました。
結果は、2.19秒が0.03秒に短縮されました。
驚きの効果ですが、実行結果を待たずにコールするだけなので当たり前ですね。
この3つの対策を行うだけで、トータルのレスポンスは、5.05秒から0.58秒へ短縮されました。
<img width="180" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2192642/bb0edfc1-e3e2-5438-4d35-78be70b6ba22.png">
## 5.4 サーバ通信回数削減
これまでの3つの改善を施すことで、十分及第点を達成できたと思いますが、ダメ押しで4つ目の方法を実施してみます。
無視してきましたが、アクションの下にオレンジの破線が付いていたのに気付いたでしょうか。
または、Warning一覧を気に留めて見たことはあるでしょうか。

```
The 'Parent' Client Action calls several Server Actions. 
To avoid performance issues, group them all in a single Server Action and call it instead.
```
実は、パフォーマンスに影響を及ぼす可能性、そして改善策をずっと示してくれていたのです。

サーバアクションやREST等、クライアントで完結しない処理が複数定義されている場合に、それらを一つのサーバアクションに纏めなさいというものです。
これは、クライアントからサーバへのREST通信の回数を削減しなさいと示唆しているのです。

並列処理をサーバアクションに纏めた上で、これと自作のGetMessageSV処理をラップしたサーバアクション"wrap"とします。
実行してみると、何と全体で0.34秒！ほぼ一瞬で処理が完了し、ユーザに操作が戻る様になりました。めでたしx2

<img width="180" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2192642/ef463d28-e64b-3c5e-0cc2-c99e3851dd98.png">

# 6. まとめ
如何でしたか。
5.05秒から0.34秒へと、実に __94%改善__ することが出来ました。
これら4つの改善策には、それぞれ前提があるものの、実行方法を工夫するだけで確かな効果を得られることを示せたと思います。
他にもSalesforce側で複数のRESTを纏めて、Outsystemsとの間のREST回数を減らす方法も有効な方法です。
レンダリングする上でも、パフォーマンスに影響を与えうる落とし穴はあり、気が向いたら書いていきます。