上了一課!

我寫了一段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

Post a comment