尝试删除数据库文件时发生 EBUSY 错误

Rde*_*ond 5 sqlite node.js

我正在尝试使用 before/after Mocha 挂钩创建然后删除 SQLite 数据库。使用Node.js SQLite 库,我在调用 时收到“错误:EBUSY:资源繁忙或锁定...” fs.unlinkSync(DB_PATH)

之后我可以删除该文件,因此与 Node.js SQLite 库不释放文件描述符有关。如何在同一个程序中创建然后关闭数据库连接并删除数据库文件,或者在删除文件之前程序必须退出吗?代码(Windows 10,Node.js v6.10.0):

var sqlite3 = require('sqlite3');

var fs = require('fs');
var childProc = require('child_process');

before(function() {
    childProc.execSync("sqlite3 ./test_db_data/test_db.sqlite3 < ./load_data/sql/init_model.sql").toString()
});

after(function() {
    fs.unlinkSync(DB_PATH);
});

describe('tests', function() {
    var db;
    before(function(done) {

        // set up database
        new Promise(function(resolve, reject) {
            console.log("Running promise");

            db = new sqlite3.Database(DB_PATH, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, function(err) {
                if (err) {
                    throw new Error("Couldn't create test database.");
                }
                resolve();
            });

        }).then(function() {
            server = app.start(DB_PATH, done);
        }, function(err) {
            console.log("Error opening database");
            done(err);
        });
    });

    after(function(done) {

        //stop server
        server.close();

        // delete database
        db.close(function(err) {
            if (!err) {
                done();
            } else {
                console.log(err);
                throw new Error("Couldn't close database connection.");
            }
        });
    });
Run Code Online (Sandbox Code Playgroud)

Mik*_*ans 2

虽然.close()保证数据库本身在回调时出于读/写目的“关闭”,但不能保证操作系统立即释放文件锁。

处理这个问题最简单的方法就是循环unlink直到成功:

const fs = require("fs/promises");

...

/**
 * Because we're going to have to "wait for a timeout loop"
 * we can't mark this function as an async function, but it is.
 * It instead explicitly returns the promise that "async" normally
 * takes care of for you.
 */
function deleteDatabaseFile(db, filepath, maxRetries = 10) {
  return new Promise((resolve, reject) => {
    const deleteDB = async (retry = 0) => {

      // Make sure we have a safety net: if ten unlink attempts
      // fail in a row, something has *actually* going wrong.
      if (retry > maxRetries)
        return reject(new Error(`Could not delete ${filepath}`));

      if (db) await db.close();
      db = false;

      // Try to delete the file. If we can't, schedule a retry "very soon".
      try {
        await fs.unlink(filepath);
        resolve();
      } catch (e) {
        setTimeout(() => deleteDB(retry + 1), 100);
      }
    };

    // and off we go.
    deleteDB();
  });
}
Run Code Online (Sandbox Code Playgroud)