記錄一個前端bug的解決過程

人在江湖飄,哪能不挨刀。vue

我捱了重重一bug。嚴格來說這多是我職業生涯以來的首個悲慘經歷,由於憑個人知識儲備和經驗,基本上任何可重現的bug都是可解的。然而這個bug卻困擾了我三個月之久,它具備如下生理特徵:後端

  1. 後臺日誌能統計到異常,偶發、低頻
  2. 報異常的用戶設備不具備規律性,什麼手機都有
  3. 咱們本身沒法復現,任何設備、任何環境都沒復現
  4. 打電話回訪報異常的用戶,確實存在問題
  5. 客服未接到用戶主動反饋這個異常

此bug並非js報錯,而是一個業務邏輯的錯誤。表現是,用戶提交的數據莫名缺失。場景是如下這個界面數組

當用戶填滿全部的空以後,提交按鈕變爲可用狀態,數據放進一個數組中提交上來。後臺有報錯日誌顯示,用戶提交上來的有些是空數組,有些是數組中缺了幾項。瀏覽器

問題在於,提交前是有校驗的,用戶不可能提交上來這種未經過校驗的數據。而且仍是偶發的啊,若是是邏輯寫錯了,那應該所有會報錯,咱們在測試的時候確定會發現。網絡

最棘手的地方是,咱們壓根沒重現過這個狀況,找各類同事、各類手機、各類胡亂操做,一次都沒重現出來。這就給調試帶來很大的麻煩,只能是猜想哪裏可能出問題,而後去驗證。可是根本無法去驗證啊。。。重現不了,又如何判斷是成功fix了。架構

看來能驗證的手段就只剩一個:線上日誌。猜問題、上線、看日誌。函數

這是一個痛苦的過程。界面雖然簡單,這倒是一個龐雜的項目。由於題型衆多,抽離了不少組件,爲了公用和靈活擴展,組件嵌套深度有五層之多。其架構複雜程度在個人職業生涯中也能排TOP3.測試

拿題乾的渲染來講,就有:公式圖片轉LaTeX、mathjax渲染公式、渲染公式上的空、給空編號、模擬光標、自動focus空、動態計算字體大小等諸多流程。並且下方那個鍵盤仍是咱們H5模擬的,並非系統鍵盤。更別提還有校驗邏輯、判分邏輯。字體

前n次嘗試優化

看距離上一版有哪些改動,抹去有嫌疑的改動,看日誌是否正常。尷尬的是,這是一次重大重構,改動的地方還特別多。因而一場盲人摸象式的遠程debug行動開始了。

一次又一次的上線、觀察日誌、下線。不斷排除了一些相關的功能,始終未能診斷到問題所在。甚至連我很確信的地方都嘗試了,仍是找不到問題。前先後後嘗試了二十屢次吧,改到我都懷疑人生了。領導看了這些上線記錄都怒了,說你這上上下下的搞雞毛呢。我也很崩潰啊。

看來用這個盲人摸象手段是搞不定了,我意識到了狀況的嚴重性,暗暗感受這可能不是輕易能解決的,呂某必定使出畢生所學,爲民除害。

第n+1次嘗試

既然有那麼多的用戶日誌,咱們本身爲什麼重現不了?這是我一直糾結的。因而再次進行瘋狂測試。

皇天不負有心人,我居然真的給重現出來了!操做是這樣的:填好空,兩個手指同時按下提交按鈕和刪除按鈕。這樣的話既經過了校驗,又能在提交以前把數據給刪了。

發現這個騷操做的時候我是很興奮的,可是會有那麼多用戶這麼操做嗎?顯然不太可能。此時我又想到,提交按鈕和刪除按鈕是挨着的,會不會是用戶按提交的時候誤觸了刪除鍵。這還算比較合理,畢竟用戶是小學生嘛,操做不必定那麼精準的。

我興奮不已的進行驗證。在刪除鍵和提交鍵之間加了「下一空」按鈕(經過配置),這樣用戶保證不會誤觸了。

上線,日誌依舊。我摔啊,看來並非誤觸的事。

第n+2次嘗試

隨着bug拖的時間愈來愈長,個人心態也有點焦躁。但思路仍是聚焦在刪除按鈕上,畢竟這是好不容易發現能重現的。

如何可以既點提交又點刪除呢?這時候我想到了點擊穿透(鍵盤爲了響應快,使用了touchstart事件)。由於在點完提交的時候,模擬鍵盤會收起來,而收起的過程當中刪除按鈕會通過提交按鈕的位置。根據點擊穿透的原理,若是此時派發的click事件做用到了刪除按鈕上,那豈不是就算點到了?

我都有點佩服個人想象力了,黔驢技窮了啊,試吧。避免點擊穿透有兩種方式,阻止click事件的默認動做,或者是讓元素收起的時間延遲。我選了後者。

上線,日誌依舊。我吐血。後來一想,刪除按鈕根本都沒監聽click事件啊,哪來的穿透。真是病急亂投醫了。

第n+3次嘗試

掃代碼,發現一個很重的疑點。答案是個數組,是引用類型。因爲複雜的組件關係,這個引用類型的數據能夠被多個組件訪問到。

使用可變數據的時候有個隱患,它可能在你不知道的地方被修改。代碼是vue寫的,有些組件中含有watch,搞很差是意外進了哪裏的watch,在點完提交的時候也會把數據給更改了。

這個猜想我以爲是合理的,在開發階段我就曾由於未使用immutable數據而隱隱擔憂過。好了,快速驗證吧。在點完提交按鈕的時候,我把答案數據給克隆了一份,而後再進行判分和提交的操做。這下就不擔憂已經拿到的數據被篡改了。

上線,日誌依舊。繼續吐血。

不過此次也縮小了嫌疑範圍,看來數據不是在點完提交的時候被篡改了,而是提交上來的就有問題。匪夷所思的是,用戶是如何繞過校驗把數據提交上來的呢?難不成是個人校驗函數有問題,這個地方把數據給改了?掃了一遍代碼,無果。

第n+4次嘗試

此時聚焦到了用戶在填寫答案的時候發生了什麼。我像偵探同樣用放大鏡一遍遍看代碼,然而好多天的追蹤,並無找到什麼有用線索。

直到有一天,那天陽光明媚天空飄着朵朵白雲,感受有什麼好事要發生。QA在反饋羣裏發了一張截圖,說公式解析的那個點點點一直不消失(正在解析的狀態),並且空裏也輸不進內容去。以下圖:

我敏感的神經頓時嗅到了一絲線索。題幹使用了mathjax來解析公式,而mathjax在解析的過程當中會按需加載一些字體文件,並且還會掃描頁面節點,並生成大量的DOM節點。這對瀏覽器來講是個壓力不小的事情,更況且是移動端。

我立刻再掃描公式處理的代碼,因爲有些空會在公式上出現,因此代碼是在等公式渲染完後統一給空編序號,而後進行自動focus,並且自動focus的時候還會首先給答案賦值。天吶,問題該不會出如今這裏吧!公式的渲染過程可能有延時,用戶可能在這個時間進行點什麼操做!

首先這符合偶發這個事實,由於公式解析中出現抖動網絡延遲什麼的也是偶然現象。再者公司的網絡快,用戶的網絡可能慢,這也符合咱們一直未重現的事實。感受此次很靠譜了!不少偵探電視都是這麼演的啊,主人公經過別人無心的一句話聯想到了線索,而後案件破解,真相大白!對對對,就是這個感受!

趕快在代碼層面作優化,儘量早地處理沒有公式的空,有公式的地方也確保執行完後用戶才能輸入。

優化完畢,迴歸測試,萬事俱備,只等線上驗證,一槌定音!

結果......日誌還有啊!啊噗!,電視裏都是騙人的啊!

等等!日誌雖然還有,但好像少了耶!難道此次的優化是有做用的?雖然從理論上能解釋一些做用,但還存在的日誌又表示什麼呢?難道形成丟答案的緣由不止一個?

第n+5次嘗試

時間一每天過去,我仍是沒找到什麼有力線索。中陸續有一些猜想,打了一些日誌點後仍是無果。看着QA同事緊縮的眉頭,領導關切的詢問,我也愈加焦慮了起來。由於我這是一個公共組件庫,有其餘項目在等着使用,若是個人bug解決不了,將影響其餘項目的進度。

又是陽光明媚的一天,天空飄着朵朵白雲。我無心跟另外一位後端同事聊到了這個話題,他隨口一說:應該是超時自動提交的吧。

什麼?什麼!自動提交?!我忽然像被閃電擊中。由於我寫的這是個公共組件,同時也對外暴漏了一些API,好比提交答案就是其中一個。我提供的是答題界面的組件,可是別人項目中有倒計時的場景,超時後會調用個人提交API,把用戶答案提交上去。

若是超時的時候,用戶什麼也沒填,那豈不是把空答案給提交上去了!!!根本沒有校驗函數什麼事,是別人調API提交的啊。

我哭了。難怪沒用戶反饋呢,時間到了自動提交空答案,他們沒理由反饋啊。難怪咱們本身沒重現呢,一直沉浸在怎麼亂點出來。就算QA同窗看到了超時提交的時候,也沒法意識此時是空答案。

沒錯,真相就是它了,修改相關邏輯後上線,果真報錯日誌沒了。困擾我三個月的bug終於解決了!我閉上眼睛,內心默默放了一把鞭炮。

總結

前先後後三個月時間,總算是找到了問題所在。其實第n+4次是解決了一些問題的,最後一次是完全解決,我用實際行動證實了,真相不止一個。而這件沸沸揚揚的丟答案bug事件,也給了我不少啓示。

  1. 作重構的時候要格外當心邏輯更改,重構後必定要跑通全部case。

  2. 排查問題的方式,這期間我使用了各類對照試驗,各類源碼級別的排查

  3. 使用vue作複雜項目的時候要格外注意組件的嵌套層數,少寫watch,避免程序執行順序的混亂

  4. 設計對外API時,要考慮健壯性,不光考慮傳入參數的不穩定,還要考慮當前上下文的不穩定。

相關文章
相關標籤/搜索