Firebase实时数据库事务有时需要很长时间才能完成

时间:2019-02-20 17:37:10

标签: node.js firebase firebase-realtime-database firebase-admin

我有一个在单个服务器上运行的nodejs应用程序。我需要在多台服务器上运行该应用程序以实现负载平衡。

此应用一次仅处理来自特定用户的单个请求。如果收到来自用户的同时请求,则将请求排队并依次执行。这样做是为了保持数据库中某些数据的一致性,如果应用程序同时处理请求,数据将被破坏。

由于我要在多台服务器上运行该应用程序,因此我需要一种方法来防止服务器处理来自单个用户的同时请求。为此,我使用了Firebase实时数据库来构建分布式锁。以下是我的代码的简单版本。

function lockUser(user) {
    return firebaseAdmin.database().ref('users/' + user + '/lock').transaction((currentData) => {
        if (currentData === null || currentData.lockTime === 0) {
            return {'lockTime': Date.now()};
        }
    }, null, false).then(async (result) => {
        if (result.committed) {
            return Promise.resolve();
        }
        log.info('failed to lock ' + user + '. retrying.');
        await sleepFor(500);
        return lockUser(user, user, res);
    }).catch(async (reason) => {
        log.info('lock failed. ' + user + '. reason: ' + reason + '. retrying');
        await sleepFor(500);
        return lockUser(user, user, res);
    });
}

function unlockUser(user) {
    log.info('unlocking firebase lock. ' + user);
    firebaseAdmin.database().ref('users/' + user + '/lock').set({'lockTime': 0}, (error) => {
        if (error) {
            log.warn('failed to unlock ' + user + '. error: ' + util.inspect(error));
        } else {
            log.info('unlocked ' + user);
        }
    });
}

使用以上代码,锁定通常需要大约100毫秒,并且大部分时间都是一致的。但是很少有时候,我发现完成交易的时间大大延迟。发生这种情况时,锁定可能需要大约30秒钟。

这种延迟的原因可能是什么?有什么理由不应该以这种方式使用Firebase实时数据库吗?

1 个答案:

答案 0 :(得分:1)

RTDB事务本质上是比较设置操作。如果在事务进行过程中数据库的状态发生变化,则SDK会使用数据库的新状态重试事务。在您的情况下,当多个进程争用该锁时,可能会发生这种情况:

  1. P1尝试获取锁,看到currentData.lockTime = 0,并决定对其进行更新。
  2. P2尝试获取该锁,看到currentData.lockTime = 0,并决定对其进行更新。
  3. P1提交交易。
  4. P2认为自上次读取数据以来,该数据已发生更改,因此它重试了该事务。

现在有一种病理情况,在P2重试交易之前,P1放弃了其锁定。因此,P2将再次看到currentData.lockTime = 0,并尝试再次获取该锁。但是,如果另一个进程P3从P2下获取了锁,则此尝试也可能失败。因此,重试循环继续。

在最坏的情况下,交易可以重试到25 times

我不确定这是否是您的情况。但这绝对是一种解释。也许启用SDK的调试日志记录,并尝试更深入地了解这些长时间的延迟情况。我还建议您尝试使用事务来实现您的常规数据更新操作,并完全消除分布式锁定。