使用案例
用户将在特定位置(例如Set<ConstraintViolation<SecondaryDTO>> violations = validator.validate(userDTO.getSecondaryDTO());
violations.forEach(violation -> constraintValidatorContext
.buildConstraintViolationWithTemplate(violation.getMessage())
.addConstraintViolation());
)获取门的密码(例如door2
,密码222
)。之后,云功能将从LocationA
文档中删除门并将其添加到empty
文档中。
初始数据库:
occupied
用户获取空门密码后
"LocationA" : {
"empty" : {
"door2" : {
"password" : "222"
},
"door3" : {
"password" : "333"
}
},
"occupied" : {
"door1" : {
"password" : "111"
}
}
}
问题:
如果有2个用户同时获得"LocationA" : {
"empty" : {
"door3" : {
"password" : "333"
}
},
"occupied" : {
"door1" : {
"password" : "111"
},
"door2" : {
"password" : "222"
}
}
}
密码怎么办?这种情况会发生吗?
我希望用户1分别获得door2
而用户2分别获得door2
。
这是我用来启动大门的代码:
door3
更新了Grimthorr回答的基础
// Read Lockers QR User(CRUD)
exports.getQRCode = functions.https.onRequest((req, res) => {
admin.database().ref('lockers/' + 'LocationA/' + 'empty').limitToFirst(1).once("value",snap=> {
console.log('QR Code for door:',snap.val());
var qrCodesForDoor = snap.val();
res.send(qrCodesForDoor);
});
});
答案 0 :(得分:1)
您描述的内容类似于race condition:
软件的行为,其输出取决于其他不可控事件的顺序或时间。当事件没有按程序员的意图发生时,它就会成为一个错误。
这在使用实时数据库时似乎不太可能,特别是在云功能中使用时,但并非完全不可能。
Firebase SDK提供transaction operations,可用于避免并发修改。对于您的方案,使用Node.js中的Admin SDK,您可以执行以下操作:
// Read Lockers QR User(CRUD)
exports.getQRCode = functions.https.onRequest((req, res) => {
admin.database().ref('lockers/LocationA/empty').limitToFirst(1).once("value", (snap) => {
if (!snap.hasChildren()) {
res.send("No doors available.");
return;
}
// Get the name of the first available door and use a transaction to ensure it is not occupied
var door = Object.keys(snap.val())[0]; // The limitToFirst always returns a list (even with 1 result), so this will select the first result
var occupiedRef = admin.database().ref('lockers/LocationA/occupied/'+door);
occupiedRef.transaction((currentData) => {
if (currentData === null) {
console.log("Door does not already exist under /occupied, so we can use this one.");
return snap.val(); // Save the chosen door to /occupied
} else {
console.log('The door already exists under /occupied.');
return; // Abort the transaction by returning nothing
}
}, (error, committed, snapshot) => {
if (error) {
console.log('Transaction failed abnormally!', error);
res.send("Unknown error."); // This handles any abormal error
} else if (!committed) {
console.log('We aborted the transaction (because the door is already occupied).');
res.redirect(req.originalUrl); // Refresh the page so that the request is retried
} else {
// The door is not occupied, so can be given to this user
admin.database().ref('lockers/LocationA/empty/'+door).remove(); // Delete the door from /empty
console.log('QR Code for door:',snapshot.val());
var qrCodesForDoor = snapshot.val();
res.send(qrCodesForDoor); // Send the chosen door as the response
}
});
});
});
这使用您现有的代码来获取下一个可用的门,不同之处在于它只会在/occupied
节点下尚未存在的情况下选择此门。它通过在选择之前使用事务来检查/occupied/door#
节点的值来实现这一点,并应用以下逻辑:
/occupied
下的门不存在,我们可以安全地选择此门,将其保存到/occupied
并从/empty
删除。