记一次bug解决经历

Bug概要

前两天freenom域名忘了续期,结果变成一年60RMB了,心里想想还有点亏,算了,还是另找个免费的域名吧,只可惜我的project里的域名都不能用了,写的一堆软件都失效了。今天在改项目的时候还发现了一个巨大的问题,发现坑太深,算是我大学3年个人独立做的最大的项目了,逻辑混乱的不行,索性写写整理整理思路,也给自己以后看看吧,加深一下对自己垃圾项目的理解,别到时候忘了。

背景

这是我的一个自用笔记项目,所以我既是开发者也是用户,本着付出最少的努力获得体验最佳的产品,必要时两个角色之间相互折衷。
现在我有一批文件,他们的内容是Json格式的思维导图文件,里面有很多树状的节点。
我为了提高文件节点的搜索速度,节约遍历文件的io时间,在Json每次同步的时候做节点遍历扫描并插入数据库的操作,即牺牲上传的时间来提高检索的速度。这就需要文件夹内容和数据库的内容一致。
那么如何解决这个同步问题?
一个简单的想法就是捕获用户的增加,修改删除操作,然后执行对应的数据库代码。但是为了应对复杂的场景,比如用户不通过本软件系统,而是在文件系统级别直接添加或者删除文件,那么这个软件项目就捕获不到事件了,无法感知也就无法在数据库中正确插入删除数据。
经过思考,我决定写一个文件扫描器,每次和数据库同步之后把目录中的文件拷贝到一个镜像目录中,确保这个镜像目录和数据库的内容始终是一致的,下一次同步的时候就和上次的镜像目录对比文件差异,查看哪些文件发生了改变,这样即使不通过本软件操作修改的文件也可以察觉到差异,从而正确同步到数据库中。
在这里插入图片描述
这样的逻辑通过比较两个File系统的一致性可以得出哪些文件发生了删除、哪些文件是新添加的。基于这种逻辑,修改文件的数据库写入逻辑就变成了先删除后增加

遇到的问题

上述逻辑对于节点的搜索问题已经解决了,节点的内容能够正确的同步到数据库中,即使用户暴力操作,它也是不受影响的。但是现在我有一个想法,就是在A–>B的时候,B发生了移动,A能够感知到并且把A内的路径指向B迁移后的路径,类似于IDE里的Refactor操作。
由于文件移动对于这个软件系统的感知方式是先删除再添加,这样的话文件就会删除并重新注册,文件的register id就发生了变化(使用autoincrement)。原先文件就找不到迁移后的B文件了。

以上的代码设计属实有问题,由于历史原因吧,当时没考虑这么多东西,把当时想到功能直接上手去做并实现了,面对新需求很难前进。渐渐发现这就是软件工程课程上遇到的问题啊,随着软件的庞大可能会陷入泥潭难以逃出。面对庞大的历史代码,我无法从底层去推翻它,而是尽量顺着它走,也可能越走越是走向错误的方向,但我仍然不想舍弃之前的代码重写,因为这更看不到希望,况且我觉得这个软件虽然有用,但也没有那么大的价值,就很纠结吧。

很多东西看起来是错误的,比如文件 修改 变成了删除+增加,导致后续找不到id关联,AB关联链接丢失的现象。

用户可能的谜之操作

把文件复制一遍,到别的文件夹下面(记录某个文件的某个版本),导致id重叠。
批量引入一批中文的、不同目录的重名文件。
把不符合规范的、非本项目的Json文件引入文件夹中。

历史代码导致项目受到的限制

  • 修改操作必须是通过删除+添加来实现(这是最错误的一步,但也降低了软件开发难度)
    删除操作会导致数据库节点删除,添加时重新添加入数据库中,以此来表示数据的修改;文件的文字内容肯定是没有变化的(笔记该是什么文字还是什么文字),思维导图node id也不会发生变化,但是注册文件是新注册的,用的是Autoincrement方式赋值的id,因此每次都会是新的文件register id(关于这个register id为什么不使用固定的,例如文件路径、或者用uuid做文件名来表示,我也做过很多考虑,比如文件移动后怎么办,同一个uuid在不同路径又怎么办?到时候基于删除+添加的方式这个注册文件不还是删除了?);另外,这样操作是有好处的,不管什么文件、多少文件修改了还是删除了,可以统一操作,代码实现上也非常的方便快捷。

  • 由于sqlite限制,所有访问db的操作都要加锁
    虽然实现很不优雅,但是自用性能基本不受影响,也是sqlite的限制。使用sqlite也有好处,就是迁移文件就是对数据库的迁移,非常的方便。尤其是对我这种vps经常需要更换的人来说。

  • 现有功能已经相当完善,希望进行尽可能少的更改(像极了产品经理的需求,把xxx功能粘贴过去就行了)
    现在已经有了refactor的功能,即B文件移动之后系统会自动修改A文件,但是更核心的问题不在这里,如果link双方的文件id始终不变,被链接的node id始终不变,这件事就简单的解决了

需求

需求很简单,就是在Json思维导图文件A–>B链接B的时候,无论A或者B移动或者修改,都能找到对方,除非一方已经消失。但是由于项目历史限制,这个需求实现非常困难。小团队或者个人开发的project,没有厉害的架构师很容易遇到这种问题(所以找工作还是得找个大厂大团队嘛)。

尝试解决办法

文件名作为register id,其中文件名为uuid

存在的问题

如果用户把文件拿去复制到另一个文件夹,uuid就会变成两个,和原本的意思不符。

把文件内的一个不可修改的字段作为uuid,一旦创建永远不能修改

存在的问题

同上,如果用户复制,仍然存在uuid出现两次的问题

总之,用户自己复制的文件这个问题几乎不可能解决,在后台查看的情况下,这两个就是一摸一样的文件。区别他们的方法就是文件的时间戳。这个地方的处理又该放在什么时机呢?说到底,我就是想要同一个文件修改之后获得一个id一样的东西,人为修改和我代码后台修改又是无法区分的东西。所以,讨论到这里,面对用户复制的行为,这是个无解的问题。无论怎么做都软件系统都无法感知到。难道说,要给用户一个警告,不要复制?

增加限制:用户不能在文件的层次上直接进行复制同名文件等危险的操作,否则后果自负。
即:允许用户在一定程度上通过后台移动文件的方式为数据库下次扫描的时候增加数据,但是不是无限度的。

采用限制用户行为的方法

  • 复制文件
    坚决不允许,后果自负
    虽说不允许,但还是可能出现用户误操作复制文件的情况,导致json内id重复,考虑以下情况:
    • 用户本意是移动文件,但是先复制了,这种行为应该禁止,用户完全可以意识到
    • 数据崩溃,导入历史文件恢复时出错
      解决办法:采用专用的数据恢复工具恢复,禁止直接导入,这个等数据崩溃的时候再做(现有的数据备份机制也有点问题)
  • 移动文件
    可以,而且软件系统会对这方面重点检测,确保逻辑跑通
  • 删除文件
    走正常删除逻辑,反正都删除了,数据丢失无所谓了
  • 直接修改文件
    不太可能,json格式用户难以操作,否则后果自负

数据迁移

制作一个工具,把数据库里的register file id 全部存入json中去,然后软件察觉到文件改变会进行扫描回写数据库中。此时链接数据全部丢失,但是保存在了json中。
此时需要修改数据库扫描逻辑代码,具体操作就是如果json文件里面存在register file id ,就把他存入数据库中,而不再使用autoincrement 操作。如果没有,则赋值一个数字(如何确保不重复?即我存入数据库的字段怎么确保本轮扫描没加入的json不会和"我"重叠?)。那就增加一个id表,仿造autoincrement机制(其实他也把nextid存在数据库里,然后下次自增直接用了),由于这个id是复用的,增速比较慢,所以不用担心overflow的问题。

jumplink删除行为 改为 source被删除则pair一定删除(如果回来它会自己加上),target被删除则不删除(因为不知道是否会回来),可能导致target冗余(不会导致source无法插入的情况,因为source插入别的文件之前一定会删除,因为发生了修改操作)。真正删除的target无法感知,可以利用闲余时间进行碎片数据的清理(检查文件是否存在,因为 修改=删除+增加 的动作只是一瞬间)。

pair就是(source,target)

代码实现和工作量估计

由于实现是需要时间的,我需要尽快使用并实现正常记笔记功能,优先解决简单的、重要的功能模块
以下工作都要线下进行,避免触发db扫描程序

  • 文件id导入json数据中 20m,使得json和id始终绑定,之后db会自动扫描并加入到错误的数据到db中,如果希望迁移后的数据能打到json和id全体一致性,应该禁用或者躲避db的扫描行为。
  • 数据库id插入、json文件检验程序 20m ,用上面数据迁移的方式把DB注册文件的逻辑改了
  • 文件批量替换程序 20m,用于把域名迁移过来,之后数据库会自动进行修改的文件扫描,由于json和id已经绑定的原因,链接包含id已经保存,保证双方文件都不会丢失

看样子这三步是固定的,无法调整顺序

最终部署调试

服务器重启后,需要及时的监视问题,防止后续 更大的错误。

之后尝试重新运行,果然,没有通过正式调试不可能直接运行的,还是有bug。
移动的时候可能有bug,但是按照上面讨论的逻辑,由于id是始终保存在json文件中的,一旦出生一直跟随下去,所以文件移动不需要手动refactor了,link所指向的两个文件id始终不会发生变化。

完全可以克服移动文件的问题、以及文件修改的问题。
但是先修改再移动就会必定出错。

为什么先修改再移动不行呢,是因为什么东西没有绑定上吗。
仔细分析数据之后发现,json的非kittyminder字段会被删掉,也就是register_file_id全都消失了,短短的几个测试文件下来,注册文件的id就增长到了30068(初始值30000),竟然新增了60次文件,并没有完成id复用。
在这里插入图片描述
改变策略,换成为root里面的一个属性,root值经过测试后是永远都不会删除的。

一切大功告成,这样就完成了域名迁移工作并且修复了refactor的bug。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值