複数のクライアントのサポート
この章では、以下の項目について説明します。
Btrieve クライアント
Btrieve クライアントとは、Btrieve 呼び出しを行うアプリケーション定義のエンティティです。各クライアントは Btrieve 呼び出しを実行でき、MicroKernel に登録されているファイルなどのリソースを個々に持ちます。また、MicroKernel はクライアントごとに排他トランザクションと並行トランザクションの両方の状態を保持します。
複数のクライアントを同時にサポートしなければならない場合は、パラメーターとしてクライアント ID を含む BTRVID 関数または BTRCALLID 関数を使用します。クライアント ID パラメーターは、MicroKernel がコンピューター上のクライアントを区別できるようにする 16 バイト構造体のアドレスです。以下に、クライアント ID を使用することが有効である場合の例を示します。
•すべて同時に処理されるいくつかのトランザクションを実行するマルチスレッド型アプリケーションを作成します。アプリケーションでは、Begin Transaction オペレーションごとに異なるクライアント ID を指定します。MicroKernel は、クライアント ID 別にトランザクションの状態を保持します。
•2 つのクライアント ID を使用し、クライアント ID ごとにいくつかのファイルを開くアプリケーションを書きます。アプリケーションは BTRVID、BTRCALLID、または BTRCALLID32 を使って Reset オペレーションを実行できるので、MicroKernel は指定された 1 つのクライアント ID のファイルを閉じて、リソースを解放することができます。
•アプリケーション自体の複数のインスタンスが同時に動作できるようなアプリケーションを書きます。アプリケーションのデータの整合性をとるために、MicroKernel にはすべてのインスタンスが 1 つのクライアントであるように見えなければなりません。この場合、アプリケーションのどのインスタンスがその呼び出しを行っているかに関係なく、アプリケーションは各 Btrieve 呼び出しで同じクライアント ID パラメーターを提供します。
•Dynamic Data Exchange(DDE)サーバーの役割を果たすアプリケーションを書きます。Btrieve 呼び出しを行うサーバー アプリケーションは、サーバー アプリケーションへ要求を発信するアプリケーション間で返された情報を分配する必要があります。この場合、アプリケーションは各要求元のアプリケーションに異なるクライアント ID を割り当てることにより、複数のクライアント間に配信される情報を追跡するための手段を提供できます。
MicroKernel はいくつかの並行制御方法を提供し、いくつかの実装ツールを使用して、複数のクライアントが同じファイル内のレコードを並行してアクセスまたは変更しようとする場合に発生する可能性のある競合を解決します。
並行制御方法は以下のとおりです。
実装ツールは以下のとおりです。
•明示的レコード ロック
•暗黙レコード ロック
•暗黙ページ ロック
•ファイル ロック
以降では、MicroKernel の並行制御方法について詳しく説明します。各セクションを読むとき、表
35 を参照してください。この表では、2 つのクライアントが同じファイルのアクセスまたは変更を行おうとする場合に発生する可能性のある競合のタイプをまとめています。この表
35 では、ローカル クライアントのアクションを説明しています。
メモ: アプリケーションが BTRVID 関数を使用して同じアプリケーション内で複数のクライアントの定義と管理を行う場合、そのようなクライアントはローカル クライアントと見なされます。
この表では、クライアント 1 は表の左端の列の略語によって識別されるアクションを実行し、次にクライアント 2 が表の上端の行の略語によって識別されるアクションのうちの 1 つを実行しようとします。
これらの略語によって表されるアクションについては、
アクション コードで説明しています。
前提条件
•表の特定のセルでは、クライアント 2 はクライアント 1 がアクションの実行を開始した後にアクションを実行しようとします。最初のアクションが終了しないと、第 2 のアクションは開始できません。
•クライアント 2 が更新または削除操作を実行するセルの場合、クライアント 2 はクライアント 1 がアクションを実行する前に、影響を受けるレコードをあらかじめ読み取っているものとします。
•アクション MDR および MTDR のようにクライアント 2 のアクションが明示的に記述されていない限り、双方の操作が読み取りであっても、更新または削除であっても、クライアント 1 と 2 は常に同じレコードに対してアクションを実行します。
•クライアント 2 のアクションが明示的に記述されていない限り(アクション ITDP を参照してください)、クライアント 1 と 2 がともに挿入、更新、または削除操作を実行する場合、両クライアントは共通するページのうち少なくとも 1 ページを変更します。
•クライアント 1 の挿入操作の後でクライアント 2 が変更を実行する場合、変更されるレコードは挿入されたレコードではありませんが、両レコードはファイル内の 1 つ以上のデータ ページ、インデックス ページ、または可変ページを共有しています。
アクション コード
RNL | トランザクションでない処理または並行トランザクションにおける、ロック要求を伴わない読み取り。 |
RWL | トランザクションでない処理または並行トランザクションにおける、ロック要求を伴う読み取り。 |
INT | トランザクションでない処理における挿入。 |
ICT | 並行トランザクションにおける挿入。 |
ITDP | 並行トランザクションにおける挿入。同様に並行トランザクション内にいるクライアント 1 が挿入、更新、または削除によって変更したページとは異なるページを変更する。 |
MNT | トランザクションでない処理における変更(更新または削除)。 |
MDR | トランザクションでない処理における変更。クライアント 1 によって変更されたレコードとは異なるレコードを変更する。 |
MCT | 並行トランザクションにおける変更。 |
MTDR | 並行トランザクションにおける変更。クライアント 1 によって変更されたレコードとは異なるレコードを変更する。 |
EXT | 排他トランザクションにおける読み取り、挿入または変更。 |
競合コード
適用外 | 適用されません(該当なし)。 |
NC | クライアント 1 とクライアント 2 のアクション間に競合またはブロックはありません。 |
RB | レコード レベルのブロック。クライアント 2 は、クライアント 1 でかけられたレコード ロックのためにブロックされます。 |
PB | ページ レベルのブロック。クライアント 2 は、クライアント 1 でかけられたページ ロックのためにブロックされます。 |
FB | ファイル レベルのブロック。クライアント 2 は、クライアント 1 でかけられたファイル ロックのためにブロックされます。 |
RC | レコードの競合。初めクライアント 2 が読み取っていたレコードを後からクライアント 1 が変更したため、クライアント 2 は操作を実行できません。MicroKernel は、ステータス コード 80 を返します。 |
競合コード RB、PB、および FB の場合、クライアント 2 がノーウェイト タイプの操作(たとえば、ノーウェイト ロックを指定した読み取りや、500 バイアスを指定して開始された並行トランザクションでの挿入または変更など)を指定していない限り、MicroKernel はクライアント 2 のアクションを再試行します。ノーウェイトの操作の場合は、MicroKernel はエラー ステータス コードを返します。
表 35 ローカル クライアントに関して発生する可能性のあるファイル操作の競合
クライアント 2 のアクション |
| RNL | RWL | INT | ICT | ITDP | MNT | MDR | MCT | MTDR | EXT |
クライアント 1 のアクション | | | | | | | | | | |
RNL | NC | NC | NC | NC | 適用外 | NC | 適用外 | NC | 適用外 | NC |
RWL | NC | RB | NC | NC | 適用外 | RB | 適用外 | RB | 適用外 | RB |
INT | NC | NC | NC | NC | 適用外 | NC | 適用外 | NC | 適用外 | NC |
ICT | NC | NC | PB | PB | NC | PB | 適用外 | PB | 適用外 | PB |
MNT | NC | NC | NC | NC | 適用外 | RC | NC | RC | NC | NC |
MCT | NC | RB | PB | PB | NC | RB | PB | RB | PB | PB |
EXT | NC | FB | FB | FB | 適用外 | FB | FB | FB | FB | FB |
以下に、表
35 でアクション コードの組み合わせを解釈するための例を示します。
•EXT-RWL の組み合わせ。クライアント 1 は、排他トランザクション内からファイルのレコードを読み取ります。クライアント 2 は、トランザクションでない処理モードから、ノーウェイト ロック バイアスを指定してファイルからレコードを読み取ろうとすると、ステータス コード 85(FB、ファイル レベルのブロック)を受け取ります。クライアント 2 がウェイト ロック バイアスを指定した場合、MicroKernel は操作を再試行します。
•ICT-ICT の組み合わせ。クライアント 1 は、並行トランザクション内からレコードを挿入します。クライアント 2 が同じファイルにレコードを挿入しようとした場合、MicroKernel は操作を再試行します。これは、この挿入操作で変更されるページのうちの 1 つが、クライアント 1 で実行された挿入操作によって既に変更されているからです。クライアント 2 が 500 バイアスを指定した並行トランザクションを開始した場合、MicroKernel はステータス コード 84 を返します(この表については、
前提条件を参照してください)。
•ICT-ITDP の組み合わせ。この組み合わせは、クライアント 1 によって変更されたページをクライアント 2 が変更しないという点を除いては、ICT-ICT に似ています。この場合、クライアント 2 で試行された操作は正常終了します(NC、ブロックなし、競合なし)。
•MCT-MTDR の組み合わせ。クライアント 1 とクライアント 2 は異なるレコードを変更しますが、クライアント 2 はページ ロックでブロックされます。このブロックが発生するのは、変更されるレコードがファイル内のデータ ページ、インデックス ページまたは可変ページを共有しているからです(この表については、
前提条件を参照してください)。
受動的並行性(パッシブ コンカレンシー)
アプリケーションが、トランザクション モード外または並行トランザクション内から単一レコードの読み取り操作および更新操作を実行する場合は、受動的並行性に依存して更新の競合を解決することができます。受動的並行性は MicroKernel によって自動的に適用されるものであり、アプリケーションまたはユーザーからの明示的な指示を必要としません。
受動的並行性の状態にある場合、MicroKernel はクライアントが操作にロック バイアスを適用しなくてもレコードを読み取れるようにします。最初のクライアントがレコードを読み取ってから、そのレコードの更新または削除を試みるまでの間に、2 番目のクライアントがそのレコードを変更した場合、MicroKernel はステータス コード 80 を返します。この場合、最初のクライアントが行う変更は、レコードの古いイメージに基づいています。そのため、最初のクライアントは更新または削除操作を実行する前に、レコードを再度読み取る必要があります。
受動的並行性により、開発者はわずかな変更だけでシングルユーザー環境からマルチユーザー環境へアプリケーションを直接移動できます。
表
36 と表
37 は、受動的並行性を使用している場合に 2 つのクライアントがどのように相互作用するかを示しています。トランザクションでない場合と並行トランザクションからの場合を示します。
表 36 受動的並行性(トランザクションでない処理の例)
クライアント 1 | クライアント 2 |
1. ファイルを開きます。 | |
| 2. ファイルを開きます。 |
3. レコード A を読み取ります。 | |
| 4. レコード A を読み取ります。 |
5. レコード A を更新します。 | |
| 6. レコード A を更新します。MicroKernel は、ステータス コード 80 を返します。 |
| 7. レコード A を再度読み取ります。 |
| 8. レコード A を更新します。 |
表 37 受動的並行性(並行トランザクションの例)
クライアント 1 | クライアント 2 |
1. 並行トランザクションを開始します。 | |
| 2. 並行トランザクションを開始します。 |
3. レコード A を読み取ります。 | |
4. レコード A を更新します。 | |
| 5. レコード A を読み取ります。 |
6. トランザクションを終了します。 | |
| 7. レコード A を更新します。MicroKernel は、競合ステータス コードを返します。 |
| 8. レコード A を再度読み取ります。 |
| 9. レコード A を更新します。 |
| 10. トランザクションを終了します。 |
メモ: クライアント 1 がレコード A の更新操作を既に実行した後でクライアント 2 がレコード A を読み取ったとしても、MicroKernel は手順 7 で競合エラーを正しく検出します。この競合が存在するのは、クライアント 1 が手順 6 でトランザクションを終了するまでレコード A に対して行った変更をコミットしないからです。クライアント 2 が手順 7 で更新を実行するときまでに、手順 5 で読み取ったレコード A のイメージは古くなってしまいます。
レコードのロック
多くの場合、クライアントは受動的並行性で実現されるものより強力な並行性制御を必要とします。そのため、MicroKernel では、クライアントが競合エラーを受け取らずにレコードの更新または削除を行えるようにすることができます。このエラーはステータス コード 80で、このアプリケーションがレコードを読み取った後に別のクライアントがそのレコードを変更したことを示します。これを実現するには、クライアントはロックを要求してレコードを読み取る必要があります。MicroKernel がロックを許可した場合、ロックをかけたクライアントがロックを解除するまで、ほかのクライアントはレコードのロック、更新、削除は行えません。
したがって、操作が一時的にブロックされるためにクライアントが待機しなければならない場合があっても、レコードを更新または削除する機能は保証されます(たとえば、クライアントのレコードと同じデータ ページ上の別のレコードが、まだ実行されている並行トランザクション内の別のアプリケーションで変更される場合に、一時的なブロックが発生する可能性があります)。
クライアントはさまざまな種類のレコード ロックを明示的に要求できます。詳細については、
ロックを参照してください。
ユーザー トランザクション
トランザクションによって、データが消失する可能性が低くなります。ファイルに加える変更が多く、また、これらの変更が確実にすべて行われるか、まったく行われないようにする必要がある場合は、1 つのトランザクションにこれらの変更用の操作を取り込みます。明示的なトランザクションを定義すれば、MicroKernel に複数の Btrieve オペレーションを 1 つのアトミック単位として処理させることができます。トランザクション内にオペレーション群を取り込むには、Begin Transaction オペレーション(19)と End Transaction オペレーション(20)でこれらのオペレーションを囲みます。
MicroKernel には、排他トランザクションと並行トランザクションの 2 種類のトランザクションがあります。どのタイプを使用するかは、変更するファイルに対するほかのクライアントからのアクセスをどのくらい厳しく制限するかにより決まります(MicroKernel では、ほかのアプリケーションやクライアントはトランザクションが終了するまで、どのような排他トランザクションまたは並行トランザクションにかかわる変更でも見ることはできません)。
タスクが排他トランザクション内のファイルで動作する場合、MicroKernel はトランザクションの期間中にファイル全体をロックします。ファイルが排他トランザクションでロックされると、ほかの非トランザクション クライアントはファイルを読み取れますが、変更することはできません。排他トランザクション内にいる別のクライアントも、最初のクライアントがトランザクションを終了してファイルのロックを解除するまで、ファイルのポジション ブロックを必要とするオペレーション、たとえば、標準の Get オペレーションや Step オペレーションを実行できません。
アプリケーションが並行トランザクション内でファイルに操作をした場合、MicroKernel は以下のように、ファイル内で影響を受けるレコードとページのみをロックします。
•Get オペレーションまたは Step オペレーションが明確的に読み取りロック バイアスを指定して呼び出された場合、あるいは Begin Transaction オペレーションから読み取りロック バイアスを継承した場合、MicroKernel は Get オペレーションまたは Step オペレーションにおける 1 つまたは複数のレコードをロックします(ロックとロック バイアスについては、
ロックを参照してください)。
•MicroKernel は、Insert、Update または Delete オペレーションで変更されるデータ ページ上のレコードをロックします。また、レコードが可変長レコードである場合、MicroKernel はそのレコードの各部分を含むすべての可変ページをロックします。最後に、MicroKernel は、Insert、Update または Delete オペレーションの結果として変更されるインデックス ページのエントリをロックします。インデックス ページの一部分の変更によって、エントリが別のページに移動することがあります。インデックス ページが分割または結合される場合が、その例です。これらの変更では、トランザクションが完了するまでインデックス ページの完全なページ ロックが保持されます。
排他トランザクションの場合と同様に、ほかのタスクは常に同時トランザクション内からロックされるデータを読み取ることができます。どのようなデータ ファイルでも、複数のタスクがそれぞれの並行トランザクションを操作して、トランザクション内で Insert、Update または Delete オペレーションを実行したり、読み取りロック バイアスを含む Get オペレーションまたは Step オペレーションを実行できます。これらの動作の唯一の制限は、2 つのタスクが個々の並行トランザクションから同じレコードまたはページを同時にロックできないということです。
並行トランザクションには、以下の追加機能が適用されます。
•ロックされたページは、トランザクションの期間中ロックされたままになります。
•トランザクションで単にレコードを読み取るだけの場合、MicroKernel はレコードも対応するページもロックしません。
•ほかのクライアントは、並行トランザクション内でファイルに加えられた変更について、トランザクションが終了するまで認識できません。
ロック
レコード、ページまたはファイル全体でもロックできます。いったんロックされたら、そのロックに関与するクライアント以外は誰もレコード、ページ、またはファイルを変更できません。同様に、あるクライアントが所有するロックは、以降で説明するように、別のクライアントによるレコード、ページまたはファイルのロックを防ぐことができます。
MicroKernel には、明示的ロックと暗黙ロックの 2 種類のロックがあります。クライアントが Btrieve オペレーション コードにロック要求を含めることによって明確にロックを要求する場合、そのロックを
明示的ロックと呼びます。しかし、たとえクライアントが明示的にロックを要求しない場合でも、MicroKernel はクライアントが実行した動作の結果として影響を受けたレコードまたはページをロックできます。この場合、MicroKernel が行うロックを
暗黙ロックと呼びます。(
暗黙レコード ロックと
暗黙ロックを参照してください。)
メモ: 特に注記がない限り、レコード ロックは明示的レコード ロックを意味します。
レコードは、暗黙または明示的にロックできます。ページは暗黙にしかロックできません。ファイルは明示的にしかロックできません。
以降では、トランザクションでない処理環境とトランザクション環境の両方で適用される各種ロックについて説明します。
トランザクションでない処理環境における明示的レコード ロック
ここでは、トランザクションでない処理環境における明示的レコード ロックについて説明します。トランザクションがレコード ロックの使用にどのような影響を与えるかについては、
並行トランザクションのレコード ロックを参照してください。
クライアントは、受動的並行性に依存しない方がよい場合があります。しかし、その同じクライアントが、クライアントにレコードの再読み取りを要求するステータス コード 80 を受け取らずに、読み取ったレコードを後で更新または削除できるようにしなければらない場合があります。クライアントはレコードに対する明示的レコード ロックを要求することによって、これらの要件を満たすことができます。アプリケーションがレコードの読み取り時にレコードをロックする場合、以下のバイアス値のうちの 1 つを対応する Btrieve Get または Step オペレーション コードに追加できます。
•100-単一ウェイト レコード ロック
•200-単一ノーウェイト レコード ロック
•300 - 複数ウェイト レコード ロック
•400-複数ノーウェイト レコード ロック
これらのロック バイアスは、Get オペレーションと Step オペレーションにのみ適用できます。トランザクションでない処理環境では、ほかのどのオペレーションにもロック バイアスを指定できません。
メモ: 単一レコード ロックと複数レコード ロックには互換性がありません。したがって、クライアントは、ファイル内の同じポジション ブロックまたはカーソルに同時には両方のタイプのロックをかけることはできません。
単一レコード ロック
単一レコード ロックでは、クライアントは一度に 1 つのレコードしかロックできません。クライアントが単一レコード ロックを使って正常にレコードをロックしている場合、クライアントが以下のイベントのいずれかを完了するまでそのロックは有効です。
•ロックされたレコードを更新または削除する。
•単一レコード ロックを使用してファイル内の別のレコードをロックする。
•Unlock オペレーション(27)を使用して明示的にレコードをロック解除する。
•ファイルを閉じる。
•Reset オペレーション(28)を発行して、開いているすべてのファイルを閉じる。
•排他トランザクション中にファイル ロックを行う。
1 人のクライアントがレコードをロックすると、ほかのクライアントはそのレコードに対して Update(3)または Delete(4)オペレーションを実行できません。ただし、Get または Step オペレーションが以下の条件に従ってさえいれば、ほかのクライアントはこれらのオペレーションを使用してレコードを読み取ることができます。
•明示的ロック バイアスが含まれていない。
•レコードを読み取るとそのレコードがロックされるようなトランザクションから実行されない。これは、たとえば MicroKernel がファイル全体をロックする排他トランザクションや、ロック バイアスで開始された並行トランザクションなどです。詳細については、
並行トランザクションのレコード ロックと
ファイル ロックを参照してください。
複数レコード ロック
複数レコード ロックを使用すると、クライアントは同じファイル内でいくつかのレコードを並行してロックできます。クライアントが複数レコード ロックを使って 1 つまたは複数のレコードを正常にロックしている場合、クライアントが以下のイベントのうち 1 つ以上を完了するまでそれらのロックは有効です。
•ロックされたレコードを削除する。
•Unlock オペレーション(27)を使用して明示的にレコード ロックを解除する。
•ファイルを閉じる。
•Reset オペレーション(28)を発行して、開いているすべてのファイルを閉じる。
•排他トランザクション中にファイル ロックを行う。
メモ: Update オペレーションは、複数レコード ロックを解除しません。
単一レコード ロックの場合と同様に、クライアントが複数レコード ロックで 1 つまたは複数のレコードをロックすると、ほかのクライアントはこれらのレコードに対して Update(3)または Delete(4)オペレーションを実行できません。
ロックで説明しているように、ほかのクライアントは Get または Step オペレーションを使用して、これまでどおりロックされたレコードを読み取ることができます。
レコードが既にロックされている場合
別のクライアントによってレコードがロックされている、または、排他トランザクションによってファイル全体がロックされているために現在使用できないレコードに対し、クライアントがノーウェイト ロックを要求した場合、MicroKernel はステータス コード 84「レコードまたはページはロックされています」またはステータス コード 85「ファイルはロックされています」を返します。クライアントがウェイト ロックを要求し、そのレコードが現在使用できない場合、MicroKernel はオペレーションを再試行します。
並行トランザクションのレコード ロック
排他トランザクション(オペレーション 19)はファイル全体をロックするので、トランザクション内のレコード ロックは並行トランザクション(オペレーション 1019)にしか適用されません(トランザクションの種類の詳細については、
ユーザー トランザクションを参照してください)。
MicroKernel では、クライアントは並行トランザクション内からファイル内の単一レコードまたは複数レコードをロックすることができます。クライアントは、以下の方法のいずれかでレコードをロックできます。
•前にリストしたバイアス値のうちの 1 つを使用して、Get または Step オペレーションでレコード ロック バイアスを明示的に指定する(並行トランザクションのレコード ロック バイアス値は、非トランザクション レコード ロックのバイアス値と同じです)。
•Begin Concurrent Transaction オペレーション(1019)でレコード ロック バイアス値を指定する(この場合も、これらのバイアス値は、前にリストした非トランザクション レコード ロックのバイアス値と同じです)。
Begin Concurrent Transaction オペレーションでレコード ロック バイアス値を指定すると、そのトランザクション内の各オペレーションは、独自のバイアス値を持たない場合には、Begin Concurrent Transaction オペレーションからバイアス値を継承します。たとえば、Get Next オペレーション(06)が、先に実行されたバイアスをかけた Begin Concurrent Transaction オペレーション(1219)から 200 バイアスを継承するとしたら、その Get Next はノーウェイト ロックの読み取り操作(206)として実行されます。
前に示したように、クライアントは並列トランザクション内で発生する個々の Step または Get オペレーションにバイアス値を追加することができます。この方法で追加されたバイアスは、継承されたバイアスより優先します。
並行トランザクションで単一レコード ロックと複数レコード ロックを解除するイベントは、トランザクションでない処理環境のイベントに似ています。単一レコード ロックについては、
単一レコード ロックを参照してください。複数レコード ロックについては、次の例外を除き、
複数レコード ロックを参照してください。
•Close オペレーションは、並行トランザクション内から設定された明示的レコード ロックを解除しない。MicroKernel のバージョン 7.0 では、たとえレコードがロックされていてもトランザクション内でファイルを閉じることができます。
•End Transaction または Abort Transaction オペレーションは、トランザクション内から得られたすべてのレコード ロックを解除する。
最後に、並行トランザクション内のクライアントがバイアスのかかっていない Get または Step オペレーションを使用して 1 つまたは複数のレコードを読み取る場合、Begin Concurrent Transaction オペレーションでロック バイアスが指定されていなければ、MicroKernel はロックを行いません。
暗黙レコード ロック
クライアントがトランザクションの
外部または
並行トランザクション内からレコードの更新または削除を行おうとすると、MicroKernel はクライアントの代わりにそのレコードを
暗黙にロックしようとします。排他トランザクションで、暗黙のレコード ロックが不要なのは、MicroKernel が Update または Delete オペレーションを実行する前にファイル全体をロックするからです(
ファイル ロックを参照してください)。
MicroKernel は、ほかのクライアントが以下の操作を行わない限り、クライアントに対して暗黙レコード ロックを与えることができます。
•レコードに明示的ロックをかける。
•レコードに暗黙ロックをかける。
•レコードを含んでいるファイルをロックする。
メモ: MicroKernel では、単一のクライアントが同じレコードに対して明示的ロックと暗黙ロックの両方をかけることができます。
MicroKernel は、オペレーションの実行中、ファイルの整合性を確保するために必要な暗黙レコード ロックおよびその他すべてのロックを正常に取得できる場合のみ、指定された Update または Delete オペレーションを実行します(
暗黙ロックを参照してください)。
オペレーションがトランザクションでない処理環境にある場合、MicroKernel は Update または Delete オペレーションの終了時に暗黙レコード ロックを解除します。オペレーションが並行トランザクションにある場合、MicroKernel はロックを維持します。その場合、クライアントがトランザクションを終了または中止するか、クライアントがリセットされる(これは Abort Transaction オペレーションを意味する)まで、ロックは有効です。暗黙レコード ロックを解除するために使用できる明示的な Unlock オペレーションはありません。
トランザクション中、暗黙ロックを維持することで、MicroKernel は別のクライアントが生成した新しいコミットされていないイメージがレコードに含まれている場合に、クライアントがロック バイアス値を指定した Get または Step オペレーションを介してそのレコードを明示的にロックする結果生じる競合を防ぐことができます。
MicroKernel が暗黙ロックを維持しなかった場合にどうなるかを考えてみましょう。クライアント 1 は並行トランザクション内からレコード A で更新を行うことによって、レコードのイメージを変更します。しかし、クライアント 1 は並行トランザクションを終了していないので、新しいイメージをコミットしていません。クライアント 2 は、レコード A を読み取ってロックしようとします。
暗黙ロックが維持されていなかったら、レコード A に対するクライアント 1 の暗黙レコード ロックはなくなっているため、クライアント 2 はレコードを正常に読み取ってロックできてしまいます。しかし、クライアント 1 が新しいイメージをコミットしていないので、クライアント 2 はレコード A の
古いイメージを読み取ります。クライアント 1 がレコード A の変更されたイメージをコミットするオペレーションを終了し、クライアント 2 がレコード A を更新しようとすると、そのレコードのクライアント 2 のイメージは無効となるので、MicroKernel はステータス コード 80 「MicroKernel でレコード レベルの矛盾が発生しました」 を返します(表
38 の例を参照してください)。
クライアントがレコードを明示的または暗黙にロックしたか、そのレコードを含むファイル全体をロックした場合を考えてみましょう。別のクライアントが並行トランザクション内から問題のレコードの更新または削除を行おうとした場合、つまり、レコードを暗黙にロックしようとした場合、MicroKernel の実装のいくつかは待機し、ロックをかけてオペレーションをブロックしているクライアントがそのロックを解除するまで引き続きオペレーションを再試行します(どのバージョンの MicroKernel も、非トランザクションの更新または削除に対する再試行作業は試みません)。
Begin Concurrent Transaction オペレーションでバイアス値 500 を指定する(1519)と、MicroKernel はトランザクション内で Insert、Update および Delete オペレーションを再試行しなくなります。
ローカル クライアントの場合、MicroKernel はデッドロック検出を行います。ただし、バイアス 500 は再試行を抑止するので、MicroKernel はデッドロック検出を行う必要はありません。
Begin Transaction オペレーションでは、このバイアス値 500 をレコード ロックのバイアス値と組み合わせることができます。たとえば、1019 + 500 + 200(1719)を使用すると、Insert、Update および Delete オペレーションの再試行が抑止されると同時に、単一レコードの読み取りノーウェイト ロックが指定されます。
以下の例は、暗黙ロックの有効性を示したものです。この例では、暗黙ロックが存在しないと一時的に仮定しています。
表 38 暗黙ロックのない例
クライアント 1 | クライアント 2 |
1. 並行トランザクションを開始します。 | |
2. レコード A を読み取ります。 | |
3. レコード A を更新します(関連するページをロックする、ただし、レコードに対する暗黙ロックなし)。 | |
| 4. 単一レコード ロック(レコードの明示的ロック)を指定してレコード A を読み取ります。 |
5. トランザクションを終了します(ページ ロックを解除する)。 | |
| 6. レコード A を更新します(競合、ステータス コード 80)。 |
| 7. ロックを指定してレコード A を再度読み取ります。 |
| 8. レコード A を更新します。 |
MicroKernel が手順 3 で暗黙レコード ロックを適用しないと仮定した場合、クライアント 2 は手順 4 でレコード A を正常に読み取り、ロックできるにもかかわらず、手順 6 でそのレコードを更新できません。これは、手順 4 でクライアント 2 がレコード A の有効なイメージを読み取っても、手順 6 に達するまでにそのイメージが有効でなくなってしまうからです。手順 5 で、クライアント 1 がレコード A の新しいイメージをコミットすることによって、手順 4 でクライアント 2 が読み取ったレコードのイメージを無効にします。
しかし、実際は、MicroKernel が手順 3 でレコード A を暗黙にロックします。つまり、MicroKernel は手順 4 でステータス コード 84 を返し、クライアント 1 が手順 5 を実行するまで読み取り操作を再試行するようにクライアント 2 に要求します。
前の例で手順 3 と 4 を逆にした場合にどうなるかを考えてみましょう。クライアント 2 は、レコード A に暗黙ロックをかけます。クライアント 1 は待機させられ、クライアント 2 が自身で読み取ったレコード A の更新を終えて、そのレコードに対する明示的ロックを解除するまで、Update オペレーションを再試行します。クライアント 1 が次にレコード A の更新を再試行すると、MicroKernel はステータス コード 80 を返します。このステータスは、クライアント 1 のレコード A のイメージが有効ではなくなったこと、つまり、クライアント 2 がレコード A を変更する前にクライアント 1 がそのレコードを読み取っていたことを示します。
暗黙ロック
複数のクライアントがファイルを同時に変更できるという大きな自由度があるのは、同じ MicroKernel でキャッシュを共有するからです。非トランザクションの変更(Insert、Update または Delete オペレーション)は、ほかの非トランザクションの変更または別のクライアントによる並行トランザクションでの変更をブロックしません。並行トランザクション内で保留になっている変更は、その変更が同一レコードに影響を与えない限り、ほかの非トランザクションの変更も並行トランザクションにおける変更もブロックしません。
トランザクションの外側または並行トランザクションの中から Insert、Update、または Delete オペレーションが発生する場合は、MicroKernel がクライアントの代わりに、これらの操作中に変更される
レコードを暗黙にロックしようとします(排他トランザクションでは、MicroKernel は Update や Delete オペレーションを実行する前にファイル全体をロックするため、暗黙のレコード ロックおよびページ ロックは必要ありません。Insert オペレーションの場合は、クライアントがまだファイルをロックしていなければ MicroKernel がファイル ロックを要求します。
ファイル ロックを参照してください)。暗黙レコード ロックと同様に、暗黙ページ ロックは MicroKernel エンジンが行うため、クライアントは明示的に要求しません。
変更または挿入が行われるデータ ページ上のレコードは常にロックされます。しかし、単一のオペレーションがいくつかのほかのレコードもロックしなければならない場合があります。たとえば、レコードに加えた変更が 1 つ以上のレコード キーに影響を与える場合、MicroKernel は影響を受けたキー値を含んでいるインデックス ページのレコードをロックする必要があります。また、MicroKernel は、オペレーション中に B ツリーのバランスを取る作用によって変更されるすべてのインデックス ページをロックする必要があります。変更がレコードの可変長部分に影響を与える場合、MicroKernel は可変ページ全体もロックする必要があります。
そのようなオペレーションがトランザクションでない処理環境で実行される場合、MicroKernel はオペレーションの終了時に暗黙レコード ロックを解除します。オペレーションが並行トランザクション内から行われる場合、MicroKernel はロックを保持します。その場合、クライアントがトランザクションを終了または中止するまで、あるいは、クライアントがリセットされる(これは Abort Transaction オペレーションを意味する)まで、ロックは有効です。明示的 Unlock オペレーションを使用して、暗黙レコード ロックや暗黙ページ ロックを解除することはできません。
並行トランザクションで発行された Insert、Update、または Delete オペレーションでレコードまたはページの変更が必要になった場合(暗黙ページ ロックを必要とします)に、そのレコードまたはページが別の並行トランザクションによって現在ロックされているか、ファイル全体が排他トランザクションによってロックされている場合、MicroKernel は待機し、ロックをかけてオペレーションをブロックしているクライアントがそのロックを解除するまで引き続きオペレーションを再試行します。MicroKernel は、非トランザクションの更新や削除に対する再試行を試みません。
暗黙レコード ロックを取得できない場合は、クライアントは Begin Concurrent Transaction オペレーションでバイアス値 500 を使用することによって、オペレーションの再試行を抑止することができます。
暗黙ページ ロックと明示的レコード ロックまたは暗黙レコード ロックは、互いにブロックに影響を与えません。クライアントは、操作対象のレコードが入っているページを別のクライアントが暗黙にロックしていたとしても、そのページ上のレコードを読み取り、ロックすることができます。ただし、これは
暗黙レコード ロックで説明しているように、ロックするレコードが更新されたレコードと同じでない場合に限ります。逆に、クライアントはレコードを更新または削除した場合、それによって影響を受けるレコードが入っているデータ ページの中に、別のクライアントによってロックされたレコードが既に含まれていても、そのデータ ページを暗黙にロックすることができます。
ファイル ロック
クライアントは排他トランザクション内で初めてファイルにアクセスするとき、ファイル ロックの取得を試みます。
メモ: 前の文章が示すように、MicroKernel はクライアントが Begin Transaction オペレーションを実行する際にはファイルをロックしません。ロックが発生するのは、Begin Transaction オペレーションの実行後、クライアントがレコードを読み取ったり変更したりするときだけです。
ファイル ロックは、その名前が示すように、ファイル全体をロックします。クライアントのファイル ロックは、そのクライアントがトランザクションを終了または中止するか、そのクライアントがリセットされる(これは Abort Transaction オペレーションを意味する)まで有効です。
クライアントが排他トランザクションでファイルをロックしようとしたとき、既に別のトランザクションがそのファイルにロック(レコード、ページ、または ファイル ロック)をかけていた場合には、MicroKernel は待機し、ロックをかけてオペレーションをブロックしているクライアントがそのロックを解除するまで引き続きオペレーションを再試行します。また、ローカル クライアントがオペレーションをブロックし、MicroKernel がデッドロックの状況を検出すると、MicroKernel はステータス コード 78「MicroKernel でデッドロックを検出しました。」を返します。
ファイル ロックを取得できない場合は、クライアントは Begin Exclusive Transaction オペレーションでノーウェイト ロック バイアス値 200 または 400(219 または 419)を指定することによって、オペレーションの再試行を抑止することができます。このような方法でクライアントがトランザクションを開始したとき、MicroKernel はファイル ロックを与えられない場合には、ステータス コード 84 または 85 を返します。
バイアス値 200 および 400 は、レコード ロックから派生したものです。しかし、レコード ロック環境における単一ロックおよび複数ロックという概念は、排他トランザクション環境では何も意味しません。事実上、ファイルがロックされるときにファイル内のすべてのレコードがロックされます。排他トランザクション環境では、バイアスの「ノーウェイト」の意味だけが残されます。
MicroKernel は、ウェイト ロック バイアス(100 または 300)を Begin Exclusive Transaction オペレーションで受け付けます(それぞれオペレーション 119、319 になります)が、Begin Transaction オペレーションのデフォルト モードはウェイト モードであるため、このようなバイアス値の加算には意味がありません。
排他トランザクションでは、ファイルのどの部分にでも最初にアクセスが生じたら、MicroKernel はファイル全体をロックします。そのため、MicroKernel は以下の例外を除き、排他トランザクション内で実行される Get または Step オペレーションのオペレーション コードに明示的に追加されたレコード ロック バイアス値を無視します。
クライアントが Begin Transaction オペレーションをウェイト モード(オペレーション 19、119、または 319)で実行しているときに、そのトランザクションの最初の読み取り(Get または Step オペレーション)にバイアス 200 または 400(ノーウェイト ロック バイアス)をかけた場合、ノーウェイト バイアスが Begin Transaction オペレーションのウェイト モードよりも優先されます。そのため、クライアントがこのバイアスのかかった読み取り操作を実行してもファイルをロックできない場合、たとえば、別のクライアントがそのファイルのレコードを既にロックしている場合は、MicroKernel は待機せず(デフォルト)、デッドロックの有無を確認しません。これは、クライアントが読み取り操作の再試行を無制限に行うことを前提としているからです。これと同じ状況で、再試行を実行する MicroKernel のほかのバージョンも、ファイル ロックを再試行したりデッドロックの有無を確認しないという指示として、ノーウェイト バイアスを自動的に認識します。
メモ: 排他トランザクション内から実行される Get または Step オペレーションに対するバイアス値 200 と 400 は、待機しないという意味しかありません。つまり、並行トランザクション内からの場合と同様に、それらの値は明示的なレコード ロックを要求しません。
ファイル ロックは、レコード ロックとページ ロックのどちらとも両立しません。したがって、別のクライアントがファイルにレコード ロックまたはページ ロックをかけている場合、MicroKernel はクライアントにそのファイルのロックを与えません。逆に、別のクライアントが既にファイルにロックをかけている場合、MicroKernel はクライアントにレコード ロックまたはページ ロックを与えません。
複数並行制御ツールの例
以下の例は、さまざまな並行制御機構の使用例を示したものです。
例 1
例 1 は、明示的レコード ロック、暗黙レコード ロック、明示的ページ ロック、受動的並行性の相互関係を示したものです。この例で操作される 2 つのレコード、レコード A とレコード B は、同じデータ ページに存在し、ファイルにはキーが 1 つだけあるものとします。各手順の詳細説明については、例の後を参照してください。
次の表は、暗黙レコード ロック、明示的レコード ロック、暗黙ページ ロック、受動的並行性の相互関係を示したものです。
表 39 レコード ロック、ページ ロック、並行性の相互関係
クライアント 1 | クライアント 2 | クライアント 3(非トランザクション) |
1. 複数ノーウェイト ロック バイアスを指定して並行トランザクションを開始する(1419) | | |
| 2. 単一ウェイト ロック バイアスを指定して並行トランザクションを開始する(1119) | |
3. 単一ノーウェイト ロック バイアスを指定した Get Equal を使用してレコード A を読み取る(205) | | |
| 4. Get Equal(5)を使用してレコード B を読み取る(単一ウェイト ロック バイアスを継承) | |
| | 5. Get Equal(5)を使用してレコード B を読み取る |
| | 6. レコード B の削除(4)を試みる:MicroKernel がステータス コード 84 を返すため、クライアント 3 は再試行しなければならない |
| 7. レコード B を更新する(3) | |
8. レコード A の更新(3)を試みる:MicroKernel が再試行しなければならない | | |
| 9. トランザクションを終了する(20) | |
10. レコード A の更新(3)を再試行する:正常終了 | | |
| | 11. レコード B の削除(4)を再試行する:MicroKernel がステータス コード 80 を返すため、クライアント 3 はレコード B を再度読み取らなければならない |
| | 12. Get Equal(5)を使用してレコード B を再度読み取る |
| | 13. レコード B の削除(4)を再試行する:MicroKernel はステータス コード 84 を返す |
14. トランザクションを終了する | | |
| | 15. レコード B の削除(4)を再試行する:正常終了 |
手順 1 で、クライアント 1 の Begin Concurrent Transaction オペレーションには一般的なバイアス値 400(複数レコード ノーウェイト ロック)を指定します。このバイアスは、このトランザクション内にあるバイアスのかかっていない各 Get または Step オペレーションに継承されます。この時点では、MicroKernel はファイル、ファイルのページまたはレコードにロックを適用していません。
手順 2 で、クライアント 2 の Begin Transaction オペレーションには一般的なバイアス値 100(単一レコード ウェイト ロック)を指定します。このバイアスは、このトランザクション内にあるバイアスのかかっていない各 Get または Step オペレーションに継承されます。MicroKernel はまだ、ファイル、ファイルのページまたはレコードにロックを適用していません。
手順 3 で、クライアント 1 の Get Equal オペレーションはバイアス値 200(単一レコード ノーウェイト ロック)を指定しています。MicroKernel は、継承された 400(複数ノーウェイト レコード ロック)ではなくこのバイアス値を受け入れます。これは、継承されたバイアス値よりも、個々のオペレーションに指定されたバイアス値が優先されるからです。
手順 4 で、クライアント 2 の Get Equal オペレーション(5)は独自のバイアス値を指定していません。したがって、このオペレーションはクライアント 2 の Begin Concurrent Transaction オペレーション(1119)から単一ウェイト ロック バイアス値 100 を継承します。たとえレコード A とレコード B が同じページ上にあっても、双方のロック要求(手順 3 と手順 4)は成功します。なぜなら、レコード ロック要求は指定されたレコードだけをロックするものであり、そのレコードが位置するデータ ページも、関連するどのインデックス ページもロックしないからです。
手順 5 で、クライアント 3 のロックを要求しない非トランザクションの Get Equal オペレーション(5)は成功します。これは、要求したレコードが存在する限り、非トランザクションの読み取りは常に成功するからです。
手順 6 で、クライアント 3 はレコード B の削除(4)を行おうとしますが、クライアント 2 がそのレコードに明示的ロックをかけているので、レコード B に対してレコードの削除に必要な暗黙レコード ロックを取得できません。その結果、MicroKernel はクライアント 3 にステータス コード 84(レコードまたはページがロックされている)を返します。そうなると、クライアント 3 は制御を放棄して、必要であれば後で Delete オペレーションを再試行する必要があります。
手順 7 で、クライアント 2 はまずレコード B に対する暗黙レコード ロックの取得に成功します。レコード B は、手順 4 で手順 2 から単一ウェイト ロック バイアスを継承するため、既にクライアント 2 で明示的にロックされていますが、明示的ロックと暗黙ロックの両方が同じクライアントに属しているので、問題はありません。それと同時に、クライアント 2 は、レコード B を含んでいるデータ ページとレコード B のキー値を含んでいるインデックス ページに対するページ ロックの取得にも成功します。
メモ: クライアント 2 によってロックされたデータ ページには、クライアント 1 で明示的にロックされたレコード A が含まれていますが、
暗黙ロックで説明しているように、レコード ロックはページ ロックをブロックしません。
MicroKernel は手順 7 で Update オペレーション(3)そのものを実行するときに、変更されたデータ ページとインデックス ページのコミットされていない新しいイメージをシャドウ ページとしてファイルに書き込みます。この時点で、MicroKernel はレコード B に対するクライアント 2 の明示的ロックを解除しますが、クライアント 2 は今し方取得した暗黙ページ ロックに加えて、レコード B の暗黙レコード ロックも保持します。クライアント 2 が手順 7 で Update オペレーション(3)を終了した後でも、クライアント 3 がまだレコード B に対する暗黙レコード ロックを取得できないのは、クライアント 2 がレコードに対する暗黙レコード ロックを今も保持しているからです。クライアント 3 は、その再試行を続ける必要があります。
クライアントがリモートである場合、クライアント 2 は実際にファイルを更新する前に、クライアント 2 のローカル クライアント間の並行制御に必要なページ ロックを設定するほか、ファイルを保留中の変更の状態とします。
手順 8 で、クライアントはまずレコード A に対する暗黙レコード ロックをうまく取得します。たとえレコード A のデータ ページが既にクライアント 2 でロックされていても、ページ ロックはレコード ロックをブロックしないので、ロックの競合はありません(
暗黙ロックを参照してください)。次に、クライアント 1 はレコード A を含んでいるデータ ページの暗黙ページ ロックを取得しようとします。この試行は、データ ページが手順 7 でクライアント 2 によって既にロックされているため失敗します。Begin Concurrent Transaction オペレーション(1419)は 500 バイアスを指定していないため、MicroKernel はオペレーションを再試行します(クライアントがローカルである場合、MicroKernel はデッドロック検出も行います)。
もしもクライアント 1 が 500 バイアスを追加して Begin Transaction オペレーション(1919)を発行していたら、MicroKernel は直ちにユーザーに制御を返したでしょう。
クライアントがリモートである場合、クライアント 1 は手順 7 でクライアント 2 によって設定された保留中の変更の状態に直面するため、MicroKernel はオペレーションを再試行します。
手順 9 で、そのトランザクションを終了することによって、クライアント 2 はレコード B に対する暗黙レコード ロックを解除し、手順 5 でロックしたデータ インデックス ページに対する暗黙ページ ロックを解除します。この時点で、MicroKernel はクライアント 2 がトランザクションで作成した新しいページ イメージをすべてコミットします。これで、これらのイメージがファイルの有効な部分になります。
クライアントがリモートである場合、クライアント 2 はファイルに対する保留中の変更の状態をクリアすると共に、ロックを解除します。
手順 10 で、クライアント 1 が続行している更新の再試行がようやく成功するのは、クライアント 2 がレコード A のデータ ページとインデックス ページをロックしなくなったからです。
手順 11 で、クライアント 2 が手順 9 でトランザクションを終了し、それによってすべてのロックを解除したにもかかわらず、クライアント 3 はまだレコード B を削除できません。現状では、クライアント 3 がレコードを削除しようとすると、MicroKernel の受動的並行制御はステータス コード 80(競合)を返します。これは、クライアント 3 が手順 5 で最初にレコード B を読み取った後で、クライアント 2 がこのレコードを変更したからです。この時点で、クライアント 3 はレコードを再度読み取らないと、Delete オペレーションを再試行できません。
手順 12 で、クライアント 3 はレコード B を再度読み取ることにより、クライアント 2 が手順 7 でレコードに加えた変更を反映し、手順 9 でコミットされたイメージを取得します。
手順 13 で、クライアント 3 はレコード B を削除しようとしても再びうまくいかず、MicroKernel からステータス コード 84 が返されます。このステータス コードは、クライアント 1 がレコード A を更新するために、レコード B を含むデータ ページとインデックス ページの暗黙ページ ロックを持っているという事実を反映しています。最初に述べたように、同じデータ ページにレコード A とレコード B が含まれており、同じインデックス ページにこれらのレコードのキー値が含まれているものと仮定します。
手順 14 で、クライアント 1 はトランザクションを終了することによって、変更をコミットし、暗黙ページ ロックを解除します。
手順 15 で、クライアント 3 はようやくレコード B を削除することができます。
例 2
例 2 は、ファイル ロックと受動的並行制御の相互関係を示したものです。各手順の詳細説明については、例の後を参照してください。
表 40 ファイル ロックと受動的並行性の相互関係
クライアント 1 | クライアント 2 |
1. ファイル 1 を開く(0) | |
2. ファイル 2 を開く(0) | |
3. ファイル 3 を開く(0) | |
4. 単一レコード ロックを使用してファイル 3 のレコード E を取得する(105) | |
| 5. ファイル 1 を開く(0) |
6. 排他トランザクションを開始する(19) | |
7. ファイル 1 のレコード B を取得する(5) | |
| 8. ファイル 1 のレコード A を取得する(5) |
| 9. ファイル 1 のレコード A を更新する(3)(ステータス コード 85、再試行) |
10. ファイル 2 のレコード C を取得する(5) | |
11. ファイル 2 のレコード C を更新する(3) | |
12. ファイル 1 のレコード B を削除する(4) | |
13. トランザクションを終了する(20) | |
| 14. 手順 9 を再試行する(成功) |
手順 4 で、クライアント 1 はファイル 3 のレコード E に対する明示的レコード ロックを取得します。
手順 6 で、クライアント 1 は排他トランザクションを開始します。クライアント 1 が 3 つのファイルを開いていますが、MicroKernel はこれらのファイルをまだロックしていませんし、Begin Transaction オペレーションを実行した結果として、ファイル 3 のレコード E に対する明示的ロックを解除することもありません。
手順 7 で、クライアント 1 はファイルにアクセスした結果としてファイル 1 に対するファイル ロックを取得します(
ファイル ロックを参照してください)。もし、直前の手順(たとえば手順 5 と 6 の間)でクライアント 2 がロック バイアスを持つオペレーションを使用してファイル 1 からレコードを読み取っていたら、手順 7 は失敗します。
手順 8 で、クライアント 2 はファイル 1 からレコード A を正常に読み取ります。この読み取りが成功するのは、ロックを要求しないからです。ただし、Get Equal オペレーション(5)がロック バイアスを指定して発行されていたら、クライアント 1 がファイル 1 を現在ロックしているため、オペレーションは失敗します。
手順 9 では、クライアント 1 がファイルをロックしているため、クライアント 2 は暗黙レコード ロックを取得できません。したがって、MicroKernel はクライアント 2 にステータス コード 85(ファイルがロックされている)を返します。クライアント 2 は直ちに制御を放棄し、クライアント 1 が手順 13 でトランザクションを終了または中止するまで、手順 9 を再試行しなければなりません(
レコードが既にロックされている場合を参照してください)。
手順 10 で、クライアント 1 はファイル 2 に対するファイル ロックを取得します。
手順 13 で、クライアント 1 はファイル 1 と 2 に対するファイル ロックを解除します。
メモ: クライアント 1 は排他トランザクションでファイル 3 にアクセスしていないので、ファイル 3 をロックしませんでした。事実上、手順 13 以降でも、クライアント 1 はファイル 3 のレコード E に対する明示的レコード ロックを保持します。クライアント 1 は、ファイル 3 にアクセスしてトランザクションでファイル全体をロックしていた場合のみ、レコード E を解除したでしょう。
手順 14 で、ようやく手順 9 におけるクライアント 2 の Update オペレーションの再試行が成功します。
複数ポジション ブロックの並行制御
MicroKernel では、同じクライアントが同じファイル内で複数のポジション ブロック(カーソル)を使用できます。
並行トランザクションまたは排他トランザクションの内側では、複数ポジション ブロックは変更されたページの同じビューを共有します。複数ポジション ブロック セット内の各ポジション ブロックは、そのセットのほかのポジション ブロックによって行われた変更であれば、変更がコミットされる前であっても、直ちにわかります。
複数ポジション ブロックはすべてのロック、つまり、明示的レコード ロック、暗黙レコード ロック、暗黙ページ ロック、ファイル ロックを共有します。したがって、どのクライアントについても、あるポジション ブロックのロックによって別のポジション ブロックが同じファイル内で別のロックを取得できなくなることはありません。
クライアントがトランザクションを終了または中止すると、MicroKernel はそのクライアントの暗黙ロックとファイル ロックをすべて解除します。ただし、クライアントの明示的レコード ロックがトランザクション内から与えられたものであるかどうかにかかわらず、MicroKernel はファイル内の各ポジション ブロックが要求したときだけクライアントの明示的レコード ロックを解除します。
たとえば、キー値として -2 を持つ Unlock オペレーション(27)は、指定されたポジション ブロックに属する複数レコード ロックのみを解除します。Close オペレーション(1)は、その実行時に指定された同じポジション ブロックに対して取得されたロックのみを解除します。同様に、クライアントがトランザクション内でポジション ブロックの最初のレコード ロック、ページ ロック、またはファイル ロックを取得したとき、MicroKernel はそのポジション ブロックに対して取得していた明示的レコード ロックのみを解除します。
例 2 で、クライアント 1 がファイル 1、ファイル 2、ファイル 3 を開くのではなく、同じファイルを 3 回開いていた場合、また、クライアント 1 が最初の 2 つのポジション ブロックのみを使用してファイルにアクセスしていた場合、3 つ目のポジション ブロック用に取得した明示的ロックは、End Transaction オペレーション以降も変化しません。
複数ポジション ブロック
BTRV 関数を使用するアプリケーションで、同じファイルに 2 つのアクティブなポジション ブロックを持ち、両方のポジション ブロックから同じレコードに対して複数レコード ロックを指定した読み取りを発行した場合、どちらのポジション ブロックも成功ステータスを受け取ります。ただし、キー番号に -1 とデータ バッファーに物理位置を指定するか、キー番号に -2 を指定するかしてレコードのロックを解除する場合は、両方のポジション ブロックがロック解除の呼び出しを発行した場合にのみ、レコードのロックは解除されます。1 つのポジション ブロックだけがロック解除呼び出しを行った場合(どの呼び出しかは問題ではない)、別のユーザーはレコードをロックしようとすると、ステータス コード 84 を受け取ります。両方のポジション ブロックがロック解除を発行した後、第 2 のユーザーはレコードをロックできます。
この動作は単一レコード ロックの場合にも当てはまりますが、この場合のロック解除コマンドは、キー番号の特定も、データ バッファーへの物理位置の指定も必要としません。ただし、別のユーザーがレコードをロックするには、両方のポジション ブロックがロック解除を発行する必要があります。
各カーソル(つまり、各ポジション ブロック)はロックを取得します。MicroKernel では同じクライアントのカーソルが同じレコードをロックできますが、各カーソルがロック解除を発行しなければ、レコードのロックは完全に解除されません。
クライアント ID パラメーター
BTRV 関数でなく BTRVID 関数を使ってアプリケーションを開発する場合は、クライアント ID と呼ばれる追加パラメーターを指定する必要があります。これにより、アプリケーションは複数のクライアント ID を Btrieve に割り当てて、ほかのクライアントの状態に影響を与えることなく、1 人のクライアントのオペレーションを実行することが可能になります。
たとえば、2 つのアプリケーションが Windows で実行されており、そのアプリケーションそれぞれが 3 つの異なるクライアント ID を使用していると仮定します。この場合、アクティブ クライアント数は 6 個になります。これが同じアプリケーション(および各インスタンス内の同じクライアント ID の値)の 2 つのインスタンスであるか、2 種類のアプリケーションであるかは問題ではありません。Btrieve は、これら 6 つのクライアント ID をそれぞれ区別します。