PSQL Programmer's Guide (v11)

ユーザー トランザクション(トランザクショナル インターフェイス)

トランザクションによって、データが消失する可能性が低くなります。ファイルに加える変更が多く、また、これらの変更が確実にすべて行われるか、まったく行われないようにする必要がある場合は、1 つのトランザクションにこれらの変更用の操作を取り込みます。明示的なトランザクションを定義すれば、MicroKernel に複数の Btrieve オペレーションを 1 つのアトミック単位として処理させることができます。トランザクション内にオペレーション群を取り込むには、Begin Transaction オペレーション(19)と End Transaction オペレーション(20)でこれらのオペレーションを囲みます。

MicroKernel には、排他トランザクションと並行トランザクションの 2 種類のトランザクションがあります。どのタイプを使用するかは、変更するファイルに対するほかのクライアントからのアクセスをどのくらい厳しく制限するかにより決まります(MicroKernel では、ほかのアプリケーションやクライアントはトランザクションが終了するまで、どのような排他トランザクションまたは並行トランザクションにかかわる変更でも見ることはできません)。

タスクが排他トランザクション内のファイルで動作する場合、MicroKernel はトランザクションの期間中にファイル全体をロックします。ファイルが排他トランザクションでロックされると、ほかの非トランザクション クライアントはファイルを読み取れますが、変更することはできません。排他トランザクション内にいる別のクライアントも、最初のクライアントがトランザクションを終了してファイルのロックを解除するまで、ファイルのポジション ブロックを必要とするオペレーション、たとえば、標準の Get オペレーションや Step オペレーションを実行できません。

アプリケーションが並行トランザクション内でファイルに操作をした場合、MicroKernel は以下のように、ファイル内で影響を受けるレコードとページのみをロックします。

排他トランザクションの場合と同様に、ほかのタスクは常に同時トランザクション内からロックされるデータを読み取ることができます。どのようなデータ ファイルでも、複数のタスクがそれぞれの並行トランザクションを操作して、トランザクション内で Insert、Update または Delete オペレーションを実行したり、読み取りロック バイアスを含む Get オペレーションまたは Step オペレーションを実行できます。これらの動作の唯一の制限は、2 つのタスクが個々の並行トランザクションから同じレコードまたはページを同時にロックできないということです。

並行トランザクションには、以下の追加機能が適用されます。

ロック

レコード、ページまたはファイル全体でもロックできます。いったんロックされたら、そのロックに関与するクライアント以外は誰もレコード、ページ、またはファイルを変更できません。同様に、あるクライアントが所有するロックは、以降で説明するように、別のクライアントによるレコード、ページまたはファイルのロックを防ぐことができます。

MicroKernel には、明示的ロックと暗黙ロックの 2 種類のロックがあります。クライアントが Btrieve オペレーション コードにロック要求を含めることによって明確にロックを要求する場合、そのロックを明示的ロックと呼びます。しかし、たとえクライアントが明示的にロックを要求しない場合でも、MicroKernel はクライアントが実行した動作の結果として影響を受けたレコードまたはページをロックできます。この場合、MicroKernel が行うロックを暗黙ロックと呼びます。(暗黙レコード ロック暗黙ロックを参照してください。)


メモ

特に注記がない限り、レコード ロック明示的レコード ロックを意味します。


レコードは、暗黙または明示的にロックできます。ページは暗黙にしかロックできません。ファイルは明示的にしかロックできません。

以降では、トランザクションでない処理環境とトランザクション環境の両方で適用される各種ロックについて説明します。

トランザクションでない処理環境における明示的レコード ロック

ここでは、トランザクションでない処理環境における明示的レコード ロックについて説明します。トランザクションがレコード ロックの使用にどのような影響を与えるかについては、並行トランザクションのレコード ロックを参照してください。

クライアントは、受動的並行性に依存しない方がよい場合があります。しかし、その同じクライアントが、クライアントにレコードの再読み取りを要求するステータス コード 80 を受け取らずに、読み取ったレコードを後で更新または削除できるようにしなければらない場合があります。クライアントはレコードに対する明示的レコード ロックを要求することによって、これらの要件を満たすことができます。アプリケーションがレコードの読み取り時にレコードをロックする場合、以下のバイアス値のうちの 1 つを対応する Btrieve Get または Step オペレーション コードに追加できます。

これらのロック バイアスは、Get オペレーションと Step オペレーションにのみ適用できます。トランザクションでない処理環境では、ほかのどのオペレーションにもロック バイアスを指定できません。


メモ

単一レコード ロックと複数レコード ロックには互換性がありません。したがって、クライアントは、ファイル内の同じポジション ブロックまたはカーソルに同時には両方のタイプのロックをかけることはできません。


単一レコード ロック

単一レコード ロックでは、クライアントは一度に 1 つのレコードしかロックできません。クライアントが単一レコード ロックを使って正常にレコードをロックしている場合、クライアントが以下のイベントのいずれかを完了するまでそのロックは有効です。

1 人のクライアントがレコードをロックすると、ほかのクライアントはそのレコードに対して Update(3)または Delete(4)オペレーションを実行できません。ただし、Get または Step オペレーションが以下の条件に従ってさえいれば、ほかのクライアントはこれらのオペレーションを使用してレコードを読み取ることができます。

複数レコード ロック

複数レコード ロックを使用すると、クライアントは同じファイル内でいくつかのレコードを並行してロックできます。クライアントが複数レコード ロックを使って 1 つまたは複数のレコードを正常にロックしている場合、クライアントが以下のイベントのうち 1 つ以上を完了するまでそれらのロックは有効です。

単一レコード ロックの場合と同様に、クライアントが複数レコード ロックで 1 つまたは複数のレコードをロックすると、ほかのクライアントはこれらのレコードに対して Update(3)または Delete(4)オペレーションを実行できません。ロックで説明しているように、ほかのクライアントは Get または Step オペレーションを使用して、これまでどおりロックされたレコードを読み取ることができます。

レコードが既にロックされている場合

別のクライアントによってレコードがロックされている、または、排他トランザクションによってファイル全体がロックされているために現在使用できないレコードに対し、クライアントがノーウェイト ロックを要求した場合、MicroKernel はステータス コード 84「レコードまたはページはロックされています」またはステータス コード 85「ファイルはロックされています」を返します。クライアントがウェイト ロックを要求し、そのレコードが現在使用できない場合、MicroKernel はオペレーションを再試行します。

並行トランザクションのレコード ロック

排他トランザクション(オペレーション 19)はファイル全体をロックするので、トランザクション内のレコード ロックは並行トランザクション(オペレーション 1019)にしか適用されません(トランザクションの種類の詳細については、ユーザー トランザクション(トランザクショナル インターフェイス)を参照してください)。

MicroKernel では、クライアントは並行トランザクション内からファイル内の単一レコードまたは複数レコードをロックすることができます。クライアントは、以下の方法のいずれかでレコードをロックできます。

Begin Concurrent Transaction オペレーションでレコード ロック バイアス値を指定すると、そのトランザクション内の各オペレーションは、独自のバイアス値を持たない場合には、Begin Concurrent Transaction オペレーションからバイアス値を継承します。たとえば、Get Next オペレーション(06)が、先に実行されたバイアスをかけた Begin Concurrent Transaction オペレーション(1219)から 200 バイアスを継承するとしたら、その Get Next はノーウェイト ロックの読み取り操作(206)として実行されます。

前に示したように、クライアントは並列トランザクション内で発生する個々の Step または Get オペレーションにバイアス値を追加することができます。この方法で追加されたバイアスは、継承されたバイアスより優先します。

並行トランザクションで単一レコード ロックと複数レコード ロックを解除するイベントは、トランザクションでない処理環境のイベントに似ています。単一レコード ロックについては、単一レコード ロックを参照してください。複数レコード ロックについては、次の例外を除き、複数レコード ロックを参照してください。

最後に、並行トランザクション内のクライアントがバイアスのかかっていない Get または Step オペレーションを使用して 1 つまたは複数のレコードを読み取る場合、Begin Concurrent Transaction オペレーションでロック バイアスが指定されていなければ、MicroKernel はロックを行いません。

暗黙レコード ロック

クライアントがトランザクションの外部または並行トランザクション内からレコードの更新または削除を行おうとすると、MicroKernel はクライアントの代わりにそのレコードを暗黙にロックしようとします。排他トランザクションで、暗黙のレコード ロックが不要なのは、MicroKernel が Update または Delete オペレーションを実行する前にファイル全体をロックするからです(ファイル ロックを参照してください)。

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 がファイル ロックを要求します。ファイル ロックを参照してください)。暗黙レコード ロックと同様に、暗黙ページ ロックは MKDE が行うため、クライアントは明示的に要求しません。

変更または挿入が行われるデータ ページ上のレコードは常にロックされます。しかし、単一のオペレーションがいくつかのほかのレコードもロックしなければならない場合があります。たとえば、レコードに加えた変更が 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 はクライアントにレコード ロックまたはページ ロックを与えません。


レコードのロック (トランザクショナル インターフェイス)

複数並行制御ツールの例 (トランザクショナル インターフェイス)