概要
TDVのプロシージャ(SQL Script)を用いて、バックエンドのデータベースに対する複数データの更新(INSERT/UPDATE/DELETE)を実装するケースは多々あります。
この際、複数の更新処理の途中でエラーが発生した場合、デフォルトの挙動では「成功した処理はコミットされ、失敗した処理以降は実行されない」という状態になり、データの不整合(部分的な更新)が発生してしまいます。
本記事では、TDV Server 8.8.0 (Windows版) と MS SQL Serverデータソース を用いて、複数の更新処理を含むプロシージャにおいてアトミック性(全処理が成功するか、全て失敗して元の状態に戻るか)を担保するための実装方法と、その挙動の違いを検証します。
検証1:アトミック性を担保しない処理(デフォルトの挙動)
まずは、トランザクション制御を明示的に行わない標準的なBEGIN ... ENDブロックのプロシージャを実行してみます。
意図的に、2つ目のINSERT処理で文字列表現を数値型 (INTEGER) のカラムに投入しようとしてキャストエラーを発生させます。
TDVプロシージャ定義: default_procedure
PROCEDURE default_procedure(OUT result_msg VARCHAR) BEGIN -- 1. Sourceから削除 (正常に成功する) DELETE FROM /shared/IndependentTransaction/DataSouces/SQLServer/TDV_DB/TDV_DB/atomicity/Atomic_Source WHERE id = 100; -- 2. Targetへ挿入 (意図的にキャストエラーを起こし失敗させる) INSERT INTO /shared/IndependentTransaction/DataSouces/SQLServer/TDV_DB/TDV_DB/atomicity/Atomic_Target(id, name) VALUES (CAST('ErrorVal' AS INTEGER), 'Moved Data'); SET result_msg = 'Unexpected Success'; EXCEPTION ELSE SET result_msg = 'Error Occurred (Non-Atomic): ' || CURRENT_EXCEPTION.MESSAGE; END
実行結果と解説
このプロシージャを実行すると、例外(EXCEPTION)ブロックに遷移し、エラーメッセージが返却されます。
しかし、データソース側を確認するとID:100のデータはAtomic_Sourceから削除されたままとなります。
これは、標準のBEGINブロックでは処理が正常終了するごとにオートコミットされてしまうためです。最初のDELETEが成功した時点でデータベースに変更が確定してしまい、その後のINSERTでエラーが発生しても、DELETEを取り消す(ロールバックする)ことはできません。
このままでは、データが「消えただけ」になり、データ不整合を招きます。
検証2:アトミック性を担保する処理 (INDEPENDENT TRANSACTION)
次に、TDVで明示的にトランザクションを管理し、アトミック性を担保する方法を検証します。
ブロックの開始をBEGINではなくBEGIN INDEPENDENT TRANSACTIONとし、例外発生時にROLLBACKを呼び出すように変更します。
TDVプロシージャ定義: atomic_procedure
PROCEDURE atomic_procedure(OUT result_msg VARCHAR) BEGIN INDEPENDENT TRANSACTION -- 1. Sourceから削除 (正常に成功する) DELETE FROM /shared/IndependentTransaction/DataSouces/SQLServer/TDV_DB/TDV_DB/atomicity/Atomic_Source WHERE id = 100; -- 2. Targetへ挿入 (意図的にキャストエラーを起こし失敗させる) INSERT INTO /shared/IndependentTransaction/DataSouces/SQLServer/TDV_DB/TDV_DB/atomicity/Atomic_Target(id, name) VALUES (CAST('ErrorVal' AS INTEGER), 'Moved Data'); -- すべて成功した場合はコミット COMMIT; SET result_msg = 'Unexpected Success'; EXCEPTION ELSE -- エラーが発生した場合はロールバックし、一連の処理を取り消す ROLLBACK; SET result_msg = 'Rolled Back: ' || CURRENT_EXCEPTION.MESSAGE; END
実行結果と解説
このプロシージャを実行すると、先ほどと同様に INSERT 処理でエラーが発生し、例外ブロックに遷移します。
しかし、データソース側を確認するとID:100のデータはAtomic_Sourceに残ったまま(初期状態のまま)となっています。BEGIN INDEPENDENT TRANSACTIONを宣言することで、コミットモードがマニュアルに変更されます。INSERTが失敗して例外ブロックに遷移した際、ROLLBACKが明示的に実行されたため、先行して成功していたDELETE処理も取り消され、データベースの整合性が保たれました。
まとめ
TDVのプロシージャを利用してバックエンドデータベースを更新する際、複数のステップ(特にテーブル間のデータ移動や連鎖的な更新)を伴う場合は、以下のベストプラクティスに従うことを推奨します。
BEGIN INDEPENDENT TRANSACTIONを使用してトランザクションの範囲を明確にする。- 処理の最後で
COMMITを明示的に実行する。 EXCEPTIONブロックでエラーを捕捉し、必ずROLLBACKを実行してデータ不整合を防ぐ。
単純な参照(SELECT)のみのプロシージャであればデフォルトのBEGINで問題ありませんが、更新系処理(DML)を組み込む際は、この「アトミック性の担保」を常に意識して実装設計を行うことが重要です。