オブジェクト同時処理のオプション
永続オブジェクトを開く場合や削除する場合には、適切に並行処理を指定することが重要です。
並行処理を指定する理由
以下のシナリオでは、オブジェクトの読み取り時または書き込み時に、適切に並行処理を制御することが重要になる理由についての例を示しています。以下のシナリオを考えてみます。
-
プロセス A は、並行処理を指定せずにオブジェクトを開きます。
SAMPLES>set person = ##class(Sample.Person).%OpenId(5) SAMPLES>write person 1@Sample.Person
-
プロセス B は、同じオブジェクトを並行処理値 4 で開きます。
SAMPLES>set person = ##class(Sample.Person).%OpenId(5, 4) SAMPLES>write person 1@Sample.Person
-
プロセス A がオブジェクトのプロパティを変更し、%Save() を使用してそれを保存しようして、エラー・ステータスを受け取ります。
SAMPLES>do person.FavoriteColors.Insert("Green") SAMPLES>set status = person.%Save() SAMPLES>do $system.Status.DisplayError(status) ERROR #5803: Failed to acquire exclusive lock on instance of 'Sample.Person'
これは、適切な並行処理の制御がない場合の同時処理の例です。例えば、プロセス A がオブジェクトをディスクに保存する可能性がある場合、他のプロセスと競合せずにオブジェクトを保存できるように、並行処理 4 でオブジェクトを開く必要があります。(これらの値については次に説明します。)この場合、プロセス B はアクセスを拒否される (並行処理違反で失敗する)、またはプロセス A がオブジェクトを解放するまで待機する必要があります。
オプション
並行処理はいくつかの異なるレベルで指定できます。
-
使用するメソッドの concurrency 引数を指定できます。
%PersistentOpens in a new tab クラスの多くのメソッドで、この引数 (整数) を指定できます。この引数により、並行処理の制御に使用されるロックの方式が決定されます。並行処理の値は複数あります。
メソッドの呼び出しで concurrency 引数の指定を省略すると、この引数はメソッドによって -1 に設定されます。つまり、操作するクラスに対して DEFAULTCONCURRENCY クラス・パラメータの値が使用されます。次の項目を参照してください。
-
関連するクラスの DEFAULTCONCURRENCY クラス・パラメータを指定できます。すべての永続クラスは、プロセスに既定の並行処理を取得する式としてパラメータを定義する %PersistentOpens in a new tab から、このパラメータを継承します。次の項目を参照してください。
クラス内で、このパラメータをオーバーライドして、ハードコードされた値を指定することも、独自のルールによって並行処理を決定する式を指定することもできます。どちらの場合も、パラメータの値は、指定可能な並行処理の値のいずれかにする必要があります。
-
プロセスの既定の並行処理モードを設定できます。そのためには、$system.OBJ.SetConcurrencyMode() メソッドを使用します (このメソッドは、%SYSTEM.OBJOpens in a new tab クラスの SetConcurrencyMode()Opens in a new tab メソッドです)。
その他の場合と同じように、指定可能な並行処理の値のいずれかを使用する必要があります。プロセスの並行処理モードを明示的に設定しない場合、既定値は 1 です。
$system.OBJ.SetConcurrencyMode() メソッドは、DEFAULTCONCURRENCY クラス・パラメータに明示的な値を指定するクラスには効果がありません。
簡単な例を挙げてみましょう。永続オブジェクトを、その %OpenId() メソッドを使用して、並行処理の値を指定せずに (または値 -1 を明示的に指定して) 開いた場合、クラスで DEFAULTCONCURRENCY クラス・パラメータが指定されておらず、コードで並行処理モードを設定していないときは、オブジェクトの並行処理の既定値は 1 に設定されます。
システム・クラスによっては、DEFAULTCONCURRENCY クラス・パラメータが値 0 に設定されているものがあります。シャード・クラスでは常に並行処理の値 0 が使用されます。特定のオブジェクトの並行処理の値を確認するには、そのオブジェクトの %Concurrency プロパティを調べます。
並行処理の値
このセクションでは、有効な並行処理の値について説明します。まず、以下の詳細に注意してください。
-
アトミック書き込みは、並行処理が 0 より大きいときに保証されます。
-
InterSystems IRIS は、オブジェクトの保存や削除などの処理中にロックの取得と解放を実行します。詳細は、並行処理の値 (どのような制約があるのか、ロック・エスカレーションのステータス、およびストレージ構造) によって異なります。
-
どのような場合でも、オブジェクトがメモリから削除されると、そのオブジェクトのロックも削除されます。
有効な並行処理の値は、以下のとおりです。このリストも示すように、それぞれの値には名前が付いています。
ロックを使用しません。
ロックは必要に応じて取得および解放され、オブジェクトの読み込みがアトミック処理として実行されることを保証します。
新規オブジェクトの作成時に、InterSystems IRIS がロックを取得することはありません。
オブジェクトを開いている間は、InterSystems IRIS はオブジェクトに対する共有ロックを取得します (アトミック読み込みを保証するために必要な場合)。読み込み処理が完了すると、InterSystems IRIS はロックを解放します。
以下のテーブルに、各シナリオに現れるロックを示します。
オブジェクトが作成されるとき | オブジェクトが開かれている間 | オブジェクトが開かれた後 | 保存処理が完了した後 | |
---|---|---|---|---|
新規オブジェクト | ロックなし | N/A | N/A | ロックなし |
既存のオブジェクト | N/A | 共有ロック (アトミック読み込みを保証するために必要な場合) | ロックなし | ロックなし |
1 (アトミック読み込み) と同じですが、オブジェクトを開くときには常に共有ロックを取得します (アトミック読み込みを保証するために必要とされない場合でも、このロックを取得します)。以下のテーブルに、各シナリオに現れるロックを示します。
オブジェクトが作成されるとき | オブジェクトが開かれている間 | オブジェクトが開かれた後 | 保存処理が完了した後 | |
---|---|---|---|---|
新規オブジェクト | ロックなし | N/A | N/A | ロックなし |
既存のオブジェクト | N/A | 共有ロック | ロックなし | ロックなし |
新規オブジェクトの作成時に、InterSystems IRIS がロックを取得することはありません。
既存のオブジェクトを開いている間は、InterSystems IRIS はオブジェクトの共有ロックを取得します。
新規オブジェクトの保存後、InterSystems IRIS はオブジェクトの共有ロックを保持します。
以下のテーブルは、シナリオのリストです。
オブジェクトが作成されるとき | オブジェクトが開かれている間 | オブジェクトが開かれた後 | 保存処理が完了した後 | |
---|---|---|---|---|
新規オブジェクト | ロックなし | N/A | N/A | 共有ロック |
既存のオブジェクト | N/A | 共有ロック | 共有ロック | 共有ロック |
既存のオブジェクトが開かれるとき、または新規オブジェクトが初めて保存されるときに、InterSystems IRIS は排他ロックを取得します。
以下のテーブルは、シナリオのリストです。
オブジェクトが作成されるとき | オブジェクトが開かれている間 | オブジェクトが開かれた後 | 保存処理が完了した後 | |
---|---|---|---|---|
新規オブジェクト | ロックなし | N/A | N/A | 排他ロック |
既存のオブジェクト | N/A | 排他ロック | exclusive lock (排他ロック) | exclusive lock (排他ロック) |
並行処理の値を明示的に指定しない場合、値は -1 に設定されます。これは、既定の並行処理の値が使用されることを意味します。既定の並行処理の値の決定方法に関する詳細は、"オブジェクト同時処理のオプション" を参照してください。
並行処理とスウィズルされたオブジェクト
プロパティによって参照されるオブジェクトは、スウィズルされるオブジェクトのクラスで定義された既定の並行処理を使用してアクセス時にスウィズルされます。そのクラスの既定値が定義されていない場合、オブジェクトは、プロセスの既定の並行処理モードを使用してスウィズルされます。(詳細は、"オブジェクト同時処理のオプション" を参照してください。)スウィズルされるオブジェクトは、それをスウィズルするオブジェクトの並行処理値は使用しません。
スウィズルされるオブジェクトが既にメモリ内にある場合は、実際にスウィズルによってオブジェクトが開かれることはありません。それは、既存のメモリ内のオブジェクトを参照するだけであり、その場合、そのオブジェクトの現在の状態が保持され、並行処理は変更されません。
この既定の動作をオーバーライドするには、以下の 2 つの方法があります。
-
スウィズルされたオブジェクトに対する同時処理を、新しい同時処理を指定する %Open() メソッドの呼び出しでアップグレードします。以下に例を示します。
Do person.Spouse.%OpenId(person.Spouse.%Id(),4,.status)
%OpenId()Opens in a new tab の最初の引数では ID を指定し、2 番目の引数では新しい並行処理を指定し、3 番目の引数 (参照渡し) はメソッドのステータスを受け取ります。
-
オブジェクトをスウィズルする前に、プロセスの既定の並行処理モードを設定します。以下に例を示します。
Set olddefault = $system.OBJ.SetConcurrencyMode(4)
このメソッドは、その引数として新しい同時処理モードを取り、前の同時処理モードを返します。
異なる並行処理モードが必要なくなった場合は、以下のように既定の並行処理モードをリセットします。
Do $system.OBJ.SetConcurrencyMode(olddefault)
バージョン確認 (並行処理引数の代替)
オブジェクトを開いたり削除したりするときに、並行処理引数を指定する代わりに、バージョン確認を実装できます。そのためには、VERSIONPROPERTY というクラス・パラメータを指定します。すべての永続クラスに、このパラメータがあります。永続クラスを定義するときに、バージョン確認を有効にする手順は次のとおりです。
-
更新可能なバージョン番号を保持した %IntegerOpens in a new tab 型のプロパティを、クラスのインスタンスごとに作成します。
-
そのプロパティについては、InitialExpression キーワードの値を 0 に設定します。
-
クラスについては、そのプロパティの名前と等しくなるように、VERSIONPROPERTY クラス・パラメータの値を設定します。VERSIONPROPERTY の値は、サブクラスからは別のプロパティに変更できません。
これでバージョン確認が、クラス内のインスタンスへの更新に組み込まれます。
バージョン確認が実装されると、クラス内のインスタンスがオブジェクトまたは SQL で更新されるたびに VERSIONPROPERTY で指定したプロパティが自動的にインクリメントされます。プロパティがインクリメントされる前に、InterSystems IRIS によってメモリ内の値と保存されている値が比較されます。この値が異なる場合は、同時処理の競合が起こりエラーが表示されます。この値が同じ場合は、プロパティがインクリメントされて保存されます。
この機能一式を使用して、楽観的同時実行制御を実装できます。
VERSIONPROPERTY が InstanceVersion と呼ばれるプロパティを指す場合、クラスの SQL 更新文に同時処理の確認を実装するには、コードを次のように設定することになります。
SELECT InstanceVersion,Name,SpecialRelevantField,%ID
FROM my_table
WHERE %ID = :myid
// Application performs operations on the selected row
UPDATE my_table
SET SpecialRelevantField = :newMoreSpecialValue
WHERE %ID = :myid AND InstanceVersion = :myversion
ここで、myversion は、元のデータで選択したバージョンのプロパティ値を指します。