Firebase Firestore:存储由外部源生成的单调递增的ID 关于我正在努力实现的目标的背景

Firebase Firestore:存储由外部源生成的单调递增的ID 关于我正在努力实现的目标的背景,firebase,google-cloud-firestore,Firebase,Google Cloud Firestore,我正在为应用程序实施应用程序内购买验证。理论上,应执行以下步骤,以避免多次向用户交付相同的购买: 应用程序以购买验证数据为参数调用Firebase云函数 云函数调用相应的谷歌服务器或苹果服务器来检查购买是否有效 云功能检查购买是否尚未交付给用户 云功能向用户交付购买 为了实现步骤3,我考虑使用Firestore集合来存储所有已交付的购买信息。它的结构应该是这样的: Firestore root | |-> purchases (Collection) |

我正在为应用程序实施应用程序内购买验证。理论上,应执行以下步骤,以避免多次向用户交付相同的购买:

  • 应用程序以购买验证数据为参数调用Firebase云函数
  • 云函数调用相应的谷歌服务器或苹果服务器来检查购买是否有效
  • 云功能检查购买是否尚未交付给用户
  • 云功能向用户交付购买
  • 为了实现步骤3,我考虑使用Firestore集合来存储所有已交付的购买信息。它的结构应该是这样的:

    Firestore root
      |
      |-> purchases (Collection)
            |
            |-> {purchaseID} (Document)
                  |
                  |-> sku: String (What was bought?)
                  |-> uid: String (Who bought it?)
    
    purchaseID应该是订单ID(对于在Android应用程序上进行的应用内购买)或交易ID(对于在iOS应用程序上进行的应用内购买)。如果您不熟悉这些ID:谷歌订单ID类似于
    GPA.1234-5678-9012-34567
    ,而苹果交易ID的格式类似于
    123456780123456

    在测试期间,我注意到订单ID看起来是随机的(即,它们既不是单调递增的,也不是单调递减的),但事务ID似乎是单调递增的。根据Firestore的和以及,使用单调递增的ID是一种反模式,因为当每秒写入超过500次时,这会导致问题。虽然我不希望达到每秒500次的应用程序内购买(我当然怀疑是否有任何应用程序达到如此高的购买率),但我仍然希望避免使用反模式

    进一步阐述为什么需要存储这些单调递增的ID 为了避免多次向用户交付相同的应用内产品,我需要跟踪已经交付的购买。让我试着通过分享我们正在开发的应用程序的更多见解来解释这一点:该应用程序允许用户购买5张、10张或20张额外的支持票,从而允许他们提出相应数量的问题。为了实现这一点,每个用户的用户文档都有一个整数,表示用户可能会问多少剩余问题

    假设一个使用iOS设备的用户购买了10张票。应用程序使用应用程序内购买的验证数据(基本上是一个很长的base64编码字符串)调用Firebase云函数来验证购买并将购买传递给该用户。云功能首先调用苹果服务器来检查验证数据是否有效。Apple服务器用一个JSON对象响应,该对象包含事务ID 23的购买详细信息(为了更好的可读性,在本例中ID被缩短)。之后,云功能需要确定与事务ID 23相关的10张票据是否已经交付。如果这10张票尚未交付,我们将更新用户文档,以反映该用户现在可以再问10个问题。此外,我们需要跟踪事务ID 23现在已交付。为了确保以原子方式执行所有这些操作,在一个事务中执行上述检查和更新

    现在让我们假设稍后同一用户又购买了10张票。同样,应用程序将调用云功能来验证和交付购买。这一次,苹果服务器用一个JSON对象进行响应,该对象包含关于交易ID 23(已交付的旧购买)和交易ID 36(新购买)的详细信息。为了正确地向用户计数器添加10张票据(而不是20张),我们需要检测到交易ID为23的购买已经交付给用户,因此需要以某种方式存储交易ID

    您现在可能想知道,是否只交付最新的购买(即具有最高交易ID的购买)是一个可行的解决方案。可悲的是,这可能会导致一些购买可能无法正确交付的问题。假设用户完全退出应用程序(即应用程序不再在后台运行),直接通过应用程序商店购买15张票(购买10张和5张票),然后再次打开我们的应用程序。该应用程序现在会注意到这两次购买,并调用云功能两次。在这两次调用期间,云功能将交付最新的购买,即5张票的捆绑。用户只收到10张票,而不是额外收到15张票

    广义问题和我想到的可能(非)解决方案 基于我的具体案例,出现了一个普遍的问题:如何存储从外部源生成的单调递增的ID

    以下是我的一些想法和想法:

  • 使用Firestore生成的ID,将外部ID作为字段存储在文档中,并按照中的说明使用分片。虽然对于大多数用例来说,这是一个很好的可扩展解决方案,但它可能并不适用于所有人。以我的用例为例:为了避免多次向用户交付采购,必须在一个事务中执行步骤3(检查采购是否已交付)和步骤4(交付采购)。但是,确定是否存在具有特定外部ID的文档需要n个查询(其中n是碎片的数量),这是非常重要的
  • 排列外部ID的数字值,并使用该排列的ID作为文档ID。例如,每个1可能被排列为8,每个2可能被排列为4,每个3可能被排列为7,依此类推。虽然这会导致相邻ID之间的间隔稍远,但这无助于防止热点,因为相似的ID不会分散得很远。此外,如果您需要在外部ID上订购(在我的