- PR -

ロック処理

1
投稿者投稿内容
未記入
常連さん
会議室デビュー日: 2007/05/01
投稿数: 35
投稿日時: 2007-06-04 20:51
お世話になっております。

VB.NETでDB(以降テーブルA)からレコードを1件ずつ読み取り、
DB(以降テーブルB)へ出力させるコンソールアプリケーションを作成しています。
テーブルAには処理済フラグの項目があり、処理が完了したレコードに対しては、
その項目を1にするUPDATE処理をします。

ソースはざっくりと下記となります。


'トランザクションの開始
Dim trn As OracleTransaction = cnnBusiness.BeginTransaction

'テーブルAから1レコードずつSELECT
Dim reader As OracleDataReader
reader = cmdSelect.ExecuteReader()
Try

While reader.Read()

Dim juchuNo As String = reader.GetValue(0)
Dim juchuGak As Integer = reader.GetValue(1)

paramJuchuNo.Value = juchuNo
paramJuchuGak.Value = juchuGak

'テーブルBにMERGE処理
cmdMerge.Parameters.Add(paramJuchuNo)
cmdMerge.Parameters.Add(paramJuchuGak)
cmdMerge.ExecuteNonQuery()
cmdMerge.Parameters.Clear()

'テーブルAのレコードの処理済フラグを1に更新
cmdUpdate.Parameters.Add(paramJuchuNo)・・・★
'cmdUpdate.CommandTimeout = 30・・・★★
cmdUpdate.ExecuteNonQuery()・・・★★★
cmdUpdate.Parameters.Clear()

cnt = cnt + 1

End While

trn.Commit()

テーブルAのあるレコードがロックされていた場合、VB.NETの処理は★で止まります。

ここで下記3点の質問があります。
1.一定時間処理がとまった場合、テーブルAの最初のレコードからリトライするという要件を入れたい。
2.5回リトライし、それでもロックが解放されなかった場合、そのレコードはスキップしたい
3.スキップしたレコードをlog4netでログファイルに出力したい

1〜3の要件を満たすためにはどのようにコーディングすればよいか
教えていただけないでしょうか?



まさる
ベテラン
会議室デビュー日: 2006/12/21
投稿数: 59
お住まい・勤務地: 越後の中ほど
投稿日時: 2007-06-04 22:57
まず問題を切り分けて考えましょう。

1、2、3それぞれを個別に実現するためには何が必要なのか。
それさえ分かれば後はロジックを組むだけです。

それはそれとして、テーブルAのデータを読むときにテーブルAのレコードをロックしてしまえば、
リトライ処理はいらないような気もします。

#なんとなく★の箇所では止まらないような気が。
#★★★の箇所なら分かるんですが。
未記入
常連さん
会議室デビュー日: 2007/05/01
投稿数: 35
投稿日時: 2007-06-05 20:37
まさるさん、ご回答ありがとうございます。

>それはそれとして、テーブルAのデータを読むときにテーブルAのレコードをロックて>しまえば、 リトライ処理はいらないような気もします。
テーブルAのレコードをロックできないときに、リトライ処理させたいのです。

>#なんとなく★の箇所では止まらないような気が。
>#★★★の箇所なら分かるんですが。
申し訳ありません。★★★で止まります。

いろいろ調べながらコーディングしたところ、もう一歩のところまで
きました。現在のソースは以下の状況です。(かなり省いてます)

Dim errorCnt As Integer = 0

While (True)

Try
cmdSelectLock.Parameters.Add(paramJuchuNo)
readerLock = cmdSelectLock.ExecuteReader()
cmdSelectLock.Parameters.Clear()
Exit While

Catch ex As OracleException
If (errorCnt = RETRY_COUNT) Then
cmdSelectLock.Parameters.Clear()
Exit While
End If
errorCnt = errorCnt + 1
cmdSelectLock.Parameters.Clear()
System.Threading.Thread.Sleep(5000)
Finally
readerLock.Close()・・・★★★★

End Try

End While

cmdSelectLockに指定してあるSQLはselect 〜 for update nowaitです。
テーブルAのレコードのロックに失敗すればリソース・ビジーとなり、
catch句の中で、5秒スリープさせリトライ処理します。

ここで壁にぶつかったのですが、★★★★でエラーとなります。
原因は、select 〜 for update nowaitでロック失敗した場合、
readerLockはnothingなので、★★★★でエラーになるのです。
★★★★を省けば、少数レコードの場合は、問題なく動作しますが、
多数のレコードを処理させようとすると、
「ORA-01000: 最大オープン・カーソル数を超えました。」
というエラーが発生します。

ご指導のほどよろしくお願い致します。
まさる
ベテラン
会議室デビュー日: 2006/12/21
投稿数: 59
お住まい・勤務地: 越後の中ほど
投稿日時: 2007-06-06 09:40
引用:

未記入さんの書き込み (2007-06-05 20:37) より:

ここで壁にぶつかったのですが、★★★★でエラーとなります。
原因は、select 〜 for update nowaitでロック失敗した場合、
readerLockはnothingなので、★★★★でエラーになるのです。
★★★★を省けば、少数レコードの場合は、問題なく動作しますが、
多数のレコードを処理させようとすると、
「ORA-01000: 最大オープン・カーソル数を超えました。」
というエラーが発生します。



ただエラーを回避するだけならば、単純にreaderLockがNothingでない時のみ、
Closeメソッドを呼ぶようにすれば良いだけだと思います。

#どうもオブジェクトの破棄などを考慮されてないような気がします。
#下記サイトを参考にして実装してみてください。
#なお、バージョンを提示されてないので分かりませんが、
#VB 2005ならUsingが使えます。

ですが、どうも最初と要件が変わったように見受けられるのですが。

テーブルAのデータが
コード:
 ID CODE VALUE
 -- ---- -----
 1  a    hoge
 2  b    foo
 3  c    bar


とします。そしてID=3のデータの更新でテーブルAのレコードがロックされていたとます。

最初の要件では、一定時間経過してもロックが解除されなかったらID=1,2のデータに対する更新をロールバックし、
もう一度ID=1のデータから更新処理を再開すると思っていました。

しかし、現在はID=3のデータに対して5秒待ってリトライするようになっています。

私、何か勘違いしていますでしょうか?
_________________
まさるblog
1

スキルアップ/キャリアアップ(JOB@IT)