實踐資料回滾解決方案

沈贇傑發表於2019-03-01

我在大量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
複製程式碼

喜歡的同學點個贊吧,感謝!

相關文章