我使用
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回调
>省略幻灯片和一些下划线
您的测试实现现在对我来说很好.