共计 1769 个字符,预计需要花费 5 分钟才能阅读完成。
公司有一个外部博客,大家能够在下面创立本人的账号,而后写文章在全公司分享。昨天这个外部博客开明了 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”即可收费获取,每日干货分享