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

一貫性と分離性

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

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

ACID 保証

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

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

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

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

書き込みの仕組み

プロパティへの書き込みと見なされるもの

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

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

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

書き込みモード

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

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

競合チェックの仕組み

競合チェックを行う分離レベルでは、オントロジーはオブジェクト単位で書き込み間の競合を検出します。 2つのアクションが同じオブジェクトに同時に書き込む場合、一方のアクションは競合エラーで失敗します。 これは、アクションがそのオブジェクトの異なるプロパティに書き込む場合にも当てはまります。

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

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

分離レベル

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

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

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

Ontology Manager の「機能」タブにある分離レベルの設定で、推奨される選択肢としてスナップショット分離が選択されている様子。

アクションタイプで利用可能なレベルの概要を以下に示します。詳細は後続のセクションで説明します。

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

レガシー

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

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

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

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

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

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

スナップショット分離

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

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

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

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

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

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

書き込みスキュー

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

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

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

例: オンコールのローテーションでは、常に1人以上のエンジニアをオンコール担当として残す必要があります。エンジニアがローテーションから外れられるのは、ほかの担当者が残る場合のみであるため、アクションは現在オンコール担当のエンジニアが2人以上いるかを確認します。エンジニア A と B がともにオンコール担当であり、それぞれが自分を担当から外すアクションを同時に実行するとします。

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

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

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

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

Read committed

Read committed 分離モードは近日中に利用可能になります。

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

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

競合チェック: 実行中に別のアクションが同じオブジェクトを変更した場合でも、アクションは編集を許可します。両方のアクションがコミットすると、異なるプロパティへの変更は保持されます。両方のアクションが同じプロパティを設定する場合、最後にコミットしたアクションによってその値が決まります。同じオブジェクトに対して多数のアクションを同時に実行すると、まれに少数の競合が発生する場合があります。

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

  • アクション A は status = "in progress" と priority = "medium" を設定します。
  • アクション B は priority = "high" を設定します。
  • アクション A が先にコミットします。チケットは status = "in progress"、priority = "medium" になります。
  • アクション B が次にコミットし、A による priority の変更を上書きします。チケットは最終的に status = "in progress"、priority = "high" になります。アクション A による status の変更と、アクション B による priority の変更は、どちらも保持されます。

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

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

同時に行われた編集が上書きされる場合があります

複数のアクションが同じプロパティを同時に設定できる場合は、Read committed の使用に注意してください。両方のアクションがコミットすると、後のアクションが、そのプロパティの以前の値を上書きします。ほかのプロパティへの変更は保持されます。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

分離レベルを選択する

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