【问题标题】:Firebase realtime database transactions sometimes take too long to completeFirebase 实时数据库事务有时需要很长时间才能完成
【发布时间】:2019-02-21 03:39:19
【问题描述】:

我有一个在单个服务器上运行的 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 实时数据库?

【问题讨论】:

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


    【解决方案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 启用调试日志记录,并尝试更深入地了解这些长时间延迟期间发生的情况。我还建议尝试使用事务实现常规数据更新操作,并完全消除分布式锁。

    【讨论】:

    • 感谢您的回答,但在这种情况下,锁争用似乎不是原因。我的日志显示,当单个锁定花费异常多的时间时,没有其他锁定尝试。另外,我通过一次发送多个客户端请求来测试锁,并且服务器设法很快地顺序执行它们(由于我的客户端应用程序的性质,同时请求很少,所以我没有同时测试大量请求)。
    • 另一种可能性是服务器故障转移。您看到的延迟可能是 SDK 检测服务器故障以及故障转移到池中的另一台服务器所需的时间(尽管大约 30 秒听起来太长了)。 SDK调试日志应该可以确认。
    • 谢谢。我将启用日志并尝试找出问题所在。
    猜你喜欢
    • 1970-01-01
    • 2018-02-07
    • 2016-07-30
    • 2013-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多