Node.js SQLite异步事务

我使用node-sqlite3,但我确信这个问题也出现在另一个数据库中.我在代码中发现了混合事务和异步代码的错误.

function insertData(arrayWithData, callback) {
    // start a transaction
    db.run("BEGIN", function() {
        // do multiple inserts
        slide.asyncMap(
            arrayWithData,
            function(cb) {
                db.run("INSERT ...", cb);
            },
            function() {
                // all done
                db.run("COMMIT");
            }
        );
    });
}

// some other insert
setInterval(
    function() { db.run("INSERT ...", cb); },
    100
);

你也可以运行the full example.

问题是,在开始或插入后的异步暂停期间,可以启动一些带有插入或更新查询的其他代码.然后在事务中运行此额外查询.提交事务时,这不是问题.但是,如果事务被回滚,则此额外查询所做的更改也会回滚.我们只是不可预测地丢失数据而没有任何错误消息.

我想到了这个问题,我认为一个解决方案是创建一个包装类,以确保:

>只有一个事务同时运行.
>当事务运行时,只执行属于事务的查询.
>所有额外查询在当前事务完成后排队并执行.
>当一个事件已经运行时,所有尝试启动事务也将排队.

但这听起来像是太复杂的解决方案.有更好的方法吗?你是如何处理这个问题的?

最佳答案
首先,我想说我没有使用SQLite的经验.我的回答是基于对node-sqlite3的快速研究.

您的代码IMHO的最大问题是您尝试从不同位置写入数据库.据我了解SQLite,您无法控制PostgreSQL中的不同并行“连接”,因此您可能需要将所有与DB的通信包装起来.我修改了你的例子以使用always insert包装器.这是修改后的功能:

function insertData(callback, cmds) {
  // start a transaction
  db.serialize(function() {
    db.run("BEGIN;");
    //console.log('insertData -> begin');
    // do multiple inserts
    cmds.forEach(function(item) {
      db.run("INSERT INTO data (t) VALUES (?)", item, function(e) {
        if (e) {
          console.log('error');
          // rollback here
        } else {
          //console.log(item);
        }
      });
    });
    // all done
    //here should be commit
    //console.log('insertData -> commit');
    db.run("ROLLBACK;", function(e) {
      return callback();
    });
  });
}

使用以下代码调用函数:

init(function() {
  // insert with transaction
  function doTransactionInsert(e) {
    if (e) return console.log(e);
    setTimeout(insertData, 10, doTransactionInsert, ['all', 'your', 'base', 'are', 'belong', 'to', 'us']);
  }

  doTransactionInsert();

  // Insert increasing integers 0, 1, 2, ...
  var i=0;

  function doIntegerInsert() {
    //console.log('integer insert');
    insertData(function(e) {
      if (e) return console.log(e);
      setTimeout(doIntegerInsert, 9);
    }, [i++]);
  }

  ...

我做了以下更改:

>添加了cmds参数,为简单起见,我将其添加为最后一个参数,但回调应该是最后一个(cmds是插入值的数组,在最终实现中它应该是一个SQL命令数组)
>将db.exec更改为db.run(应该更快)
>添加了db.serialize以序列化事务内的请求
> BEGIN命令的ommited回调
>省略幻灯片和一些下划线

您的测试实现现在对我来说很好.

转载注明原文:Node.js SQLite异步事务 - 代码日志