Firebase 实时数据库 - 使用交易进行配对 = 高下载使用率

Firebase Realtime Database - Matchmaking using transactions = High download usage

问题

我正在使用 Firebase 实时数据库(适用于 Unity)来管理回合制游戏的服务器端,但我的配对有问题...下载使用率很高。

每个在线游戏都有 2 个基本状态,以避免超过 2 个玩家加入游戏:已创建已加入

我正在使用 RunTransaction 来防止超过 2 名玩家加入游戏,但我检查了由于 本地缓存 [= 而未从数据库中获取最新数据42=],在我的 matches-{lang} 子节点上添加 keepSynced 将始终拥有最新数据,但这自然会产生高下载使用率。

private DatabaseReference DatabaseReference()
{
    return FirebaseDatabase.DefaultInstance.RootReference.Child(MatchesLocation(LanguageManager.Manager.GetPlayerLanguageCode()));
}

private DatabaseReference DatabaseReferenceLangMatch(Language language)
{
    return FirebaseDatabase.DefaultInstance.RootReference.Child(MatchesLocation(LanguageManager.Manager.GetLanguageCode(language)));
}

public void ManageKeepSyncedMatches(Language lang)
{
    DatabaseReferenceLangMatch(Language.English).KeepSynced(lang == Language.English);
}

public void JoinMatchTransaction(GameMatchOnline gameMatchOnline, UnityAction<string, bool> callback)
{
    JoinTransactionAbort joinResult = JoinTransactionAbort.None;

    DatabaseReference matchesListRef = DatabaseReference();
    Dictionary<string, object> joinerDict = gameMatchOnline.ToJoinDictionary();

    matchesListRef.Child(gameMatchOnline.matchId).RunTransaction(matchData =>
    {
        Dictionary<string, object> matchDict = matchData.Value as Dictionary<string, object>;
        if (matchDict == null)
        {
            joinResult = JoinTransactionAbort.Null;
            return TransactionResult.Success(null);
        }

        if (!matchDict.ContainsKey("state"))
        {
            joinResult = JoinTransactionAbort.Error;
            return TransactionResult.Abort();
        }
        GameMatchOnline.State state = (GameMatchOnline.State)System.Convert.ToInt32(matchDict["state"]);

        if (state != GameMatchOnline.State.Created)
        {
            joinResult = JoinTransactionAbort.Error;
            return TransactionResult.Abort();
        }

        joinResult = JoinTransactionAbort.None;

        matchDict.Add("joinerInfo", joinerDict["joinerInfo"]);
        matchDict["state"] = joinerDict["state"];
        matchData.Value = matchDict;

        return TransactionResult.Success(matchData);

    }).ContinueWith(task =>
    {
        // Fail
        if (task.IsFaulted || task.IsCanceled)
        {
            UnityThread.executeInUpdate(() =>
            {
                if (joinResult == JoinTransactionAbort.Error)
                {
                    callback(null, false);
                }
            });
        }
        // Can Join match
        else if (task.IsCompleted)
        {
            UnityThread.executeInUpdate(() =>
            {
                if (joinResult == JoinTransactionAbort.None)
                {
                    AddListenerResultsValueChanged(gameMatchOnline.matchId, gameMatchOnline.joinerInfo.userId, gameMatchOnline.isPrivate, gameMatchOnline.language);
                    callback(gameMatchOnline.matchId, true);
                }
                else
                {
                    callback(null, false);
                }
            });
        }
    });
}

问题

  • 删除 keepSynced 玩家将在本地缓存 matches-{lang} 的信息,我可以相信通过这样做每场比赛不会超过 2 名玩家吗? *交易应该避免这种问题。
  • 有没有办法避免请求的本地缓存,从而始终获得更新的数据?
  • 最好的解决方案是将游戏移动到另一个节点以减小 matches-{lang} 节点的大小吗?

谢谢!

Removing "keepSynced" players will have locally cached information for "matches", can I trust that by doing this there will be no more than 2 players per game? *Transactions are supposed to avoid this kind of problem.

关闭 KeepSynced 后,事务仍会访问本地缓存,然后访问 Internet。它可能会为您节省一些带宽,因为它是一种惰性访问(假设您不执行 "get all matches" 之类的操作),并且您将能够做出所需的保证。无论您是否使用 KeepSynced,您都应该为您的事务做好准备 运行 多次(如果本地缓存为空,则针对空数据)。

Is there a way to avoid the local cache for a request and thus always get the updated data?

更正

我好像有点倒退了,有关详细信息,请参阅 。它将 return cached 值并请求一个更新的值。后续调用将在可用时获得新值。如果可能,您应该始终尝试使用 ValueChanged

旧答案:

You _can_ just say `GetValueAsync`, which has to bypass the cache since it will only fire once. You really should use ValueChanged listeners to listen for changes and Transactions to change data if you can to keep everything up to date and to avoid data races.

Could the best solution be to move the games to another node to reduce the size of the "matches" node?

通常访问共享资源的人越少,您的表现就越好。如果您还没有,请查看 Loteria post 以了解一个团队如何在实时数据库上创建一个实时游戏,该游戏的弹性足以成为 Google 涂鸦。

TLDR 是不是玩家负责创建或寻找游戏,而是将寻找游戏的玩家写入队列。当一个玩家被添加到配对队列时,一个 Cloud Function trigger 会触发,它会完成连接用户的工作。客户端通过将 ValueChanged 侦听器放入数据库中的玩家条目并等待将游戏写入其中来知道他们在游戏中。

游戏通过一些手动分片逻辑进一步保持低延迟。他们执行了一些分析以查看单个数据库可以处理多少流量,然后编写一些快速(手动 - 因为这是一天的事情)扩展逻辑以将玩家分配到多个数据库之一。

希望对大家有所帮助!

--帕特里克