トランザクション処理
この章では、以下の項目について説明します。
トランザクションとは、動作の論理ユニットです。トランザクション処理は、データベースの論理整合性の維持に役立ちます。
例えば銀行業務で、ある口座から別の口座に送金する場合、送金元のテーブル内のフィールドから差し引いた額を、送金先のテーブルのフィールドに追加する必要があります。両方の更新で 1 つのトランザクションとなることを指定すると、両方の処理が実行されるか、両方とも実行されないかのいずれかになります。つまり、一方の処理のみを実行できません。
アプリケーション内で、単一の SQL INSERT、UPDATE、DELETE 文、あるいは単一のグローバル SET や KILL は、それ自体で完全なトランザクションを構築しないことがあります。このような場合、トランザクション処理コマンドを使用して、完全なトランザクションを構成する一連の操作を定義します。1 つのコマンドでトランザクションの開始を示し、一連の多数のコマンドを置いた後に、他のコマンドでトランザクションの終了を示します。
通常、トランザクションは全体的に実行されます。プログラム・エラーやシステムの動作不良が原因で不完全なトランザクションになった場合、部分的に完了したトランザクションはロールバックされます。
アプリケーション開発者は、アプリケーション内でトランザクション・ロールバックを処理する必要があります。Caché は、リカバリ、HALT や ResJob の実行中など、システム障害時やさまざまな状況でトランザクション・ロールバックを自動的に処理します。
LogRollback 構成オプションが設定されている場合、Caché は、cconsole.log ファイルにロールバックを記録します。管理ポータル、[システム処理]、[システムログ] オプションを使用すれば、cconsole.log を表示できます: システム, システムログ, コンソールログの表示。
アプリケーションでのトランザクション管理
以下のいずれかを使用して、Caché はアプリケーションのトランザクションを定義します。
-
マクロ・ソース・ルーチンの SQL 文
-
ObjectScript コマンド
両方の手法が機能します。SQL INSERT、UPDATE、DELETE 文あるいは ObjectScript の SET コマンドと KILL コマンドのいずれかを使用して、トランザクションを構築するデータベースを変更するかどうかは関係ありません。
トランザクション・コマンド
Caché は、ANSI SQL 操作 COMMIT WORK と ROLLBACK WORK をサポートしています (Caché SQL では、キーワード WORK はオプションとなります)。また、Caché SQL 拡張機能 SET TRANSACTION、START TRANSACTION、SAVEPOINT、および %INTRANS もサポートしています。また、Caché には、M 言語 Type A 標準のトランザクション・コマンドのうち、実装されているものもあります。
以下のテーブルは、これらの SQL と ObjectScript コマンドの概要です。
SQL コマンド | ObjectScript コマンド | 定義 |
---|---|---|
SET TRANSACTION | トランザクションを開始せずに、トランザクション・パラメータを設定します。 | |
START TRANSACTION | TSTART | トランザクションの開始を表します。 |
%INTRANS | $TLEVEL | トランザクションが現在進行中かどうかを検出します。
|
SAVEPOINT | トランザクション内のポイントを指定します。セーブポイントまでの部分的なロール・バックに使用できます。 | |
COMMIT | TCOMMIT | トランザクションの正常終了を示します。 |
ROLLBACK | TROLLBACK | トランザクションの失敗を示します。トランザクションの開始後に実行されたすべてのデータベース更新は、ロールバックされるか、元の状態に戻ります。 |
これらの ObjectScript と SQL のコマンドは完全に互換性があり、置き換え可能ですが、以下の例外があります。
ObjectScript TSTART と SQL START TRANSACTION はどちらも、トランザクションが進行中でない場合にトランザクションを開始します。ただし、START TRANSACTION では、入れ子になったトランザクションはサポートされません。そのため、入れ子になったトランザクションが必要な場合 (または必要になる可能性がある場合) には、トランザクションを TSTART で始めることをお勧めします。SQL 標準との互換性が必要な場合は、START TRANSACTION を使用してください。
Caché MultiValue Basic (MVBasic) トランザクション・コマンドも ObjectScript と SQL のトランザクション・コマンドと互換性があります。
トランザクションでの LOCK の使用
複数のプロセスがアクセスする可能性のあるグローバルにアクセスする際、常にそのグローバルで LOCK コマンドを使用して、データベースの整合性を保護する必要があります。グローバル変数に対応したロックを発行し、グローバルの値を変更し、次にロックをアンロックします。LOCK コマンドは、指定したロックのロックとアンロックの両方に使用されます。グローバルの値を変更しようとするその他のプロセスはロックを要求し、そのロックは最初のプロセスがロックを解除するまで待機します。
トランザクションでロックを使用する際、以下の 3 つの考慮事項があります。
-
ロック/アンロック操作は、ロールバックを実行しません。
-
トランザクション内で、プロセスによって保持されたロックをアンロックすると、以下の 2 つのうちどちらかが発生する場合があります。
-
ロックがすぐにアンロックされる。ロックは直後に別のプロセスによって取得可能になります。
-
ロックがロック解除状態に置かれる。ロックはアンロックされていますが、現在のトランザクションの終了まで別のプロセスによって取得可能な状態になりません。
ロックがロック解除状態にある場合は、Caché はトランザクションがコミットされるかロールバックされるまで、アンロックを延期します。トランザクション内では、ロックはアンロック状態で表示され、同じ値の後続のロックが許可されます。しかし、トランザクションの範囲外では、ロックはロックされたままになります。詳細は、このドキュメントの “ロック管理” の章を参照してください。
-
-
タイムアウトになるロック操作は、$TEST を設定します。トランザクション時に $TEST で設定される値はロールバックしません。
トランザクションでの $INCREMENT と $SEQUENCE の使用法
$INCREMENT または $SEQUENCE 関数呼び出しは、トランザクションの一部ではありません。したがって、トランザクション・ロールバックの一部としてロールバックされません。これらの関数を使用すると、LOCK コマンドを使用せずにインデックス値を取得できます。この機能は、トランザクションの継続中にカウンタ・グローバルをロックしたくない場合に役立ちます。
$INCREMENT は、1 つ以上のプロセスからインクリメント要求を受け取った順に個々の整数値を割り当てます。$SEQUENCE は複数プロセスに対して高速手段を提供するものであり、整数値のシーケンス (範囲) を各増分プロセスに割り当てることにより、同一のグローバル変数に対して一意 (重複なし) の整数を取得します。
$INCREMENT は、トランザクション内の 1 プロセスによってインクリメントされます。また、トランザクションの処理中、並行するトランザクションの別のプロセスによってもインクリメントされる場合があります。最初のトランザクションをロールバックすると、インクリメントが “スキップされ”、“使用されない” 番号が発生する可能性があります。
アプリケーション内でのトランザクション・ロールバック
トランザクション中にエラーに遭遇した場合、以下の 3 つの方法でロールバックできます。
-
SQL ロールバック・コマンド ROLLBACK WORK の発行
-
ObjectScript ロールバック・コマンド TROLLBACK の発行
-
%ETN の呼び出し
トランザクションをロールバックする場合、既定クラスの IDKey はデクリメントされません。 IDKey の値は、$INCREMENT 関数によって自動的に変更されます。
SQL あるいは ObjectScript ロールバック・コマンドの発行
アプリケーション開発者は、2 種類のロールバック・コマンドを使用して、トランザクションが失敗した場所を指定し、不完全なトランザクションを自動的にロールバックします。
-
マクロ・ソース・ルーチンで、##sql(ROLLBACK WORK) を使用
-
マクロあるいは中間ソース・コードで、ObjectScript TROLLBACK コマンドを使用
以下の例のように、ロールバック・コマンドは、エラー・トラップと併用する必要があります。
ROU ##sql(START TRANSACTION) set $ZT="ERROR"
SET ^ZGLO(1)=100
SET ^ZGLO=error
SET ^ZGLO(1,1)=200
##sql(COMMIT WORK) Write !,"Transaction Committed" Quit
ERROR ##sql(ROLLBACK WORK)
Write !,"Transaction failed." Quit
この例のコードでは、トランザクションがコミットされる前にプログラム・エラーが発生した場合、$ZT が ERROR サブルーチンを実行するように設定されています。ROU 行は、トランザクションを開始し、エラー・トラップを設定します。ROU+1 行と ROU+3 行では、グローバル ^ZGLO のノードを設定します。しかし、変数 error が未定義の場合、ROU+2 でプログラム・エラーが発生し、ROU+3 行は実行されません。プログラムは、ERROR サブルーチンを実行し、^ZGLO(1) の設定は取り消されます。ROU+2行が削除されていたら、^ZGLO は 2 回とも値を設定し、トランザクションはコミットされて、“Transaction committed” というメッセージが表示されます。
%ETN への呼び出し
ロールバック・コマンドでトランザクション・ロールバックを処理しない場合、エラー・トラップ・ユーティリティ %ETN が不完全なトランザクションを検出し、トランザクションのコミットまたはロールバックのいずれかをユーザに指示します。不完全なトランザクションをコミットすると、通常、論理データベースの整合性の低下を招くため、アプリケーション内でロールバックを処理する必要があります。
トランザクションの進行中に発生したエラーの後に %ETN を実行する場合、以下のロールバック・プロンプトが表示されます。
You have an open transaction.
Do you want to perform a Commit or Rollback?
Rollback =>
10 秒のタイムアウト時間内に何も応答がない場合、既定でロールバックされます。ジョブ起動ジョブあるいはアプリケーション・モード・ジョブでは、メッセージなしでトランザクションがロールバックされます。
%ETN 自体は、トランザクションのロールバックを引き起こしませんが、通常、%ETN は Caché の停止によって終了します。ObjectScript の停止時にトランザクション・ロールバックが発生し、システムが %HALT を実行して Caché プロセスのクリーンナップを実行します。%ETN には、BACK^%ETN というエントリ・ポイントがあり、halt ではなく quit で終了します。ルーチンが BACK^%ETN ではなく ^%ETN や FORE^%ETN を呼び出す場合、エラー処理プロセスの一部としてトランザクション・ロールバックを実行しません。
アプリケーション内のトランザクション処理の例
以下の例は、マクロ・ソース・ルーチンでトランザクションを処理する方法です。SQL コードでデータベース更新を実行します。以下の SQL 文は、ある口座から別の口座に資金を送金します。
Transfer(from,to,amount) // Transfer funds from one account to another
{
TSTART
&SQL(UPDATE A.Account
SET A.Account.Balance = A.Account.Balance - :amount
WHERE A.Account.AccountNum = :from)
If SQLCODE TRollBack Quit "Cannot withdraw, SQLCODE = "_SQLCODE
&SQL(UPDATE A.Account
SET A.Account.Balance = A.Account.Balance + :amount
WHERE A.Account.AccountNum = :to)
If SQLCODE TROLLBACK QUIT "Cannot deposit, SQLCODE = "_SQLCODE
TCOMMIT
QUIT "Transfer succeeded"
}
自動トランザクション・ロールバック
以下の場合、トランザクション・ロールバックが自動的に発生します。
-
Caché の起動 (リカバリが必要な場合)。起動されると、Caché はリカバリが必要なことを判定し、コンピュータ上の不完全なトランザクションをすべてロールバックします。
-
HALT コマンド (現在のプロセスの場合) または ^RESJOB ユーティリティ (その他のプロセスの場合) を使用したプロセスの終了。バックグラウンド・ジョブ (非インタラクティブ・プロセス) を停止すると、進行中の現在のトランザクションに対する変更が自動的にロールバックされます。インタラクティブ・プロセスを停止すると、進行中の現在のトランザクションに対する変更をコミットするかロールバックするかを確認するプロンプトが表示されます。^RESJOB をプログラマ・モード・ユーザ・プロセスで発行すると、ユーザにメッセージが表示され、現在のトランザクションをコミットするかロールバックするか尋ねられます。
また、システム管理者は、^JOURNAL ユーティリティを実行して、クラスタ固有のデータベースで不完全なトランザクションをロールバックできます。^JOURNAL ユーティリティのメイン・メニューから Restore Globals From Journal オプションを選択すると、ジャーナル・ファイルがリストアされ、すべての不完全なトランザクションがロールバックされます。
トランザクション処理に関するシステム全体の問題
このセクションでは、トランザクション処理に関するさまざまなシステム全体の問題について説明します。バックアップに関する問題の詳細は、"Caché データ整合性ガイド" の "バックアップとリストア" の章を参照してください。ECP に関する問題の詳細は、"Caché 分散データ管理ガイド" の付録 "ECP リカバリ保証と制限" を参照してください。
トランザクション処理でのバックアップとジャーナリング
トランザクション処理を実装する際、以下のバックアップおよびジャーナリングの手順を検討してください。
Caché の各インスタンスにはジャーナルがあります。ジャーナルとは、最後のバックアップ以後、データベースに対して行われた変更を、時間順で記録している一連のファイルからなるログです。Caché トランザクション処理は、データの論理的整合性を維持するために、ジャーナリングと共に機能します。
ジャーナルには、トランザクションにあるグローバルに対する SET 処理と KILL 処理が記録されます。これは、影響を受けるグローバルが保存されているデータベースのジャーナル設定には関係なく記録されます。さらに、[グローバル・ジャーナル状態] を “Yes” に設定したデータベースのグローバルに対するすべての SET 処理と KILL 処理も記録されます。
バックアップは、トランザクション処理中に実行できます。ただし、その結果として生ずるバックアップ・ファイルには、トランザクションの一部が含まれる場合があります。災害が発生し、バックアップからリストアする必要が生じた場合は、まずバックアップ・ファイルをリストアしてから、リストアしたデータベースのコピーにジャーナル・ファイルを適用します。ジャーナル・ファイルを適用すると、バックアップの時点から災害発生時までの間にジャーナルに記述された更新がすべてリストアされます。トランザクションの一部を完了し、コミットされていないトランザクションをロールバックすることによってデータベースのトランザクションの整合性をリストアするには、ジャーナルの適用が必要です。これは、バックアップを実行した時点では、データベースにすべてのトランザクションが記録されていない可能性があるためです。詳細は、以下を参照してください。
非同期エラーの通知
%SYSTEM.ProcessOpens in a new tab の AsynchError()Opens in a new tab メソッドを使用して、ジョブが非同期エラーで割り込まれるかどうかを指定できます。
-
%SYSTEM.Process.AsynchError(1) は、非同期エラーの受け入れを可能にします。
-
%SYSTEM.Process.AsynchError(0) は、非同期エラーの受け入れを不可能にします。
Config.MiscellaneousOpens in a new tab クラスの AsynchErrorOpens in a new tab プロパティは、新規プロセスに対するシステム全体の既定として、プロセスが非同期エラーで割り込まれるかどうかを設定します。既定値は 1 で、“割り込まれる” を意味します。
特定のジョブで複数の非同期エラーが検出される場合、システムは少なくとも 1 つ非同期エラーを引き起こします。しかし、どのエラーが起こるかは判別できません。
非同期エラーには、以下が実装されています。
-
<LOCKLOST> — このジョブで一度ロックされたもののうち、いくつかがリセットされました。
-
<DATALOST> — このジョブで実行されたデータ修正のうち、いくつかはエラーとしてサーバから返されました。
-
<TRANLOST> — このジョブで開された分散型トランザクションが、サーバによって非同期的にロール・バックされました。
非同期エラーを受信しているジョブを無効にした場合でも、次に ZSync コマンドを実行すると、非同期エラーが引き起こされます。
TStart、TCommit、または LOCK 操作、およびネットワーク・グローバル参照が行われるたびに、Caché は保留になっている非同期エラーをチェックします。ネットワーク上の SET 操作と KILL 操作は非同期であるため、SET の生成時と非同期エラーの報告時の間に、任意の数の他の指示が挿入される場合があります。