Finally...
0 |
上了一課!
我寫了一段Code,利用catch SqlException偵測Deadlock, 前兩次延遲一小段時間再試,第三次則直接丟出Exception。在少數特殊情境下,程式要先切換成特定使用者身份,待處理完成後再切回系統身份。程式示意如下:
1: try
2: {
3: for (int i = 0; i < 3; i++)
4: {
5: try
6: {
7: if (needImpersonate)
8: changeIdentity(someUser);
9: doTheProcess();
10: if (needImpersonate)
11: changeIdentity(systemUser);
12: break;
13: }
14: catch (SqlException se)
15: {
16: //...some code...
17: //if deadlock and i<2
18: // log error and
19: // Thread.Sleep()
20: //else
21: // throw DBFailException
22: }
23: }
24: }
25: catch (Exception ex)
26: {
27: LogError(ex);
28: }
一直不覺得這寫法有什麼問題,直到昨天被電...
無意間發現一個作業的執行身份不正確,明明系統身份才會執行的作業,卻掛了某個陌生的使用者名稱。滿心狐疑地仔細一查,媽媽咪呀! 從某個時點之後,程式一直在借用這個倒楣的使用者身份處理日常作業,一路下來,有近兩千筆的作業記錄,執行身份都誤植成無辜的受害者...
所幸程式裡涉及身份切換的地方不多,很快的就聚焦到這段程式上。用力想了一下,啊! 如果需要變身的情境,changeIdentity(someUser)過後,doTheProcess()發生非DB錯誤,程式便不會跳進14列catch (SqlException se),而會進入25列的外圈catch中,那麼11列changeIdentity(systemlUser)被略過忘了將身份還原回來,於是系統接著就用someUser一路跑下去。
知道問題根源,解決只在彈指之間。此處只需加上一個finally block,將還原系統身份的Code搬進去。如此不管天崩地裂、海枯石爛,切換回systemUser的程式碼都一定會被執行。(有人可能會懷疑,不是已經break了,還會向下跑嗎? 記住,finally block裡的程式,不管你在try block下了break, continue, return還是Response.End()... 都得乖乖跑完,這點特性要牢記。)
for (int i = 0; i < 3; i++)
{
try
{
if (needImpersonate)
changeIdentity(someUser);
doTheProcess();
break;
}
catch (SqlException se)
{
//...some code...
//if deadlock and i<2
// log error and
// Thread.Sleep()
//else
// throw DBFailException
}
finally
{
if (needImpersonate)
changeIdentity(systemUser);
}
}
而我學的教訓是: 永遠要假設每一段程式都可能會出錯,並設想出錯時的因應之道。如果你有某段程式無論如何都要執行,記得善用finally block。
Comments
Be the first to post a comment