タイムアウトの仕組み
ほとんどのユースケースではデフォルトのタイムアウト値で十分ですが、このガイドでは、OutSystemsアプリケーションの機能やOutSystemsプラットフォーム全般に影響を及ぼす可能性のあるいくつかのタイプのタイムアウト構成を設定するために必要な情報を提供しています。これにより、時間のかかるリクエストをアプリケーションで処理する方法を制御できるようになります。
背景
Webアプリケーションサーバーやデータベース上で実行されるOutSystemsプラットフォームでは、Webの特性により、複数の相互接続に暗黙的な時間制限が存在します。
OutSystemsアプリケーションの開発、デプロイ、実行において、誰もが一度はタイムアウトを経験しているのではないでしょうか。そこで、ここではOutSystemsアプリケーションの適切な開発、デプロイ、実行時に影響を与える可能性のあるタイムアウトイベントをいくつか取り上げたいと思います。
まずは、タイムアウトの種類を確認していきましょう。実行時レベルのタイムアウトとしては、SQLクエリのタイムアウト、アプリケーションリクエストのタイムアウト、Webサービスリクエストのタイムアウト、Webサーバー接続のタイムアウト、セッションタイムアウト、タイマーのタイムアウトがあります。デプロイレベルのタイムアウトとしては、デプロイプロセスに影響する可能性のある実行時レベルのタイムアウトのほかに、Development環境のアップロード/ダウンロードのタイムアウトがあります。
ただし、多くのタイムアウトイベントの原因は、システムが緩慢であること(これはリソースのアップグレードや拡張により改善できます)、あるいはロジックやデータモデルの最適化レベルが非常に低いか連携が遅いことです。アプリケーションの目的やデータセットの量にもよりますが、タイムアウト設定は適切に調整する必要があります。
SQLクエリのタイムアウト
タイムアウトの説明
データベースバックボーン(Microsoft SQL、Oracle、またはMySQL)に支えられているという性質上、OutSystemsプラットフォームとそのアプリケーションで最も多いタイムアウトイベントです。データベースサーバーのパフォーマンスが悪い場合や、データ量の多い環境の場合は特に発生しやすくなります。
このSQLクエリのタイムアウトでは、データベースエンジンがSQL文を完全に実行し、その結果であるデータセットが呼び出し側のアプリケーションに返されるまでの最大時間を定義します。影響は、OutSystemsアプリケーションの簡素なクエリや高度なクエリだけでなく、エンティティアクション (Create、Update、Get、GetForUpdate and Deleted)にも及びます。
さらに、LifeTimeやService CenterのアプリケーションやOutSystemsのサービスに散在するOutSystemsプラットフォームのシステムクエリにも影響を及ぼします。アプリケーションレベルでは、アプリケーションモジュールの開発中に、Development環境で簡素なクエリや高度なクエリに対してこのタイムアウト値を定義することができます。ただし、エンティティアクションやOutSystemsプラットフォームのシステムクエリに個別のタイムアウト値を定義することはできません。プラットフォーム全体のデフォルトのクエリのタイムアウトが適用されるためです。
タイムアウトの症状
このタイムアウト値に達したときに検出される症状は、通常、アプリケーションまたはデプロイプロセスでの実行時エラーの発生と、Service Centerへのログエントリの記録です。ログエントリは、ほぼ以下のようになります。
Message :Error in advanced query GetConsumers in Espace (SELECT ...): Timeout expired.The timeout period elapsed prior to completion of the operation or the server is not responding.
Stack: at ssServiceCenter.Actions.QueryEspace_UpdateCacheGetConsumers(HeContext heContext, Int32 maxRecords, String qpkey)
at ssServiceCenter.Actions.UserActionEspace_UpdateCache(HeContext heContext, Int32 inParameSpaceId, Boolean inParamSkipConsumers, Boolean inParamOnlyConsumers)
at ssServiceCenter.Actions.UserActionEspace_PrepareDeploy(HeContext heContext, Int32 inParamUserId, Int32 inParameSpaceId, Int32 inParamVersionId, Boolean inParamDebug, Boolean inParamExcludePTA, Boolean inParamExcludeCacheUpdate, RLReferenceRecordList inParamDeployPack, RLHEMessageRecordList& outParamErrors)
at ssServiceCenter.Actions.UserActionEspace_Publish(HeContext heContext, Int32 inParamUserId, Int32 inParamVersionId, Boolean inParamForceDebug, Boolean inParamExcludePTA, Boolean inParamExcludeCacheUpdate, RLReferenceRecordList inParamPublishPack, RLHEMessageRecordList& outParamMessages, Int32& outParamErrorCount)
at ssServiceCenter.Actions.WebSrvcServiceStudioPublish(HeContext heContext, String inParamusername, String inParampassword, Int32 inParamversionid, RLHEMessageRecordList& outParammessages)
at ssServiceCenter.WebServices.ServiceStudio.Publish(String username, String password, Int32 versionid, WORCHEMessageRecord[]& messages)
この例では、タイムアウトについて明確に言及されており、エラースタックによってSQLクエリ(この例では高度なクエリQueryEspace_UpdateCacheGetConsumers)でタイムアウトが発生していると断定できます。しかし、常にこのようになるとは限りません。以下のように、タイムアウトエラーが別のエラー(通常は非常に重大な実行時エラー)に隠されてしまう場合もあります。
Error in advanced query DeleteTestResults in DeleteInstanceFromDatabase ( DELETE ...): Object reference not set to an instance of an object.
at ssreg_dashboard.Flows.FlowAdministrationFlow.ScrnManageInstances.CommandDelete(HeContext heContext, Int32 inParamInstanceId)
at ssreg_dashboard.Flows.FlowAdministrationFlow.ScrnManageInstances.wt_Submitwidget491643_Click(Object sender, EventArgs e)
Exception has been thrown by the target of an invocation.
at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess)
at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean verifyAccess)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at ssreg_dashboard.Flows.FlowAdministrationFlow.ScrnManageInstances.recTableTableRecords1_Select(Object sender, DataGridCommandEventArgs e)
at System.Web.UI.WebControls.DataGrid.OnItemCommand(DataGridCommandEventArgs e)
at System.Web.UI.WebControls.DataGrid.OnBubbleEvent(Object source, EventArgs e)
at System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args)
at System.Web.UI.WebControls.DataGridItem.OnBubbleEvent(Object source, EventArgs e)
at System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args)
at System.Web.UI.WebControls.Button.OnCommand(CommandEventArgs e)
at System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument)
at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)
at System.Web.UI.Page.ProcessRequestMain()
この例外的なケースでは、悪名高い「Object reference not set to an instance of an object」エラーが表示されており、重大なクラッシュが発生しているように見えます。開発者が一瞬で青ざめる事態です。しかし実際には、単にタイムアウトイベントが制限を超えたことが原因となっています。1つ目のシナリオほど発生頻度は高くありませんが、アクセスの最中という悪いタイミングでタイムアウトイベントによりSQLリーダーが閉じてしまうと「参照がない」という例外が生成され、このような事象が起こることもあります。
タイムアウトの設定
アプリケーションの簡素なクエリや高度なクエリについては、開発フェーズでアプリケーションのモジュールを編集するときにタイムアウト値を秒単位で指定できます。このようなクエリにタイムアウト値を指定しなかった場合、プラットフォーム全体のデフォルトのクエリのタイムアウトが適用されます。LifeTime、Service Center、OutSystemsのサービスからのクエリにもこのデフォルト値がタイムアウト値として適用されます。
このデフォルトのクエリのタイムアウトは、OutSystemsプラットフォームのConfiguration Toolの[Database]タブで指定できます。デフォルトでは、30秒に設定されています。この値を変更すると、アプリケーション以外のすべてのクエリに影響を与えるので注意してください。
ツールを実行するには、Platform ServerのインストールディレクトリにあるConfigurationTool.exeアプリケーションを実行します。変更を確実に反映するため、ツールを閉じるときには必ず[OK]ボタンを押すようにしてください。
アプリケーションリクエストのタイムアウト
タイムアウトの説明
OutSystemsアプリケーションにWebリクエストを行うと、アプリケーションサーバー(IIS上のASP.NET、またはJBoss)での処理に時間がかかります。いつまでもWebリクエストが終了しないという事態を回避するため、これにはリクエストのタイムアウトが適用されます。つまり、Webリクエストの処理に時間がかかりすぎて定義済みのリクエストのタイムアウトに達した場合、そのリクエストを実行しているスレッドが突然中断し、実行時エラーを生成してリソースの浪費を防ぎます。
タイムアウトの症状
当然、application-not-working-anymoreという実行時エラーの発生は、Service Centerの[Error Logs]ページにすべてロギングされます。このエラーは、リクエストの処理にかかった時間の長さに依存するものであるため、アプリケーションリクエストのどの部分でも発生する可能性があります。たとえば、page_load、page_render、SQLクエリ、エクステンション、eSpaceメソッドなどです。ASP.NETバージョンの場合、「Thread was being aborted」というメッセージから、これがタイムアウトイベントであることがわかります。たとえば、以下のエラースタックの原因は、SQLクエリが実行されたタイミングで発生したWebリクエストのタイムアウトイベントです。
Module_Name:
Message :[1] Error in advanced query GetOldProducers in Espace_UpdateCache_OldProducersRefs (SELECT ...): Thread was being aborted.
Stack: at ssServiceCenter.Flows.FlowOperations_eSpace.ScrneSpace_Publish_Logic_Go.Preparation(HeContext heContext)
at ssServiceCenter.Flows.FlowOperations_eSpace.ScrneSpace_Publish_Logic_Go.Page_Load(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain()
もう1つ例を見てみましょう。同じくOutSystemsプラットフォームのASP.NETバージョンで、エクステンションメソッドの呼び出し中にタイムアウトイベントが発生した場合です。
Module NameWeb service met
Message: Thread was being aborted.
Stack: at OutSystems.NssOMLProcessor.CssOMLProcessor.MssCompileeSpaceAsync(Boolean ssDebug, Byte[] ssOML, String ssServerKind, RLHEMessageRecordList& ssMessages, Int32& ssErrorCount, String& ssErrorText, String ssEspaceName, Int32 ssEspaceVersionId)
at ssServiceCenter.RssExtensionOMLProcessor.MssCompileeSpaceAsync(HeContext heContext, Boolean ssDebug, Byte[] ssOML, String ssServerKind, RLHEMessageRecordList& ssMessages, Int32& ssErrorCount, String& ssErrorText, String ssEspaceName, Int32 ssEspaceVersionId)
at ssServiceCenter.Actions.ssCompileFlow3004763(HeContext heContext, String ssusername, String sspassword, Int32 ssversionid, RLHEMessageRecordList& ssmessages)
at ssServiceCenter.WebServices.ServiceStudio.Compile(String username, String password, Int32 versionid, WORCHEMessageRecord[]& messages)
このように、エラーメッセージから簡単にわかります。
タイムアウトの設定
OutSystemsプラットフォームのASP.NETバージョンでは、[httpRuntime]セクションにあるmachine.configファイルのexecutionTimeoutパラメータでこのタイムアウトイベント値を定義します。デフォルトでは値は設定されていません。その場合のデフォルトは100秒となります。アプリケーションサーバーにexecutionTimeoutを設定する方法については、OutSystemsプラットフォームのインストールチェックリストをご覧ください。ご利用のバージョン、およびアプリケーションサーバースタック用のインストールチェックリストは、OutSystemsのダウンロードページから入手できます。
Webサービスリクエストのタイムアウト
タイムアウトの説明
Webサービスは、他のアプリケーションや外部システムとの連携手段として非常によく使用されます。アプリケーションは、Webサービスを通じてリモートでメソッドを呼び出すことができます。こうした呼び出しは、標準的なWebリクエスト/レスポンスを用いたWebサーバーとのXML情報のやりとりにすぎません。継承されているWebリクエストのタイムアウトとは別に、Webサービス自体にも呼び出しのタイムアウトイベントが存在します。このタイムアウトイベントは、Webサービス呼び出しからレスポンスすべての受け取りまでにかかった合計時間を定義します。
タイムアウトの症状
タイムアウトイベント発生時の典型的な症状は、非常に明快です。OutSystemsアプリケーションでWebサーバーアクションを呼び出した場合、Webサービスに時間がかかりすぎると以下のようなタイムアウトエラーが発生することがあります。
Module_Name: Web service met
Message: The operation has timed-out.
Stack: at System.Web.Services.Protocols.WebClientProtocol.GetWebResponse(WebRequest request)
at System.Web.Services.Protocols.HttpWebClientProtocol.GetWebResponse(WebRequest request)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
at sswstimeout_om_.WebClient313.WebService1.VeryLongMethod()
この例では、このタイムアウトイベントにWebサービス呼び出しのタイムアウトが存在していることを見分けるのに、WebClientProtocolメソッドが重要な役割を果たしています。
タイムアウトの設定
Development環境でeSpaceを開発しているときに、そのeSpaceにWeb参照を追加する場合、各メソッド/アクションにTimeout In Secondsプロパティを設定できます。ここでは、Webサービスメソッドの呼び出しでタイムアウトイベントを発生させるまでの最大時間を秒で指定できます。このプロパティで値を指定しなかった場合、Web参照メソッドのタイムアウトは100秒となります。
タイマーのタイムアウト
タイムアウトの説明
タイマーは、スケジュール設定された時間に指定されたアプリケーションロジックを実行するOutSystemsアプリケーションの同期ジョブであり、OutSystems Scheduler Serviceによって呼び出されます。タイマーのタイムアウトイベントは、タイマーの呼び出しから完了までの時間が指定の値を超過した場合に発生します。
タイムアウトの症状
タイマーが設定されたタイムアウトに到達すると、Service Centerの[Error Logs]ページにエラーログが登録されます。エラーメッセージとエラースタックは以下のようになります。
Module NameScheduler Service:
Error Message: Error executing request http://127.0.0.1/TesteSpace/timerhandler.asmx for Timer Send_Messages.Request duration = 1439 secs.[Marked as 'not running'.Computed next run.]
Stack: System.Net.WebException: The operation has timed-out.
at System.Web.Services.Protocols.WebClientProtocol.GetWebResponse(WebRequest request)
at System.Web.Services.Protocols.HttpWebClientProtocol.GetWebResponse(WebRequest request)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName Object[] parameters)
at OutSystems.HubEdition.RuntimePlatform.TimerHandler.ExecuteTimer(String ssKey Int32 timeout)
at OutSystems.HubEdition.Scheduler.Scheduler.ExecuteJob(JobData job)
OutSystems Scheduler Serviceが、実行するアプリケーションのタイマーを呼び出しているため、タイマーの実行を中止するタイムアウトイベントの発生に伴うエラー例外をキャッチすることもできます。この例では、タイマーの実行は1439秒の時点で中止されています。
タイムアウトの設定
Development環境でeSpaceを開発しているときや、タイマーオブジェクトを作成しているときに、Timeout in Minutesプロパティを指定できます。この値は、OutSystemsプラットフォームがタイマーに関連付けられたアクションの実行を完了させる必要がある最大経過時間となります。OutSystemsプラットフォームによるタイマー処理の詳細については、Service Studioのヘルプにある「タイマーの処理方法」のトピックをご覧ください。
メール送信のタイムアウト
タイムアウトの説明
OutSystemsプラットフォームでは、OutSystemsアプリケーションから直接メールを送信できます。メール送信プロセスは、以下の2つの手順で実行されます。
-
1つ目の手順では、メールをレンダリングしてキューに追加します。レンダリングでは、メールのコンテンツが含まれた「Webページ」をレンダリングする特定のエンドポイントに、特殊なWebリクエストを送信する必要があります。このコンテンツは、データベースに保存されます。このリクエストは、メールを送信するロジックで同期的に行われます。 たとえば、ページ内のボタンをクリックしてメールを送信した場合、ページが返される前にメールがレンダリングされ、キューに追加されます。
-
2つ目の手順では、Scheduler Serviceがキューからメールを取得し、サーバーのSMTP構成を使用してサーバーへ送信します。
1つ目の手順では、メールで送信するページのレンダリングにタイムアウトが適用されます。ページのレンダリングに時間がかかりすぎる場合、アプリケーションのメール送信能力に影響を及ぼすおそれがあります。
タイムアウトの症状
タイムアウト値に到達すると、以下のようなエラーメッセージが表示されます。
Error creating Email.The operation has timed out
at System.Net.HttpWebRequest.GetResponse()
at OutSystems.HubEdition.RuntimePlatform.Email.EmailHelper.HttpGetContent(String ssUrl, String method, String contentType, String userAgent, Cookie cookie, QueryParameter[] parameters, String& ssContent, String& ssContentEncoding)
at OutSystems.HubEdition.RuntimePlatform.Email.EmailHelper.HttpPost(String ssUrl, QueryParameter[] parameters, String userAgent, Cookie cookie, String& ssContent, String& ssContentEncoding)
at OutSystems.HubEdition.RuntimePlatform.Email.EmailProcessor.SendEmailRequest(String url, String from, String to, String cc, String bcc, Int32 activityId, Int32 tenantId, Boolean storeContent, EmailType type) On
at OutSystems.HubEdition.RuntimePlatform.Email.EmailProcessor.SendEmailRequest(String url, String from, String to, String cc, String bcc, Int32 activityId, Int32 tenantId, Boolean storeContent, EmailType type)
at ssUserManagement.Flows.FlowMain.ScrnListUsers.CommandResetPassword(HeContext heContext)
非常にわかりやすいエラーメッセージです。
タイムアウトの設定
送信するメールのレンダリングに時間がかかりすぎる場合、メールのレンダリングにかける時間を短くする必要があります。メールエンジンのページレンダリングプロセスの制限に対応できるタイムアウト設定がないためです。
ページリクエストでHTTPRequestHander.SetRequestTimeoutを使用しても、メール処理時間の延長はできません。一方、メールを呼び出すメソッド(OutSystems.HubEdition.RuntimePlatform.Email.EmailHelper.HttpGetContent)には、ベストプラクティスを遵守できるように制限が設けられています。
つまり、呼び出し側はリクエストの準備ができるまで待機しません。ページのレンダリングにかかる時間を短縮して解決する必要があります。