Firebase: структурирование данных с помощью копий для каждого пользователя? Риск повреждения данных?

Реализация приложения Android+Web(Angular)+Firebase, которое имеет отношение «многие ко многим»: Пользователь ‹-> Виджет (виджеты могут быть доступны нескольким пользователям).

Соображения:

  1. Список всех виджетов, которые есть у пользователя.
  2. Пользователь может видеть только те виджеты, которые ему предоставлены.
  3. Иметь возможность видеть всех пользователей, которым предоставлен доступ к данному виджету.
  4. Один виджет может принадлежать/администрироваться нескольким Пользователям с равными правами (изменить виджет и изменить, кому он будет предоставлен). Подобно тому, как Google Диск предоставляет доступ определенным пользователям.

Один из подходов к реализации выборки (стиль объединения) — это воспользоваться следующим советом: https://www.firebase.com/docs/android/guide/structuring-data.htmlJoining Flattened Data») через несколько прослушивателей. Однако у меня есть сомнения по поводу этого подхода, потому что я обнаружил, что загрузка данных будет тревожно медленной (по крайней мере, на Android) - я спросил об этом в другом вопросе - Firebase Android: медленное соединение с использованием многих слушателей, похоже, противоречит документации .

Итак, этот вопрос касается другого подхода: пользовательских копий всех виджетов, которые есть у пользователя. Как используется в руководстве 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. Пока пользователь 1 находится в автономном режиме, пользователь 2 предоставляет доступ к виджету 1 пользователю 3 (копия пользователя 3 еще не будет знать о переименовании). Пользователь 1 выходит в сеть и отправляет информацию о переименовании Виджета 1 (только своей копии и копии Пользователя 2, о чем клиентский код знал на момент переименования, не обновляя копию Пользователя 3). Теперь в наивной реализации User3 будет иметь старое имя, а остальные будут иметь новое имя. Вероятно, это будет редко, но все же немного беспокоит.
  3. Мог ли/должен ли быть сценарий повреждения данных в пункте «2». решить с помощью некоторого процесса (например, в AppEngine), прослушивающего изменения и обеспечивающего надлежащее распространение на все пользовательские копии?
  4. И/или может/должен быть сценарий повреждения данных в пункте «2». решить путем реализации избыточного прослушивания как изменений общего доступа, так и переименования, а также распространения изменений на копии для каждого пользователя, чтобы справиться с особым случаем? В большинстве случаев в этом нет необходимости, поэтому это может привести к снижению производительности/пропускной способности и усложнению кода. Стоит ли оно того?
  5. В дальнейшем, когда у нас будет несколько версий, развернутых «в дикой природе», не станет ли разработка схемы громоздкой, учитывая, какая часть ответственности за обработку данных лежит на коде в клиентах? Например, если мы добавим новую связь, о которой старые версии клиента еще не знают, не кажется ли она хрупкой? Затем вернитесь к серверному процессу syncer-ensurerer, например. AppEngine (описано в вопросе «3.»)?
  6. Не кажется ли вам хорошей идеей также иметь «основную справочную копию» каждого виджета/списка покупок, чтобы предоставить хороший «источник правды» для любых операций типа синхронизатора-гаранта, которые будут обновлять копии для каждого пользователя. ?
  7. Какие-либо особые соображения/ловушки/блокировщики в отношении разрешений rules.json/rules.bolt для данных, структурированных таким (избыточным) образом?

PS: Я знаю об атомарных многопутевых обновлениях через updateChildren() - обязательно воспользуюсь ими.

Любые другие подсказки/наблюдения приветствуются. ТИА.


person KarolDepka    schedule 16.03.2016    source источник
comment
Я хотел бы получить общее мнение о примере ShoppingList++ - стоит ли следовать этому примеру или мне следует относиться к нему скептически. Это будет невероятно широко и очень субъективно, поэтому я склонен проголосовать за закрытие. Возможно, вы захотите удалить его.   -  person Frank van Puffelen    schedule 17.03.2016
comment
7 вопросов в одном посте - это слишком. Он также намного слишком широк. Пожалуйста, сузьте его до одного вопроса, на который можно ответить за разумное количество времени/пространства.   -  person Frank van Puffelen    schedule 17.03.2016
comment
См. как спросить   -  person Kato    schedule 17.03.2016
comment
Моя голова взорвалась. Тем не менее, в посте есть несколько хороших вопросов. Я прошу ОП сузить вопросы до одного на пост и ограничить объем вопроса, пример кода и структуру Firebase. Сделайте это, и мы сможем помочь.   -  person Jay    schedule 17.03.2016
comment
@FrankvanPuffelen - я удалил часть вопроса о доверии к ShoppingList++ в целом. Остальные вопросы должны оставаться в силе. Помимо того, что в одном посте слишком много вопросов, как предлагали некоторые люди, которые я постараюсь разделить.   -  person KarolDepka    schedule 17.03.2016
comment
@Kato: что касается того, как спросить - какая часть (части) конкретно? Я не думаю, что вопрос некорректен по всем принципам...   -  person KarolDepka    schedule 17.03.2016


Ответы (1)


Я предлагаю иметь только одну копию виджета для всей системы. У него будет исходный идентификатор пользователя и набор пользователей, которые имеют к нему доступ. Дерево виджетов может содержать разрешения пользователя и историю изменений. Каждый раз, когда вносятся изменения, к дереву добавляется ветвь. Затем ветки могут быть «повышены» до «мастера» вроде GIT. Это гарантирует целостность данных, поскольку прошлые версии никогда не изменяются и не удаляются. Это также упростило бы вашу выборку... Я думаю :)

{ 
  users:[
    bob:{
      widgets:[
        xxx:{
           widgetKey: xyz,
           permissions: *,
           lastEdit... 
        }
      ]
    }
    ...
  ]
  widgets:[
    xyz:{
       masterKey:abc,
       data: {...},
       owner: bob,
    },
    ...
  ]
  widgetHistory:[
    xyz:[
      v1:{
         data:{...},
      },
      v2,
      v3
    ]
    123:[
       ...
    ],
    ...
  ]
 }
person Aaron Franco    schedule 16.03.2016
comment
Спасибо. Но как мне эффективно прочитать все виджеты, которые есть у данного (в данный момент вошедшего в систему) пользователя? Можете ли вы вставить несколько json-файлов с примером структуры? Любая ссылка на аналогичный подход? Кстати, как долго вы используете Firebase? - person KarolDepka; 17.03.2016
comment
Вы можете сохранить отдельное дерево идентификаторов ссылок для доступа к виджетам непосредственно для этого пользователя. - person Aaron Franco; 17.03.2016
comment
Спасибо. Я рассматривал этот подход (кроме идеи управления версиями), но он кажется слишком медленным для выборки (часто до 6 секунд всего для 100 элементов) - stackoverflow.com/questions/35996865/ . - person KarolDepka; 17.03.2016
comment
Что такое masterKey:abc? - person KarolDepka; 17.03.2016
comment
Это будет ключом к версии, которая активно используется. - person Aaron Franco; 17.03.2016