Bug分析,假刪除導致文章發佈成功卻打不開的問題
公司有一個內部博客,大家可以在上面創建自己的賬號,然後寫文章在全公司分享。昨天這個內部博客開通了API,因此我準備寫一個Python程序,把自己文章都搬運上去。
然後我就發現這個API接口有一個bug。並且根據它的現象,猜到它問題出在哪裏。
我先來簡單描述一下現象。
假設我硬盤上現在有50個Markdown文件。現在我要把它發佈到網站上。簡化代碼如下:
import glob
import requests
for path in glob.glob('blog/*.md'):
with open(path) as f:
article = f.read()
requests.post('https://xxx.yyy.com/post?token=abcasdf', json={'content': content})
發佈完成以後,文章確實都已經在網頁上出現了,並且每篇文章都能正常顯示。但我粗略瀏覽了一下,發現裏面有一些文章的末尾帶上來二維碼。我不想讓公司的人知道我的公眾號,所以準備修改一下文章。
有一些文章有二維碼,有一些沒有,一個一個改起來很麻煩,所以我做了兩步操作。首先寫了一個程序,掃描所有Markdown文件,發現二維碼就刪掉。然後,我直接在網站上把剛剛發佈的所有文章都刪了(懶得去看哪篇有二維碼,哪篇沒有,乾脆全刪了重發)。
接下來,我再次運行程序批量重新發布文章。2秒鐘以後發佈完成。
本來一切看起來都很正常,但是當我到網站上查看的時候,發現有很多文章點開以後,都提示『該文章已經刪除』。
我一開始在想是不是我的程序寫得不對,漏掉了這些文章。我重新單獨一篇一篇發佈這篇文章,API接口返回發佈成功,可在網頁上還是顯示文章已經刪除。
然後我一篇一篇檢查這些發佈失敗的文章,發現有一個共同的特點:他們是一開始就沒有二維碼的文章。相當於這些文章我在網站上刪除以後原樣重新又發了一次。
那我就有了一個初步的猜測,大概知道原因是什麼了:
- 因為每篇文章有一個docid,當第一次發佈文章的時候,這個docid就是文章正文內容的md5值。只要文章完全一樣,連續發多少次,它的docid都一樣。這樣就可以防止出現重複文章。(更新的時候,需要用户主動提供docid,避免重新生成新的)。
- 這個網站的刪除功能,肯定是假刪除。也就是當我點了刪除文章的按鈕時,文章其實依然在數據庫裏面,只不過增加了一個字段removed=True。網頁顯示文章的時候,查詢條件肯定是col.find({'removed': {"$ne": True}}),所以就不會把這些被軟刪除的文章顯示出來。
- API發佈新文章的時候,肯定使用的是更新操作。並且使用了upsert=True。
以MongoDB為例,這個API背後的邏輯肯定是這樣的:
def post_article(docid, article_info):
mongo.update_one({'_id': docid}, {'$set': article_info}, upsert=True)
upsert=True的作用,是先檢查數據是否存在,如果存在就更新,如果不存在就插入。
第一次發佈的時候,文章不存在,直接插入,正常。如果用户正常使用修改接口,修改了正文,因為用户主動提供了docid,所以也能正常更新。
但如果用户先刪除了數據,此時數據庫中,增加了一個字段removed=True。然後用户又原封不動重新發一次文章。那麼docid肯定還是原來那個。這條文章已經在數據庫中存在了。於是逐一更新了每個字段。但是新發布的字段裏面是沒有removed這個字段的,所以更新的時候不會更新它,它還在數據庫裏面。所以就出現了發佈成功,但是打開新聞又提示文章已經刪除。
我去問了一下做這個API的同學,果然它的bug原因跟我設想的一模一樣。
這個bug解決方法非常簡單,發佈新文章的時候,把update_one改成replace_one就可以了:
def post_article(docid, article_info):
mongo.replace_one({'_id': docid}, {'$set': article_info}, upsert=True)
以上就是本次分享的所有內容,想要了解更多 python 知識歡迎前往公眾號:Python 編程學習圈 ,發送 “J” 即可免費獲取,每日干貨分享
- 5 分鐘,快速入門 Python JWT 接口認證
- 5 分鐘,教你用 Docker 部署一個 Python 應用!
- uni-app關閉系統側邊滑動返回的方法總彙
- C 中不一樣的重載
- 字節一面:Redis主節點宕機,如何處理?
- 如何使用 Redis 實現 “附近的人” 這個功能?
- 介紹一款能取代 Scrapy 的爬蟲框架 - feapder
- 直觀講解一下 RPC 調用和 HTTP 調用的區別!
- MySQL 億級數據分頁的優化
- Python 多線程小技巧:比 time.sleep 更好用的暫停寫法!
- Python面試官:請説説併發場景鎖怎麼用?
- Python如何異步發送日誌到遠程服務器?
- Python 中的數字到底是什麼?
- 如何建立一個完美的 Python 項目?
- 詳解 Python 的二元算術運算,為什麼説減法只是語法糖?
- Python 為什麼沒有 main 函數?為什麼我不推薦寫 main 函數?
- Bug分析,假刪除導致文章發佈成功卻打不開的問題
- Python 進階:queue 隊列源碼分析
- Python實例篇:自動操作Excel文件(既簡單又特別實用)
- 誰説程序員不懂浪漫,當代碼遇到文學..