スケーラブル カスタマイズ設計: データベース トランザクション
注意
これは、スケーラブル カスタマイズ設計に関する 2 つめのトピックです。 最初から始めるには、Microsoft Dataverse におけるスケーラブル カスタマイズ設計 を参照してください。
ここで直面する多くの課題の背後にある最も基本的な概念の 1 つは、データベース トランザクションの概念です。 Dataverse で、データベースはシステムへのほとんどすべての要求の中心であり、データの一貫性が主として適用される場所です。
- Dataverse のデータ操作は、内部でも、コードのカスタマイズの一部でも、完全に独立して機能することはありません。
- すべての Dataverse のデータ操作は、データ レベル、またはプロセッサ、メモリ、I/O の使用状況などのインフラストラクチャ レベルのいずれかで、同じデータベース リソースと対話します。
- 不整合な変更から保護するために、各要求は表示または変更されるリソースをロックします。
- これらのロックはトランザクション内で行われ、トランザクションが確定または中止されるまで解除されません。
トランザクションおよびロックの認識
この領域で問題が発生する一般的な理由は、カスタマイズがトランザクションにどのように影響を与えるかという認識不足です。
これがどのように行われるかの詳細はこの記事の範囲外ですが、考慮すべき最も単純な要素は、Dataverse がそのデータベース内のデータと対話することです。 SQL Server は、そのデータに対してトランザクションが行う適切なロックを決定します。次に例を示します。
- レコードを作成するとき、そのレコードに対して書き込みロックを生成します。
- レコードを更新するとき、そのレコードに対して書き込みロックをかけます。
- ロックがテーブルまたはレコードに対してかけられるとき、対応するすべてのインデックス レコードに対してもかけられます。
ただし、これらのロックのスコープと期間に影響を与えることは可能です。 また、SQL Server に対して、特定のシナリオにはロックは不要であると指示することも可能です。
ここでは、SQL Server データベースのロックと、同じデータにアクセスしようとする別の要求の影響について考えてみます。 次の例では、アカウント作成によって一連のプロセスが設定されました。これらには、レコードが生成されたらすぐにトリガーされるプラグインを含むものや、生成時に開始される関連の非同期ワークフローに含まれるものがあります。
この例は、他の活動も同じアカウント レコードと対話している中、アカウントの更新プロセスに複雑な後処理がある場合の結果を示しています。 アカウントの更新処理がまだ実行中に非同期ワークフローが処理される場合、このワークフローは、まだロックされている同じアカウント レコードを変更するための更新ロックの取得を待ってブロックされる可能性があります。
トランザクションは、プラットフォームへの特定の要求の有効期間内のみに行われることに注意してください。 ロックは、ユーザー セッション レベルで、または情報がユーザー インターフェイスに表示されている間は、実行されません。 プラットフォームが要求を完了した直後に、データベース接続や関連のトランザクション、そしてかけられたすべてのロックを解除します。
ブロック
前の例のようなブロックはそれ自体不便ですが、Dataverse が数百の同時アクションを処理できるプラットフォームであると考えると、より深刻な結果を招く可能性があります。 個別のアカウント レコードをロックすることにはある程度限定的な意味合いしかないものの、リソースがより激しく争われるとどうなるでしょうか?
たとえば、各アカウントに一意な参照番号が与えられたとき、使用されている参照番号を追跡している単一のリソースが、すべてのアカウント生成プロセスによってブロックされてしまう可能性があります。 自動付番の例 で説明されているように、多数のアカウントが同時に生成される場合、重複している要求はすべてその自動付番のリソースにアクセスする必要があり、アクションを完了するまでそのリソースをブロックします。 各アカウントの作成プロセスに時間がかかり、同時要求が多くなればなるほど、ブロックも多く実行されます。
自動付番のリソースのロックを取得するための最初の要求は簡単に取得できますが、2 番目の要求は、次の一意の参照番号が何かを確認する前に最初の要求が完了するのを待つ必要があります。 3 番目の要求は、1 番目と 2 番目の要求がどちらも完了するのを待つ必要があります。 要求が多ければ多いほど、ブロックも長く発生します。 十分な数の要求があり各要求に十分な時間がかかると、個々の要求が正しく完了したとしても、この遅れにより後の要求がタイムアウトするまでプッシュされる可能性があります。
ロック解除
ロックが解除されずにトランザクションの完了まで保持される主な理由は 2 つあります。
- トランザクションが後でデータ アイテムを更新するために別要求を行う場合に備えて、データベース サーバーは一貫性のためにロックを保持します。
- またデータベース サーバーは、後で発行されるエラーまたは中止のコマンドがトランザクション全体をロールバックする可能性があるということを考慮する必要があるため、トランザクションの有効期間全体にわたりロックを保持し一貫性を確保する必要があります。
プロセスが特定のデータとの対話を完了した可能性があっても、トランザクション全体が完了し確定するまでロックが保持されることを認識しておく必要があります。 トランザクションが長くなるほどロックは長く保持され、他のスレッドがそのデータと対話することを防ぎます。 後に示されるように、これには同じトランザクション内で機能する関連のカスタマイズも含まれ、同期ワークフローなどのトランザクションの有効期間を大幅に延長できます。
以下の例では、アカウントの作成前プラグインのユーザー定義エンティティへの書き込みロックは、アカウント作成に関連付けられたすべてのロジックが完了するまでロックされています。
断続的エラー: タイミング
断続的な動作は、同時活動のブロックの明らかな症状です。 先に失敗したときと全く同じアクションを繰り返して後で成功する場合は、同時に発生している他の何かによってエラーや遅延が引き起こされた可能性が非常に高いです。
問題のデバッグには、問題がある機能を最低限に抑えることを含める場合が多いため、これを理解することが重要です。 ただし、断続的にのみ問題が発生する場合、失敗するアクションが他の活動と競合しているのはシステム内のどこかを確認しなければならない場合があり、さらに潜在的な競合ポイントを確認する必要があります。 個々のプロセスを最適化することで競合を軽減できます。ただし、処理時間が短くなるほど、活動が他のプロセスと競合する可能性は低くなります。
トランザクション コントロール
多くの場合トランザクションの使用方法はプラットフォームに管理を委ねることができますが、必要なロジックが複雑で、結果を達成するにはトランザクションに対する理解と影響が必要だというシナリオがあります。 Dataverse には、トランザクションの使用方法に異なる影響を与える、多数のカスタマイズ方法が用意されています。
各タイプのカスタマイズがプラットフォーム トランザクションにどのように関与するかを理解することで、Dataverse で複雑なシナリオを効果的にモデル化し、その動作を予測できます。
前述のように、トランザクションはプラットフォームへの要求の有効期間中にのみ保持され、プラットフォームのステップが完了した後に維持されるものではありません。 この限定された期間により、トランザクションが外部クライアントによって長時間保持され、他のプラットフォームの活動がブロックされることを回避します。
プラットフォームのジョブは、プラットフォームのトランザクション パイプラインを通して一貫性を維持し、必要に応じてカスタマイズを同じトランザクションに参加させることです。
モデル駆動型アプリがトランザクションを使用する方法
カスタマイズがプラットフォームとどのように対話するかを理解する前に、モデル駆動型のアプリがプラットフォームへの要求をどのように使用し、それがどのようにトランザクションの使用へ影響を与えるかを理解しておくと有用です。
オペレーション | Description |
---|---|
フォーム (Retrieve) | • 他の使用への影響は低いです。 |
Create | • プラットフォームを使用して作成要求を実行します • 新規レコードが他には何もブロックしないため、他の使用への影響は低いはずです。 • 完了までテーブル全体に対するロック クエリをブロックする可能性があります。 • カスタマイズで関連のアクションがトリガーされることが多く、それが影響を与える可能性があります。 |
更新する | • プラットフォームを使用して更新要求を実行します。 • 競合がある可能性が高いです。 更新ロックは、そのレコードを更新するものをすべてブロックします。 • 他の活動をトリガーする場合が多いです。 • まれに、更新ロックが読み取りロックをブロックすることがあります。 これが発生する可能性のある一例は、Dataverse メタデータが変更される場合です。 Dataverse メタデータ変更トランザクションによる読み取りは、更新ロックによってブロックされます。 |
表示 (RetrieveMultiple) | • 他の使用への影響は低いです。 • クエリのパフォーマンスが低いと、データベースのリソースが過負荷になり、タイムアウトが発生する可能性があります。 |
イベント パイプライン: プラットフォーム ステップ
イベント パイプラインが開始されると、プラットフォームのステップを含めるために SQL トランザクションが作成されます。 これにより、プラットフォームによって実行されるすべてのデータベース活動が一貫して処理されます。 トランザクションはイベント パイプラインの最初に作成され、処理が成功したかどうかによって、処理が完了したときに確定または中止されます。
カスタマイズ要求
カスタマイズ内でプラットフォームにより開始されたトランザクションに参加することもできます。 カスタマイズの種類ごとに異なる方法でトランザクションに参加します。 以下のセクションでそれぞれについて順に説明します。
- 同期プラグイン (事前または事後操作: トランザクション コンテキスト内)
- 同期プラグイン (事前および事後操作: トランザクション コンテキスト内)
- 同期プラグイン (PreValidation: トランザクション コンテキスト外)
- 同期プラグイン (PreValidation: トランザクション コンテキスト内)
- 非同期プラグイン
- プラグイン トランザクションの使用概要
- 同期ワークフロー
- 非同期ワークフロー
- ユーザー定義ワークフロー活動
- ユーザー定義アクション
- Web サービス要求
同期プラグイン (事前または事後操作: トランザクション コンテキスト内)
プラグインがイベントに登録されると、トランザクション内にある PreOperation または PostOperation ステージに対して登録できます。 すべてのプラグインからのメッセージ要求がトランザクションの内部で実行されます。 これはトランザクションの有効期間を意味し、実行されたすべてのロックが延長されます。
同期プラグイン (事前および事後操作: トランザクション コンテキスト内)
プラグインは PreOperation および PostOperation ステージの両方に対して登録できます。 この場合は、トランザクションが PreOperation プラグインの開始から PostOperation プラグインの完了まで延長するため、トランザクションはさらに延長できます。
同期プラグイン (PreValidation: トランザクション コンテキスト外)
プラグインは、PreValidation ステージに登録されることで、プラットフォーム トランザクションの外部で機能するように登録されることも可能です。
注意
これは独自のトランザクションを作成しません。 そのため、プラグイン内の各メッセージ要求はデータベース内で単独で処理されます。
このシナリオは、PreValidation がイベント パイプラインの最初のステージとして呼び出されたときにのみ適用されます。 プラグインが PreValidation ステージで登録されても、次のセクションで示すように、トランザクションに参加することは可能です。 PreValidation プラグインがトランザクションに参加していないとは限りませんが、その場合は実行コンテキストから確認できます。
同期プラグイン (PreValidation: トランザクション コンテキスト内)
関連のシナリオは、PreValidation プラグインが登録されたものの、関連するイベント パイプラインが既存のトランザクション内からのメッセージ要求によってトリガーされたときに発生します。
次の図で示すように、アカウントを作成すると、初回作成の実行時に PreValidation プラグインがトランザクションの外部で実行される可能性があります。 プラグイン後の一部として、2 番目のイベント パイプラインが親パイプライン内から開始されるために関連の子アカウントを作成するというメッセージ要求が行われた場合、それは同じトランザクションに参加します。
その場合、PreValidation プラグインはトランザクションがすでに存在することを検出し、PreValidation ステージに登録されている場合でもそのトランザクションに参加します。
前述のとおり、プラグインは IsInTransaction プロパティの実行コンテキストを確認します。それは、このプラグインがトランザクション内で実行されているかどうかを示します。
非同期プラグイン
プラグインは、非同期で機能するように登録することもできます。 この場合、プラグインは、プラットフォーム トランザクションの外部でも機能します。
注意
プラグインは、自身のトランザクションは作成しません。プラグイン内の各メッセージ要求は独立して処理されます。
プラグイン トランザクションの使用概要
要約:
- 同期プラグインは通常トランザクションに参加します。
- 非同期プラグインは、プラットフォームのトランザクションには一切参加しません。各要求は独立して実行されます。
- PreValidation プラグインは、トランザクションがすでに存在する場合、トランザクションを作成せず参加します。
イベント | ステージ名 | トランザクションまだ存在しません | トランザクションはすでに存在します |
---|---|---|---|
イベント前 | 事前検証 | トランザクションは作成されません。 トランザクションに参加しません。各要求は、データベースへの独立したトランザクションを使用します | 既存のトランザクションに参加します |
イベント前 | 事前操作 | 既存のトランザクションに参加します | 既存のトランザクションに参加します |
イベント後 | PostOperation | 既存のトランザクションに参加します | 既存のトランザクションに参加します |
非同期 | N/A | トランザクションは作成されません。 トランザクションに参加しません。各要求は、データベースへの独立したトランザクションを使用します | 該当なし |
同期ワークフロー
トランザクションの観点から見たとき、同期ワークフローは事前または事後操作のプラグインとして機能します。したがって、それらはプラットフォーム パイプライン トランザクション内で機能し、トランザクション全体の長さに同じ影響を与える可能性があります。
非同期ワークフロー
非同期ワークフローがプラットフォーム トランザクションの外部でトリガーされます。
注意
ワークフローは独自のトランザクションも生成しないため、ワークフロー内の各メッセージ要求は独立して処理されます。
次の図は、プラットフォーム トランザクションの外部で処理される非同期ワークフローと、独立したトランザクションの開始における各ステップを示します。
ユーザー定義ワークフロー活動
ユーザー定義ワークフロー活動は、親ワークフローのコンテキスト内で機能します。
- 同期ワークフロー: トランザクション内で機能
- 非同期ワークフロー: トランザクション外で機能
次の図は、まず同期ワークフロー内で、次に非同期ワークフロー内で機能するユーザー定義活動を示します。
カスタム アクション
ユーザー定義アクションは、独自のトランザクションを作成できます。 これは主要な機能です。 ユーザー定義アクションは、「ロールバックを有効にする」に設定されているかどうかに応じて、プラットフォームのステップ外に別のトランザクションを作成できます。
- 「ロールバックを有効にする」が設定
- トランザクション内で実行中のプラグインからのメッセージ要求によって呼び出され、「ロールバックを有効にする」が設定されている場合、ユーザー定義アクションは既存のトランザクション内で機能します。
- それ以外の場合、ユーザー定義アクションは新しいトランザクションを作成しその中で実行します。
- 「ロールバックを有効にする」が未設定
- ユーザー定義アクションは、トランザクションの内部で機能しません。
Web サービス要求
要求が Web サービスを介して外部に作られると、前に説明したように、パイプラインが作成されパイプライン内で処理されるトランザクションが発生しますが、応答が返されたらトランザクションは維持されません。 次の要求までどのくらいかかるかがわからないため、プラットフォームは他の活動をブロックするリソースのロックを許可しません。
同じ実行コンテキストを使用してプラグイン内で複数の要求が作られる場合、トランザクション参照を維持し、同期プラグインでは各要求が同じトランザクション内で行われるようにするのは、共通の実行コンテキストです。 要求間で実行コンテキストを維持する能力はプラグインの外部では使用できないため、トランザクションは外部で作られた別々の要求間では維持されません。
1 つの Web サービス要求の一部として複数のアクションを Dataverse プラットフォームに渡すことができる 2 つの特別なメッセージがあります。
Message | 内容 |
---|---|
ExecuteMultiple |
これにより、複数の独立したアクションを同じ Web サービス要求内で渡すことができます。 これらの各要求はプラットフォーム内で個別に実行されるため、要求間で保持されるトランザクション コンテキストはありません。 |
ExecuteTransaction |
これにより、同期プラグイン内で作られた複数のメッセージ要求と同様の方法で、同じデータベース トランザクション内で複数のアクションを処理できます。 またこの能力は、複数のメッセージ要求と同様の意味を持ちます。つまり、アクションごとに大幅な時間がかかる場合は (高クエリを作成したり、関連の同期プラグインまたはワークフローの長いチェーンをトリガーしたりする場合)、より幅広いプラットフォームでブロックの問題が発生する可能性があります。 |
プラグインの Web API (OData) 要求
プラグイン内の Web API (OData) 要求を、同じ組織でプラグインとして使用しないでください。 常に IOrganizationService メソッドを使用します。 これにより、トランザクション コンテキストが渡され、パイプライン トランザクションに操作を参加させることが可能になります。
次の手順
データベース トランザクションに加えて、複数のデータ操作がシステムに与える影響を理解することが重要です。 詳細: スケーラブル カスタマイズ設計: 同時実行問題
注意
ドキュメントの言語設定についてお聞かせください。 簡単な調査を行います。 (この調査は英語です)
この調査には約 7 分かかります。 個人データは収集されません (プライバシー ステートメント)。