今天下午有同事反饋訂單列表頁狀態(tài)與詳情頁狀態(tài)不一致。
背景:
用戶下單及付款等操作后,部分訂單信息會推送到公司二方團隊用于列表頁展示。
(列表頁由平臺統(tǒng)一維護,詳情頁業(yè)務(wù)方維護)
有業(yè)務(wù)事件發(fā)生時,會推一次訂單。因而同一訂單會多次推送。
不同業(yè)務(wù)事件間隔不定,存在并發(fā)推送一條數(shù)據(jù)的場景。
核對日志,發(fā)現(xiàn)了問題。
訂單推送時,平臺會以version的大小作為是否拋棄該次推送結(jié)果。
背景:
version計算邏輯:用當(dāng)前時間減去下單時間
將歷史推送的version存于DB,每次從DB中獲取當(dāng)前的version(只查有效的,不含軟刪)。
version+1后,先將當(dāng)前的version軟刪,再寫進一條數(shù)據(jù)。
(超過Integer.MAX_VALUE有單獨邏輯)
若version不存在,則會根據(jù)當(dāng)前時間重新生成一條version,寫入DB。
坑點:平臺接收推送數(shù)據(jù)成功,數(shù)據(jù)格式正確,即會告知業(yè)務(wù)方推送成功。
對推送version小于歷史version的數(shù)據(jù),平臺內(nèi)部會拋棄該次推送的數(shù)據(jù),但是仍然告知業(yè)務(wù)方推送成功。
根據(jù)訂單號去查該訂單的推送version記錄:

verison記錄
可以看出有一條version超大。從日志上看,35和10973兩條記錄的推送間隔在ms級別。
由于平臺的策略,導(dǎo)致version10973之后的推送數(shù)據(jù)均會失效。
根據(jù)DB中version數(shù)據(jù)反推,時序如下:

請求時序
這里還存在代碼bug:之前是向從庫查詢version記錄,在A將34置為無效并同步到從庫后,B讀從庫發(fā)現(xiàn)無記錄,然后B會向主庫寫了一條新的version記錄10973。讀從庫增大了A、B之間的時間差。
發(fā)現(xiàn)了問題之后,fix策略:
查詢version均走主庫
若不存在version,生成后寫入;
若存在,將當(dāng)前version+1,帶樂觀鎖version更新(放棄軟刪+新增步驟);
若更新失敗,則查詢實時的version,+1后再次帶樂觀鎖version更新;
對于推送的數(shù)據(jù)部分,實時查主庫后再推送;
(實際fix邏輯比這復(fù)雜,由于一些原因不過多描述)
事后反思,存在以下問題:
關(guān)鍵操作,不能讀從庫;
對于同一條數(shù)據(jù)的操作,不能先軟刪,再新增一條數(shù)據(jù)(非原子操作,會造成很多問題,如軟刪遺漏、新增失敗,均會導(dǎo)致問題);
平臺應(yīng)明確告知會拋棄當(dāng)前數(shù)據(jù)(告知原因更佳),而不是告知成功;
推送前應(yīng)實時查最新數(shù)據(jù),而是已有數(shù)據(jù);