Python によるトランザクションの管理とロック
Native SDK for Python は、インターシステムズのトランザクション・モデルを使用するトランザクション・メソッドとロック・メソッドを提供します。これについて、以下のセクションで説明します。
-
トランザクションの処理 — トランザクションの開始、入れ子、ロールバック、およびコミットの方法について説明します。
-
並行処理の制御 — さまざまなロック・メソッドの使用法について説明します。
インターシステムズのトランザクション・モデルの詳細は、"ObjectScript の使用法" の “トランザクション処理” を参照してください。
Python でのトランザクションの処理
iris.IRIS クラスは、トランザクション処理のための以下のメソッドを提供します。
-
IRIS.tCommit() — 1 レベルのトランザクションをコミットします。
-
IRIS.tStart() — トランザクションを開始します (このトランザクションは入れ子になっている場合あり)。
-
IRIS.getTLevel() — 現在のトランザクション・レベル (トランザクション内でない場合は 0) を返します。
-
IRIS.increment() — ノードをロックすることなく、ノード値をインクリメントまたはデクリメントします。
-
IRIS.tRollback() — セッション内の開いているトランザクションすべてをロールバックします。
-
IRIS.tRollbackOne() — 現在のレベルのトランザクションのみをロールバックします。入れ子になったトランザクションである場合、それより上のレベルのトランザクションはロールバックされません。
以下の例では、3 レベルの入れ子のトランザクションを開始し、各トランザクション・レベルで異なるグローバル・ノードの値を保存します。3 つのノードはすべて出力され、それらに値があることが証明されます。その後、この例では、2 番目と 3 番目のレベルがロールバックされ、最初のレベルがコミットされます。3 つのノードはすべて再び出力され、依然として値があるのは最初のノードのみであることが証明されます。また、この例では、トランザクション時に 2 つのカウンタをインクリメントして、increment() メソッドと += 演算子の違いを示します。
この例では、新しいストレージ・クラスを作成することなく、データベースに値を保存する便利な方法として、グローバル配列を使用します。この例に直接関係のないグローバル配列操作は、このメインの例の直後にリストされたユーティリティ関数に分離されています。
irispy が接続された iris.IRIS のインスタンスであるとします (“Python での接続の作成” を参照してください)。
グローバル配列ユーティリティ関数 store_values()、show_values()、start_counters()、および show_counters() は、この例の直後にリストされています (“グローバル配列ユーティリティ関数” を参照してください)。
tlevel = irispy.getTLevel()
counters = start_counters()
action = 'Initial values:'.ljust(18,' ') + 'tLevel='+str(tlevel)
print(action + ', ' + show_counters() + ', ' + show_values() )
print('\nStore three values in three nested transaction levels:')
while tlevel < 3:
irispy.tStart() # start a new transaction, incrementing tlevel by 1
tlevel = irispy.getTLevel()
store_values(tlevel)
counters['add'] += 1 # increment with +=
irispy.increment(1,counters._global_name, 'inc') # call increment()
action = ' tStart:'.ljust(18,' ') + 'tLevel=' + str(tlevel)
print(action + ', ' + show_counters() + ', ' + show_values() )
print('\nNow roll back two levels and commit the level 1 transaction:')
while tlevel > 0:
if (tlevel>1):
irispy.tRollbackOne() # roll back to level 1
action = ' tRollbackOne():'
else:
irispy.tCommit() # commit level 1 transaction
action = ' tCommit():'
tlevel = irispy.getTLevel()
action = action.ljust(18,' ') + 'tLevel=' + str(tlevel)
print(action + ', ' + show_counters() + ', ' + show_values() )
出力:
Initial values: tLevel=0, add=0/inc=0, values=[]
Store three values in three nested transaction levels:
tStart: tLevel=1, add=1/inc=1, values=["data1"]
tStart: tLevel=2, add=2/inc=2, values=["data1", "data2"]
tStart: tLevel=3, add=3/inc=3, values=["data1", "data2", "data3"]
Now roll back two levels and commit the level 1 transaction:
tRollbackOne(): tLevel=2, add=2/inc=3, values=["data1", "data2"]
tRollbackOne(): tLevel=1, add=1/inc=3, values=["data1"]
tCommit(): tLevel=0, add=1/inc=3, values=["data1"]
この例では、新しいストレージ・クラスを作成することなく、データベースに値を保存する便利な方法として、グローバル配列を使用します (“Python を使用したグローバル配列へのアクセス” を参照してください)。次の関数では、data_node と counter_node という 2 つの IRISGlobalNode オブジェクトを使用して、永続データを保存および取得します。
data_node オブジェクトは、トランザクション値の保存および取得に使用されます。トランザクションごとに、添え字としてレベル番号を使用して、別個の子ノードが作成されます。
irispy.kill('my.data.node') # delete data from previous tests
data_node = irispy.node('my.data.node') # create IRISGlobalNode object
def store_values(tlevel):
''' store data for this transaction using level number as the subscript '''
data_node[tlevel] = '"data'+str(tlevel)+'"' # "data1", "data2", etc.
def show_values():
''' display values stored in all subnodes of data_node '''
return 'values=[' + ", ".join([str(val) for val in data_node.values()]) + ']'
increment() メソッドは、一般にデータベースへの新しいエントリの追加を試みる前に呼び出され、ノードをロックしなくても、カウンタが迅速かつ安全にインクリメントされるようにします。インクリメントされたノードの値は、トランザクションのロールバックによる影響を受けません。
これを示すために、counter_node オブジェクトを使用して、2 つのカウンタ値を管理します。counter_node(’add’) サブノードは標準の += 演算子を使用してインクリメントされ、counter_node(’inc’) サブノードは increment() メソッドを使用してインクリメントされます。counter_node(’add’) 値とは異なり、counter_node(’inc’) 値はロールバック後もその値が維持されます。
# counter_node object will manage persistent counters for both increment methods
irispy.kill('my.count.node') # delete data left from previous tests
counter_node = irispy.node('my.count.node') # create IRISGlobalNode object
def start_counters():
''' initialize the subnodes and return the IRISGlobalNode object '''
counter_node['add'] = 0 # counter to be incremented by += operator
counter_node['inc'] = 0 # counter to be incremented by IRIS.increment()
return counter_node
def show_counters():
''' display += and increment() counters side by side: add=#/inc=# '''
return 'add='+str(counter_node['add'])+'/inc='+str(counter_node['inc'])
Python による並列処理の制御
並行処理の制御は、InterSystems IRIS などのマルチプロセス・システムのきわめて重要な機能です。この機能により、データの特定の要素をロックして、複数のプロセスが同じ要素を同時に変更することによって起きるデータ破損を防ぐことができます。Native SDK トランザクション・モデルは、ObjectScript コマンドに対応する一連のロック・メソッドを提供します ("ObjectScript リファレンス" の “LOCK” を参照してください)。
クラス iris.IRIS の以下のメソッドを使用して、ロックを取得および解放します。
-
IRIS.lock() — lockReference および *subscripts 引数で指定されたノードをロックします。このメソッドは、ロックを取得できない場合、事前に定義した間隔が経過するとタイムアウトします。
-
IRIS.unlock() — lockReference および *subscripts 引数で指定されたノードのロックを解放します。
-
IRIS.releaseAllLocks() — この接続で現在保持されているロックをすべて解放します。
パラメータ :
lock(lockMode, timeout, lockReference, *subscripts)
unlock(lockMode, lockReference, *subscripts)
releaseAllLocks()
-
lockMode — 以前に保持されていたロックを処理する方法を指定する str。有効な引数は、共有ロックを表す S、エスカレート・ロックを表す E、共有とエスカレートの両方を表す SE です。既定値は空の文字列 (排他の非エスカレート) です。
-
timeout — ロックの取得を試行するときに待機する秒数。この秒数を経過するとタイムアウトになります。
-
lockReference — 曲折アクセント記号 (^) で始まり、その後にグローバル名が続く str (例えば、単なる myGlobal ではなく ^myGlobal)。
重要:ほとんどのメソッドで使用される globalName パラメータとは異なり、lockReference パラメータには、先頭に曲折アクセント記号を付ける必要があります。globalName ではなく lockReference を使用するのは、lock() および unlock() のみです。
-
subscripts — ロックまたはアンロックするノードを指定する 0 個以上の添え字。
これらのメソッドに加え、すべてのロックとその他の接続リソースを解放するために、IRISConnection.close() メソッドが使用されます。
管理ポータルを使用して、ロックを検証できます。System Operation > View Locks に移動して、システムでロックされている項目のリストを表示します。
並行処理の制御に関する詳細な説明は、このドキュメントの対象ではありません。この内容に関する詳細は、以下の項目を参照してください。
-
"ObjectScript の使用法" の “トランザクション処理” および “ロック管理”
-
"サーバ側プログラミングの入門ガイド" の “ロックと並行処理の制御”
-
"ObjectScript リファレンス" の “LOCK”