昨天,我和在Famigo公司的同事Cody 和 Shaun 一起去参加 MongoDallas研讨会。我们在几个月前听说了这个会议,感到去这个会议将会是次有趣的活动。
我们公司几乎所有的东西都是存储在MongoDB里的,Cody会在这个会议上做一次演讲介绍我们的使用情况。
会议办的非常好,进行的过程中没有出什么意外情况。(跟上次活动一样,10gen公司给会议提供了大量的饮料。)午餐期间,我们跟 GameStop公司的几个家伙侃大山。
其中有个人问我们在正式环境服务器上做过的最糟的一件事情是什么。我想不出什么, 但Cody给大家讲了一个他在以 前的岗位上的一个故事。是他把完全重写的代码放到服务器上后,整个环境立即崩溃了。
可结果却是,我在下午实现了我对生产环境犯下的最大的错误。
午餐之前,在两个演讲之间,我检查了一下我们的服务器,看看是否一切正常。我发现了一个异常,跟保持唯一数据值有关。
我们的API中的一个竞争关系的条件语句导致了数据库中的两个账户保存了相同的email地址,但每个账户的email地址必须是唯一的。
我迅速的定位了问题,在我们的缺陷跟踪系统了添加了一条记录,描述了问题的原因,以及产生冲突的账户。
我删除了这个账户,因为它没有跟任何数据关联 ,我们的客户在下次登录时,系统会自动初始化一条记录。
然后,我继续查找,看看数据库中是否还有其它产生冲突的账户。我循环数据库里的每个账户,依次保持它们(没有做任何改变);
有问题的数据会在保持时抛出异常信息。我在Python的交互shell里编码,所以当时的代码并没有保留下来,但它们大概是这样的:
from mongoengine import connect
from models import Family
connect('the-production-database')
for family in Family.objects:
family.save()
代码执行完并没有出现异常,于是我关掉了笔记本,把注意力重新放到会议上。几个小时后,Cody收到了大量的报告服务器响应变慢的邮件。
他迅速的打开了笔记本,我在旁边看着他的屏幕。当我看到这一幕时,几乎诱发了我的心脏病:
>>> Family.objects.count()
38
这数量少了好几个数量级!我们极度不安,从会议厅里溜了出去。
事情很快就明白了,我们的帐户信息,而且只是帐户信息,被弄丢了。
我查看新近出现的账户信息,把它们加入的时间和我最后一次提交操作的时间对比。它们不可思议的接近。
不幸的是,我的屏幕会话没有足够的回滚信息来让我看看今天早些时间究竟做了什么。
因为我是在交互式shell里执行的,我找不到任何历史记录。最大可能的猜测,我应该是干了类似这样的事情:
for family in Family.objects:
family.delete()
我晕倒!执行save 和 delete操作都不会返回任何信息,所以在看着帐户信息在屏幕上滚动时没有发现任何的异常。(我并不确认究竟是怎么回事,但这是最简单的解释。奥坎氏简化论在这里打倒了我的自负。)
会场的网速很差劲,而且找不到电源插座,于是我们收拾起东西,匆忙的想找一家附近咖啡馆。结果发现,达拉斯市中心所有的咖啡馆下午4点钟全关门了。
幸运的是,它们提供24小时的免费wifi,于是我们就在一家星巴克外面安营扎寨,开始了工作。
(达拉斯这个地方要比我们预想的冷的多。当我们离开奥斯汀时,那里是华氏80度,阳光明媚。而在达拉斯,这里一直40度,阴天,有风。我们三个穿着T恤、牛仔裤的人挤在笔记本前,希望能在冻僵前尽快解决问题。)
这周早期,我们有个数据库备份,我们把它导入到了我们的开发环境中,Cody把它恢复到了一个独立的数据库里。
我写了一个脚本,把丢失的账户信息一一从一个数据库导入另一个数据库。很幸运,进行的很顺利,所有的信息都恢复了。
剩下还有一些要做的事情,要检查所有对这些数据的引用都指向了正确的地方,但最重要的大火是已经被扑灭了。11月17号,它一直保留下来,成为了我们的备份宣传日。
(完)
你有过类似的经历吗?如果是你,你会如何处理这些“误删数据”的失误呢?欢迎留言与运维人一起讨论。
来源:外刊IT评论