【问题标题】:Firebase: structuring data via per-user copies? Risk of data corruption?Firebase:通过每个用户的副本构建数据?数据损坏的风险?
【发布时间】:2016-03-16 20:54:02
【问题描述】:

实现一个 Android+Web(Angular)+Firebase 应用,具有多对多的关系:用户 Widget(Widget 可以共享给多个用户)。

注意事项:

  1. 列出用户拥有的所有小部件。
  2. 用户只能看到共享给他/她的小部件。
  3. 能够查看共享给定小部件的所有用户。
  4. 一个小部件可以由多个具有同等权利的用户拥有/管理(修改小部件并更改它的共享对象)。类似于 Google 云端硬盘与特定用户共享的方式。

实现获取(连接样式)的方法之一是通过多个侦听器遵循以下建议:https://www.firebase.com/docs/android/guide/structuring-data.html ("Joining Flattened Data")。 但是我对这种方法有疑问,因为我发现数据加载会慢得令人担忧(至少在 Android 上)——我在另一个问题中问过这个问题——Firebase Android: slow "join" using many listeners, seems to contradict documentation

所以,这个问题是关于另一种方法的:用户拥有的所有小部件的每个用户副本。与 Firebase+Udacity 教程“ShoppingList++”(https://www.firebase.com/blog/2015-12-07-udacity-course-firebase-essentials.html)中使用的一样。

它们的结构如下所示:

特别是这部分 - userLists:

  "userLists" : {
    "abc@gmail,com" : {
      "-KBt0MDWbvXFwNvZJXTj" : {
        "listName" : "Test List 1 Rename 2",
        "owner" : "xyz@gmail,com",
        "timestampCreated" : {
          "timestamp" : 1456950573084
        },
        "timestampLastChanged" : {
          "timestamp" : 1457044229747
        },
        "timestampLastChangedReverse" : {
          "timestamp" : -1457044229747
        }
      }
    },
    "xyz@gmail,com" : {
      "-KBt0MDWbvXFwNvZJXTj" : {
        "listName" : "Test List 1 Rename 2",
        "owner" : "xyz@gmail,com",
        "timestampCreated" : {
          "timestamp" : 1456950573084
        },
        "timestampLastChanged" : {
          "timestamp" : 1457044229747
        },
        "timestampLastChangedReverse" : {
          "timestamp" : -1457044229747
        }
      },
      "-KByb0imU7hFzWTK4eoM" : {
        "listName" : "List2",
        "owner" : "xyz@gmail,com",
        "timestampCreated" : {
          "timestamp" : 1457044332539
        },
        "timestampLastChanged" : {
          "timestamp" : 1457044332539
        },
        "timestampLastChangedReverse" : {
          "timestamp" : -1457044332539
        }
      }
    }
  },

如您所见,购物清单"Test List 1 Rename 2" info 的副本出现在两个地方(2 个用户)。

为了完整起见,其余部分如下:

{
  "ownerMappings" : {
    "-KBt0MDWbvXFwNvZJXTj" : "xyz@gmail,com",
    "-KByb0imU7hFzWTK4eoM" : "xyz@gmail,com"
  },
  "sharedWith" : {
    "-KBt0MDWbvXFwNvZJXTj" : {
      "abc@gmail,com" : {
        "email" : "abc@gmail,com",
        "hasLoggedInWithPassword" : false,
        "name" : "Agenda TEST",
        "timestampJoined" : {
          "timestamp" : 1456950523145
        }
      }
    }
  },
  "shoppingListItems" : {
    "-KBt0MDWbvXFwNvZJXTj" : {
      "-KBt0heZh-YDWIZNV7xs" : {
        "bought" : false,
        "itemName" : "item",
        "owner" : "xyz@gmail,com"
      }
    }
  },
  "uidMappings" : {
    "google:112894577549422030859" : "abc@gmail,com",
    "google:117151367009479509658" : "xyz@gmail,com"
  },
  "userFriends" : {
    "xyz@gmail,com" : {
      "abc@gmail,com" : {
        "email" : "abc@gmail,com",
        "hasLoggedInWithPassword" : false,
        "name" : "Agenda TEST",
        "timestampJoined" : {
          "timestamp" : 1456950523145
        }
      }
    }
  },

  "users" : {
    "abc@gmail,com" : {
      "email" : "abc@gmail,com",
      "hasLoggedInWithPassword" : false,
      "name" : "Agenda TEST",
      "timestampJoined" : {
        "timestamp" : 1456950523145
      }
    },
    "xyz@gmail,com" : {
      "email" : "xyz@gmail,com",
      "hasLoggedInWithPassword" : false,
      "name" : "Karol Depka",
      "timestampJoined" : {
        "timestamp" : 1456952940258
      }
    }
  }
}

但是,在我开始在我的应用中实现类似的结构之前,我想澄清一些疑问。

这是我的相关问题:

  1. 在他们的 ShoppingList++ 应用程序中,他们只允许一个“所有者” - 在 ownerMappings 节点中分配。因此没有其他人可以重命名购物清单。我想拥有多个具有平等权利的“所有者”/管理员。这种按用户保留副本的结构是否仍然适用于多个所有者/管理员用户,而不会有数据损坏/“去同步”或“恶作剧”的风险?
  2. 在以下情况下可能会出现数据损坏:User1 脱机,将 Widget1 重命名为 Widget1Prim。当 User1 处于离线状态时,User2 将 Widget1 共享给 User3(User3 的副本还不知道重命名)。 User1 上线并发送有关 Widget1 重命名的信息(仅发送给他自己和 User2 的副本,客户端代码在重命名时知道这些信息 - 不更新 User3 的副本)。现在,在一个简单的实现中,User3 将使用旧名称,而其他用户将使用新名称。这可能很少见,但还是有点担心。
  3. 可能/应该出现第“2”点中的数据损坏情况。通过让某些进程(例如在 AppEngine 上)监听更改并确保正确传播到所有用户副本来解决?
  4. 和/或可能/应该出现第“2”点中的数据损坏情况。通过实现对共享和重命名更改的冗余侦听以及将更改传播到每个用户副本来解决特殊情况?大多数时候这不是必需的,因此可能会导致性能/带宽损失和复杂的代码。值得吗?
  5. 展望未来,一旦我们“在野外”部署了多个版本,考虑到客户端中的代码承担了多少数据处理责任,进化模式是否会变得笨拙?例如,如果我们添加一个旧客户端版本还不知道的新关系,它看起来不脆弱吗?然后,回到服务器端同步器-确保器进程,例如AppEngine(在问题“3.”中描述)?
  6. 为每个小部件/购物清单提供一个“主参考副本”,以便为任何会更新- 用户副本?
  7. 对于以这种(冗余)方式结构化的数据的 rules.json / rules.bolt 权限是否有任何特殊注意事项/陷阱/阻止程序?

PS:我通过updateChildren() 了解原子多路径更新 - 肯定会使用它们。

欢迎任何其他提示/观察。 TIA。

【问题讨论】:

  • “我会重视对 ShoppingList++ 示例的一般判断 - 这是值得效仿的示例还是我应该持怀疑态度。”这将是非常广泛和高度主观的,所以我倾向于投票结束。您可能想要删除它。
  • 一篇文章中有 7 个问题太多了。它也方式太宽泛了。请将其缩小到一个可以在合理的时间/空间内回答的问题。
  • 我的头爆炸了。但是,帖子中有一些很好的问题。我要求 OP 将问题缩小到每个帖子一个,并限制问题、示例代码和 Firebase 结构的范围。这样做,我们可以提供帮助。
  • @FrankvanPuffelen - 我已经删除了一般询问 ShoppingList++ 可信度的部分。其余的问题应该仍然成立。除了一些人建议的帖子中有太多问题之外 - 我将尝试拆分。

标签: firebase database-schema angularfire firebase-realtime-database


【解决方案1】:

我建议为整个系统只保留一个小部件副本。它将有一个原始用户 ID,以及一组有权访问它的用户。小部件树可以保存用户权限和更改历史记录。每次进行更改时,都会向树中添加一个分支。然后可以将分支“提升”为类似于 GIT 的“主”。这将保证数据的完整性,因为过去的版本永远不会更改或删除。它还可以简化您的获取...我认为 :)

{ 
  users:[
    bob:{
      widgets:[
        xxx:{
           widgetKey: xyz,
           permissions: *,
           lastEdit... 
        }
      ]
    }
    ...
  ]
  widgets:[
    xyz:{
       masterKey:abc,
       data: {...},
       owner: bob,
    },
    ...
  ]
  widgetHistory:[
    xyz:[
      v1:{
         data:{...},
      },
      v2,
      v3
    ]
    123:[
       ...
    ],
    ...
  ]
 }

【讨论】:

  • 谢谢。但是我如何有效地读取给定(当前登录的)用户拥有的所有小部件?您可以粘贴一些带有示例结构的 json-s 吗?有类似方法的链接吗?顺便说一句,您使用 Firebase 有多久了?
  • 您可以保留一个单独的参考 ID 树,以便该用户直接访问小部件。
  • 谢谢。我已经考虑过这种方法(除了版本控制的想法),但它似乎太慢了(通常只有 100 个项目需要 6 秒)-stackoverflow.com/questions/35996865/…
  • masterKey:abc 是什么?
  • 这将是当前正在使用的版本的关键。
猜你喜欢
  • 1970-01-01
  • 2021-10-21
  • 2015-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-21
  • 2017-07-26
相关资源
最近更新 更多