Firebase Firestore事务内执行普通读取操作是否合法?
关于Firestore事务内混合普通读取操作的合法性解答
你当前的实现是错误的,会触发不可预期的逻辑错误,违反Firestore事务的使用规范。
核心问题说明
- 首先纠正你的认知误区:Firestore的
Transaction对象完全支持传入查询对象执行读取,你认为「使用Transaction对象执行查询不合法」是错误的。你代码中的普通查询完全可以替换为事务读取,只需把const querySnapshot = await query.limit(1).get();改为const querySnapshot = await transaction.get(query.limit(1));即可正常执行。 - Firestore事务有强制顺序要求:所有事务读取操作必须全部放在写入操作之前执行,不管你是否关心读取目标的变更情况。你当前在
transaction.create写入操作执行完成后,才调用updateUser做读取,哪怕把读取改成走事务接口也会直接抛出语法错误。 - 事务内使用普通读取的风险:
- 普通读取无法读到当前事务内还未提交的写入变更,会导致后续
transaction.update的逻辑基于旧数据执行,出现数据一致性问题。 - 普通读取的结果不会被纳入Firestore事务的冲突检测范围,如果你读取的用户文档在事务执行过程中被其他请求修改,事务不会触发重试,最终会写入脏数据。
- 普通读取无法读到当前事务内还未提交的写入变更,会导致后续
正确实现示例
修正后的用户更新逻辑
module.exports = async function updateUser(id, transaction) { // 注意原where语法的运算符需要加引号,修正为"==" const query = firestore .collection("users") .where("id", "==", id); // 改用事务接口执行查询 const querySnapshot = await transaction.get(query.limit(1)); if (!querySnapshot.empty) { transaction.update(querySnapshot.docs[0].ref, { // 此处补充你的更新字段逻辑 }) } }
调整顺序后的事务调用
return firestore.runTransaction(async (transaction) => { /* 所有读取操作统一放在最前面 */ const a = await transaction.get(/* 你的原有读取参数 */) // 提前执行admin用户的查询读取,放在写入前完成 const adminQuery = firestore.collection("users").where("id", "==", ADMIN_ID).limit(1) const adminSnap = await transaction.get(adminQuery) /* 所有写入操作放在全部读取完成之后 */ transaction.create(/* 你的原有创建参数 */) // 执行admin用户更新 if (!adminSnap.empty) { transaction.update(adminSnap.docs[0].ref, { // 此处补充admin用户的更新字段逻辑 }) } });
内容的提问来源于stack exchange,提问作者Raul
相关产品推荐
相关产品推荐

