实践数据回滚解决方案

3,452 阅读4分钟

我在大量Nodejs开发实践中,发现回滚是一个非常严谨的话题,我们应该重视这个回滚话题。我们不能祈祷上天程序不要出错,而忽略掉错误处理方案。

比如,我们在开发一个发布系统的过程中,有一系列的操作,数据库CRUD,FS操作项目文件,同时对gitlab的api进行操作。这个时候我们的顺序可能是这样的:

  1. 数据库插入一个项目并且返回一个项目ID
  2. 使用这个项目ID,通过FS操作,命名这个项目的文件夹为project_${id}
  3. 将这个项目文件夹及一下所有的文件上传到一个FTP去
  4. 然后在gitlab中操作这个项目相关详情

我们来脑补下场景:

  1. 如果我们在第1步就出错了,数据库插入就出现问题,可能我们数据库突然挂掉,那么程序就会抛出错误,当然没关系,因为后续操作没有进行。
  2. 如果我们在第2步出现错误,这个文件夹是重名的,那么可能会抛出错误了,但是当时我们没有使用回滚,导致数据库已经插入,但是文件夹内容与我们想要的不匹配。
  3. 如果我们在第3步出错了,数据库已经插入,文件夹已命名,FTP突然挂掉,那么数据库出现了无效数据,文件也出现了无效数据。
  4. 如果我们在第4步出错了,其他都正确,那么我们是否因为前3步出现的操作,导致数据无法同步,文件无法同步,api操作无法同步

那么我们就多么尴尬啊。当然,我们可以通过捕获错误来进行相对应的错误处理。但是我们不知道我们会在哪一步出现而回滚对应的行为。经过我的总结,提出3种回滚模式:

  1. 事务回滚 (mysql, redis, mssql等)
  2. 物理回滚 (主要是fs操作等)
  3. api回滚

我们需要建立一套任务机制,在此机制上面实现2个概念:

  1. task:resolve 任务成功
  2. task:reject 任务失败回调

我们来看下以下的内容。

通用想法

说到任务机制,我们自然会想到如下的模式:

const task = new Tasker();
task.add(async () => {
    await mysql.add({
        a: 1,
        b: 2
    })
});
task.add(async () => {
    fs.writeFileSync('/a', 'aaaaaa', 'utf8');
});

// ....

await task.run();

对,其实想法都是非常正确,再结合我们提出的概念,就变成了如下的写法:

task.add(async function resolve() {
    fs.writeFileSync('/a', 'aaaaaa', 'utf8');
}, async function reject() {
    fs.unlinkSync('/a');
})

确实我们是实现了简单的任务机制。但是我们是否觉得这样的写法非常麻烦,那么我们将引出我们今天的重点模块 ys-dbo

使用ys-dbo解决回滚问题

我们先来看一段代码:

const DBO = require('ys-dbo');
const dbo = new DBO();
// 这里我们假设已经安装了mysql模块
dbo.until(async thread => {
    // 我们把之前的代码实现一下
    const file = '/tmp/test.txt';
    await mysql.begin();
    await mysql.insert('table', {
        a: 1,
        b: 2
    });
    fs.writeFileSync(file, 'aaaaaa', 'utf8');
    thread.on('beforeRollback', async () => fs.unlinkSync(file));
    gitlab.fork('project');
    thread.on('beforeRollback', async () => gitlab.deleteProject('project'));
}).then(...).catch(...)

从上面代码中我们完全可以看出没有任何task.add的痕迹,取而代之的是beforeRollback或者afterRollback等事件。我们通过action+event的模式将任务机制简化了。

所以我们可以看一个图:

回滚机制图

上图明确描述了回滚的机制,我们通过一一对应注册的模式,来管理任务队列。

我现在发现很多同学不爱用回滚,可能嫌弃比较麻烦的写法,这样写出来的代码兼容性不会太好。但是我们要知道回滚其实是数据处于一致性状态的重要手段。我们要重视回滚,不在祈祷上天不要出错。

在我们公司,我也问过很多java开发的同学,很少有人用到回滚,我就很纳闷,作为一位开发工程师,不考虑严谨模式,这是不合格的,回滚也可以看出一个工程师的代码素养。

ys-dbo是什么?

大家不用刻意在意这个模块,毕竟是个人开发实践出来的模块,可能完善度不是很高,但是我会一直维护下去,因为毕竟自己的工作中也常用到。

项目地址:github.com/yskit/ys-db…

npm i ys-dbo

喜欢的同学点个赞吧,感谢!