You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js中Firestore事务多文档读取实现Token转账原子操作

How to Read Both User Balances in a Firestore Transaction

Great question! The key here is that you need to perform both document reads before executing any writes in your transaction. Since each t.get() returns a Promise, you can use Promise.all() to fetch both user documents in parallel—this is efficient and ensures all required reads are completed first, which is a critical rule for Firestore transactions.

Here's the adjusted code that properly reads both balances before updating:

const db = admin.firestore();
const user1Ref = db.collection('users').doc(user1Id);
const user2Ref = db.collection('users').doc(user2Id);

db.runTransaction(t => {
  // Fetch both user documents simultaneously using Promise.all
  return Promise.all([t.get(user1Ref), t.get(user2Ref)])
    .then(([user1Snap, user2Snap]) => {
      // Extract balances from both snapshots
      const user1Balance = user1Snap.data().balance;
      const user2Balance = user2Snap.data().balance;

      // Optional (but highly recommended): Add balance validation
      if (user1Balance < 100) {
        throw new Error('User 1 has insufficient tokens to complete the transfer');
      }

      // Execute the atomic updates
      t.update(user1Ref, { balance: user1Balance - 100 });
      t.update(user2Ref, { balance: user2Balance + 100 });

      return `Transferred 100 tokens from ${user1Id} to ${user2Id}`;
    });
})
.then(result => {
  console.log('Transaction success', result);
})
.catch(error => {
  console.log('Transaction failed', error);
});

Important Details:

  • Promise.all(): This method waits for both t.get() promises to resolve before moving to the update step, ensuring all reads are done before any writes (Firestore enforces this to maintain transaction integrity).
  • Array Destructuring: The ([user1Snap, user2Snap]) syntax lets you easily unpack the two snapshots from the resolved promise array.
  • Validation Check: Adding a balance check prevents invalid transfers and triggers a transaction failure if the condition isn't met. Firestore will automatically retry transactions in case of conflicts, but this validation stops unnecessary retries for impossible scenarios.
  • Error Handling: The .catch() block ensures you can handle failures like network issues, insufficient funds, or exhausted retries gracefully.

This approach guarantees atomicity: either both balance updates succeed, or neither does, keeping your database data consistent.

内容的提问来源于stack exchange,提问作者Dori

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:57:58