Firebase:通过每用户副本构建数据?数据损坏风险?

时间:2016-03-16 20:54:03

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

实施具有多对多关系的Android + Web(Angular)+ Firebase应用:用户< - >窗口小部件(窗口小部件可以与多个用户共享)。

考虑:

  1. 列出用户拥有的所有小部件。
  2. 用户只能看到与他/她共享的小部件。
  3. 能够看到共享给定Widget的所有用户。
  4. 单个Widget可以由具有相同权限的多个用户拥有/管理(修改Widget并更改共享对象)。与Google云端硬盘与特定用户共享的方式类似。
  5. 实现抓取(join-style)的方法之一是通过多个侦听器来使用此建议: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"信息的副本出现在两个地方(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. 展望未来,一旦我们“在野外”部署了多个版本,考虑到客户端中的代码有多少数据处理责任,发展架构会不会变得难以处理?例如,如果我们添加一个新的关系,旧的客户端版本还不知道,它看起来不脆弱吗?然后,回到例如服务器端的syncer-ensurerer进程。 AppEngine(在问题“3.”中描述)?
    6. 对于每个Widget /购物清单都有一个“主参考副本”似乎是一个好主意,以便为任何syncer-ensurerer类型的操作提供良好的“真实来源”。 - 用户副本?
    7. 关于以这种(冗余)方式构建的数据的rules.json / rules.bolt权限的任何特殊注意事项/陷阱/阻止程序?
    8. PS:我知道通过updateChildren()进行原子多路径更新 - 肯定会使用它们。

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

1 个答案:

答案 0 :(得分:0)

我建议只为整个系统提供一个小部件的副本。它将具有原始用户ID和一组有权访问它的用户。窗口小部件树可以保存用户权限并更改历史记录。每次进行更改时,都会在树中添加一个分支。然后可以促销分支机构#34;到#"主人"有点像GIT。这将保证数据完整性,因为以前的版本永远不会被更改或删除。它也会简化你的提取......我认为:)

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