注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。

整合性と分離性

アクションは、オントロジーからの読み取り、ユーザー定義のロジックの適用、および1個以上のオブジェクトとリンクへの編集の書き込みを行える単一のトランザクションを通じて、オントロジーに編集を適用します。

このページでは、並行実行時にアクションタイプが提供する保証と、特定のユースケースに適した分離レベルの選び方について説明します。

ACID の保証

アクションはデータベースのトランザクションとして動作し、標準的な ACID 特性を提供します。

  • 原子性: アクションによるオントロジーへの編集は、すべて適用されるか、まったく適用されないかのいずれかとなる単一のバッチとして適用されます。この保証は、通知や Webhook など、アクションのその他の副次的効果や、アクションの基になる関数が行う外部呼び出しなどの副次的効果には及びません。
  • 整合性: アクションの編集は、Value Type の制約や必須プロパティなど、オントロジーの制約を満たす場合にのみコミットされます。
  • 分離性: 同時に実行されるアクションが互いにどのような影響を与えるかを規定します。以下のセクションでは、アクションタイプで利用できる分離レベルについて説明します。
  • 永続性: アクションがコミットされると、その編集は永続的に保存されます。

アクションが完了すると、その後に開始されるアクションやクエリでは、完了したアクションによって適用されたすべての編集を参照できます。 この保証は、同じアクションの実行中に行われる読み取りには適用されません。

それらの編集を参照できるかどうかは、書き込みモードで説明するように、アクションの書き込みモードによって異なります。

書き込みの仕組み

プロパティへの書き込みに該当するもの

アクションがプロパティに書き込むかどうかは、アクションの実装方法によって異なります。

オントロジー SDK のアクションリクエスト

オントロジー SDK を通じてアクションを適用する場合、任意のパラメーターをアクションリクエストから省略できるのは、そのパラメーターにデフォルト値が設定されている場合のみです。 デフォルト値にオブジェクトプロパティの現在の値を使用している場合、アクションは省略されたパラメーターにそのプロパティ値を設定し、プロパティに書き戻します。

書き込みモード

アクションには2つの書き込みモードがあります。 どちらのモードでも、アクションの実行中に編集が収集され、最後に単一のアトミックなバッチとしてオントロジーにコミットされるため、ほかのアクションが部分的に適用されたアクションを参照することはありません。 2つのモードの違いは、アクション自身の読み取りで、それまでに行った編集を参照できるかどうかです。

  • バッチ書き込み(デフォルト): アクションの実行中に編集がバッチに収集され、最後に単一のアトミックな操作としてオントロジーにコミットされます。アクション自身の読み取りでは、バッチに収集された編集を参照できません。すべての読み取りで、このアクションがそれまでに収集した編集を含まない、オントロジー内のデータがそのまま返されます。たとえば、アクションがオブジェクトのプロパティを更新した後にそのオブジェクトを再度読み取ると、更新後の値ではなく元の値が返されます。
  • ステージング書き込み: アクションの読み取りで、自身の編集を参照できます。同じアクションの実行内で、アクションが編集したオブジェクトを読み取ると、編集後の値が返され、検索や集計にも編集が反映されます。ステージング書き込みは現在、TypeScript v2、Python、および AIP Logic の関数ベースのアクションタイプでサポートされています。

競合チェックの仕組み

競合チェックを行う分離レベルでは、オントロジーはオブジェクト単位で書き込み同士の競合を検出します。 2つのアクションが同じオブジェクトに並行して書き込むと、いずれかのアクションは競合エラーによって失敗します。 これは、アクションがそのオブジェクトの異なるプロパティに書き込む場合にも適用されます。

オブジェクトタイプまたは多対多リンクタイプの元データセットの再ビルドも、競合を引き起こす可能性があります。 アクション実行間の競合は、両方のアクションが書き込むオブジェクトに限定されますが、元データセットの再ビルドによる競合は、より広い範囲で発生します。 再ビルドによってアクションの書き込み対象のオブジェクトが変更されない場合でも、再ビルドの実行中にアクションで競合が発生することがあります。 これらの競合は一時的なものであるため、再ビルドの完了後にアクションを再試行すると、通常は成功します。

競合が検出されると、失敗したアクションの編集はすべて破棄されます。競合の通知と再試行の方法については、一時的なエラーに対する再試行モードを参照してください。

分離レベル

アクションタイプの分離レベルは、同じデータを操作するほかのアクションと並行してアクションが実行される際に適用される保証を定めます。 分離レベルは、アクション内の各読み込みでどのバージョンのデータが参照されるか、および、どのような並行変更が発生すると、すでに変更されたデータに基づく結果をコミットせずにアクションが失敗するか、という2つのことを定義します。 より強いレベルでは、より多くの異常を防げますが、競合によって拒否されるアクションが増えます。より弱いレベルでは、コミットされる頻度が高まりますが、保証は少なくなります。

アクションタイプの分離レベルは、Ontology Manager のアクションタイプの機能タブで設定します。 レベルは作成後に変更でき、以降の実行に適用されます。

バッチ書き込みを使用するすべての新しいアクションタイプは、デフォルトでスナップショット分離を使用するように作成されます。 ステージング書き込みを使用するアクションタイプは、現在、レガシーモードのみをサポートしています。 詳細については、ステージング書き込みの現在の動作を参照してください。

Ontology Manager の機能タブにある分離レベルの設定。推奨オプションとしてスナップショット分離が選択されています。

アクションタイプで利用可能なレベルを以下にまとめ、続くセクションで詳しく説明します。

レベル読み込み競合チェック
レガシー読み込まれるデータが単一の時点のものである保証はありません。同じアクション内でも、読み込みごとに異なる状態を参照することがあります。このアクションが書き込んだオブジェクトを別のアクションが変更した場合、失敗します。
スナップショット分離すべての読み込みで、アクションの開始時に取得された1つのスナップショットを使用します。このアクションが書き込んだオブジェクトを別のアクションが変更した場合、失敗します。
Read committed各読み込みは、読み込み時点でコミット済みの最新の値を返します。別のアクションが同じオブジェクトを変更していても、編集が適用されます。

レガシー

明示的な分離レベルが導入される前に作成されたアクションタイプでは、機能タブで引き続きレガシー分離モードを利用できます。 バッチ書き込みを使用する新しいアクションタイプでは、代わりにスナップショット分離がデフォルトになります。

読み込み: 通常、読み込まれるデータは単一の時点のものですが、これは保証されません。オブジェクトストレージからのデータ取得でエラーが発生すると、アクションの実行中に基盤となるデータが更新されることがあるため、同じアクションの実行中に読み込まれたオブジェクトが、オントロジーの異なる状態を反映している場合があります。

書き込み: レガシー分離は、両方の書き込みモードをサポートしています。バッチ書き込みでは、同じアクションの実行中に後から行われる読み込みに、その実行中に先に行われた編集は含まれません。ステージング書き込みでは、後の読み込みにそれらの編集が含まれます。詳細については、書き込みの仕組みおよびステージング書き込みの現在の動作を参照してください。

競合チェック: アクションが書き込むオブジェクトを読み込んでからコミットするまでの間に、別のアクションがそのオブジェクトを変更すると、アクションは競合エラーで失敗します。ただし、読み込みの一貫性は保証されないため、アクションが読み込んだだけのデータに基づく判断は、異なる時点のデータを組み合わせたビューに基づいて行われる場合があります。

レガシー分離モードは、下位互換性のために提供されています。

新しいアクションタイプでは、スナップショット分離を使用すると、同じ競合チェックに加えて、すべての読み込みが単一の時点のデータを参照するという保証も得られます。

スナップショット分離

これは、バッチ書き込みを使用する新しいアクションタイプのデフォルトのレベルです。ステージング書き込みを使用するアクションタイプの動作については、ステージング書き込みの現在の動作を参照してください。

ソースは異なるデータを参照する場合があります

ロジックルールアクションと関数ベースのアクションは、単一の整合した時点のオントロジーを読み込みます。関数がソースを呼び出すと、そのソースはアクションの特定時点のビューの外で動作します。ソースはオントロジーやほかのシステムから読み込むことがあり、それらの読み込みでは異なる時点のデータを参照する場合があります。アクションの原子性と競合チェックは、ソースが実行する操作には適用されません。

読み込み: アクションが直接実行するすべてのオントロジーの読み込みは、アクション開始時に取得された、単一の整合した時点のビューを参照します。同じオブジェクトを読み込む場合や同じ検索を2回実行する場合は、このアクションの実行中にほかのアクションが何をコミットしても、常に同じ一致するデータが返されます。

書き込み: アクションはバッチ書き込みを使用するため、同じアクションの実行中に後から行われる読み込みに、その実行中に先に行われた編集は含まれません。

競合チェック: アクションが書き込むオブジェクトを読み込んでからコミットするまでの間に、別のアクションがそのオブジェクトを変更すると、アクションは競合エラーで失敗します。アクションは、並行して行われた変更を上書きするのではなく失敗します。このレベルでは、アクションが読み込むだけのオブジェクトは、競合による失敗の原因にはなりません。競合時の再試行については、一時的なエラー時の再試行モードを参照してください。

書き込みスキュー

競合チェックの対象は書き込まれたオブジェクトのみであるため、スナップショット分離は読み込みと書き込みの競合を検出しません。

2つのアクションがそれぞれ重複するデータを読み込み、その読み込みに基づいて判断し、異なるオブジェクトに書き込んで、両方ともコミットされることがあります。互いの書き込みを参照していれば、どちらもコミットされなかった場合でも同様です。

これは書き込みスキューと呼ばれます。

例: オンコールのローテーションでは、常に1人以上のエンジニアをオンコールに保持する必要があります。エンジニアがローテーションを離れられるのは、ほかの誰かが残る場合に限られるため、アクションは現在オンコールのエンジニアが2人以上いるかをチェックします。エンジニア A と B がどちらもオンコールで、それぞれが自身を外すアクションを同時に実行します。

  • A のアクションは、自身のスナップショットからローテーションを読み込み、オンコールのエンジニアが2人いることを確認して処理を進めます。A を外しても B が残るためです。
  • B のアクションは、自身のスナップショットから同じ状態を読み込み、オンコールのエンジニアが2人いることを確認して、同じ理由で処理を進めます。
  • A は A.onCall = false を書き込み、B は B.onCall = false を書き込みます。2つのアクションは異なるオブジェクトに書き込んだため、書き込み同士の競合はなく、両方ともコミットされます。ローテーションは空になり、不変条件に違反します。

スナップショット分離ではこれを検出できません。違反した不変条件は、いずれかのアクションが書き込んだ単一のオブジェクトではなく、アクションが読み込んだデータ(オンコールのエンジニアの集合)に関するものだからです。

この異常を回避するには、単一の共有オブジェクトへの書き込みとして不変条件を表現するように、ワークフローを再設計します。

たとえば、両方のアクションが onCallCount プロパティを持つ Rotation オブジェクトに書き込むようにすれば、アクション同士が競合するようになります。

コミット済み読み取り

コミット済み読み取り分離モードは近日中に利用可能になります。

読み取り: 各読み取りでは、その読み取りが実行された時点での、コミット済みの最新の値が返されます。固定されたスナップショットはないため、同じアクション内の異なる時点で行われた2回の読み取りの間に別のアクションがコミットすると、それぞれの読み取りで異なる状態が観測されることがあります。

書き込み: アクションはバッチ書き込みを使用するため、同じアクションの実行中に後から行われる読み取りには、その実行中にそれ以前に行われた編集は含まれません(書き込みの仕組みを参照)。

競合チェック: このアクションが書き込むオブジェクトに対して同時に変更が行われても、アクションは失敗しません。最後にコミットしたアクションが優先され、先にコミットしたアクションの編集は破棄されます。

例:Ticket オブジェクトに status = "open" と priority = "low" が設定されています。2つのアクションが同時に実行されます。

  • アクション A が status = "in progress" を設定します。
  • アクション B が priority = "high" を設定します。
  • アクション A が先にコミットします。チケットは status = "in progress"、priority = "low" になります。
  • アクション B が次にコミットします。競合チェックがないため、B のコミットは成功しますが、A の編集全体を上書きします。チケットは最終的に status = "open" と priority = "high" になります。アクション A による status の変更は失われます。

スナップショット分離では、両方のアクションが同じオブジェクトに書き込んだため、アクション B は競合チェックに失敗し、アクション A の編集が保持されます。

この分離レベルでは、正しさの保証が弱くなる代わりに、コミットの成功率が高くなります。パラメーターとして渡された値など、アクションがすでに保持している値のみを書き込み、書き込む内容を決める際に読み取った内容に依存しない場合に適しています。

同時に行われた編集が上書きされる可能性があります

複数のアクションが同じオブジェクトを同時に編集できる場合は、コミット済み読み取りを慎重に使用してください。別のアクションがまだ実行中であっても、あるアクションがコミットすることがあります。後のアクションがコミットすると、競合を報告せずに先のアクションの変更を上書きする可能性があります。

ステージング書き込みの現在の動作

ステージング書き込みを使用するアクションタイプは、現在、レガシー分離モードのみをサポートしています。

このセクションでは、ステージング書き込みによって提供される保証について説明します。

読み取り: 各読み取りでは、その読み取りが実行された時点での、コミット済みの最新の値に、アクション自身のステージングされた編集を加えた値が返されます。固定されたスナップショットはないため、同じアクション内の異なる時点で行われた2回の読み取りの間に別のアクションがコミットすると、それぞれの読み取りで異なる状態が観測されることがあります。ただし、アクション自身のステージングされた編集は、その後の読み取りで常に読み取れます。

書き込み: アクションはステージング書き込みを使用するため、同じアクションの実行中に後から行われる読み取りには、その実行中にそれ以前に行われた編集が含まれます。他のアクションは、最後に編集がアトミックにコミットされるまで、その編集を読み取れません(書き込みの仕組みを参照)。

競合チェック: アクションが書き込むオブジェクトを読み込んだ時点からアクションがコミットする時点までの間に、別のアクションがそのオブジェクトを変更すると、アクションは競合エラーで失敗します。同時に行われた変更を上書きするのではなく、アクションは失敗します。アクションが読み取るだけのオブジェクトは、競合による失敗の原因にはなりません。競合時の再試行については、一時的なエラー時の再試行モードを参照してください。

この動作は、ステージング書き込みの、自身の書き込みを読み取る機能と、書き込み間の競合検出を組み合わせたものです。ただし、オントロジーからの読み取りが単一の時点のデータを返すことは保証されません。

一時的なエラー時の再試行モード

プラットフォームは、競合エラーやオブジェクトストレージからのオブジェクトの読み込みの失敗など、一時的なエラーが発生した場合に、アクションを自動的に再試行できます。再試行のたびに、アクションに設定された分離レベルを使用して、アクションが最初から再実行されます。試行は最大5回です。現在の値を読み取るロジックは、新しい実行で利用可能なデータを使用して編集内容を再計算します。すべての試行に失敗した場合、呼び出し元にエラーが返されます。

デフォルトでは、外部システム呼び出し(Webhook など)のないアクションは再試行されますが、外部システム呼び出しを含むアクションは再試行されません。これは、アクションを再実行すると、たとえば Webhook を2回実行するなど、副次的効果も繰り返されるためです。

[機能] タブの一時的なエラー時の再試行モード設定を使用して、アクションタイプごとにこの動作を調整できます。

機能タブの一時的なエラー時の再試行モードのドロップダウンに表示された利用可能な選択肢。

  • 外部呼び出しのないアクションのみ(デフォルト): プラットフォームは、アクションタイプに外部システム呼び出しがない場合にのみ再試行します。
  • すべてのアクション: プラットフォームは、外部呼び出しを含むアクションも含め、すべてのアクションを再試行します。アクションタイプの外部呼び出しを繰り返しても安全(冪等)であることがわかっている場合に使用します。
  • 無効: プラットフォームは自動的に再試行しません。最初の失敗時に、呼び出し元にエラーが返されます。呼び出し元ですべての競合を検知して処理したい場合に使用します。

それ以外の点では同一の2つのアクションタイプでも、この設定によって競合時の動作が異なることに注意してください。一方は再試行によって競合が解消されるため常に成功するように見えますが、もう一方は呼び出し元に競合エラーを返します。

また、自動再試行により、競合が頻繁に発生するアクションは、最終的に成功した場合でも、完了までにかかる時間が著しく長くなることがあります。

分離レベルを選択する

  • 特別な理由がない限り、スナップショット分離を使用します。スナップショット分離は、個々のオブジェクトに対する読み取り・変更・書き込みロジックを含め、ほとんどのワークフローに最適な分離レベルです。
  • 明示的な分離レベルが導入される前に作成されたアクションタイプでは、分離レベルをスナップショット分離に設定します。これにより、実行中のすべての読み取りが同じ時点のデータを返すことが保証されます。レガシーの動作では、通常は同じ時点のデータが返されますが、保証はされません。ただし、スナップショット分離は実行全体を通して同じ時点のビューを使用するため、アクションの実行時間が長く、頻繁に更新されるデータを編集しようとする場合、再試行の頻度が高くなる可能性があります。
  • コミット済み読み取りを検討するのは、競合による失敗が頻繁に発生し、このモードで許容される異常を理解しており、アクションの書き込みが安定したデータの読み取りに依存しない場合のみです。既存のアクションタイプをこのモードに切り替える前に、コミット済み読み取りセクションの警告を確認してください。