云开发数据库的事务处理

云开发数据库文档中其实有一些事务处理的指引和demo,不过基本都是await风格的,只能在异步函数里面使用。

有的时候希望用『同步函数+callback』的方式代替await来实现更好的并发执行能力,那就需要用promise的编程风格了,写了个demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
exports.main_handler =  (event, context , callback) => {
let collectionName = "test",commitState = false, n = 500;
db.startTransaction().then(transaction=>{
transaction.collection(collectionName).add([
{_id:Math.floor(Math.random()*n),a:1},
{_id:Math.floor(Math.random()*n),a:2},
]).then(res=>{
return transaction.collection(collectionName).add({_id:Math.floor(Math.random()*n),b:2})
}).then(res=>{
return transaction.collection(collectionName).add({_id:Math.floor(Math.random()*n),b:2})
}).then(res=>{
commitState=true;
return transaction.commit()
}).then(res=>{
callback(null,{code:0,msg:"事务提交成功: "+JSON.stringify(res)})
},rej=>{
if(commitState){
transaction.rollback().then(res=>{
callback(null,"提交失败,回滚成功")
},rej=>{
callback(null,"提交失败,回滚失败")
})
}else{
callback(null,"事务创建失败,尚未提交,无需回滚")
}
})
},()=>{
console.log("开启事务失败")
callback(null,"开启事务失败")
})
};

代码中刻意使用了500内的随机整数来当id来制造随机的id冲突以随机初发事务的失败。

其实如果能用Promise.all的化代码还能更好看些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 以下代码会触发云开发数据库sdk的bug */
db.startTransaction().then(transaction=>{
for(var i=0;i<5;i++){
p.push(transaction.collection(collectionName).add({_id:Math.floor(Math.random()*n),a:3}))
}
Promise.all(p).then(res=>{
transaction.commit().then(res=>{
callback(null,{code:0,msg:"事务提交成功: "+JSON.stringify(res)})
},rej=>{
transaction.rollback().then(
res=>{
callback(null,"提交失败,回滚成功")
},rej=>{
console.log(rej)
callback(null,"提交失败,回滚失败")
})
})
},rej=>{
console.log(rej)
callback(null,"事务创建失败,尚未提交,无需回滚")
})
},()=>{
callback(null,"开启事务失败")
})

但是云开发数据库的sdk不支持这么玩。Promise.all里的数据库操作一多起来,就有一定的概率触发这样的错误:

TcbError: [ResourceUnavailable.TransactionBusy] Transaction is busy.

暂时只能用上面的一步一步then的方式来执行了,或者用网上流行的Array.reduce的方式来让Promise排队执行。