Транзакция Firestore на основе несуществующего документа (блокировка на уровне коллекции недоступна) - PullRequest
0 голосов
/ 04 августа 2020

На основании этого SO-ответа я узнал, что firestore не имеет блокировки уровня коллекции в транзакции. В моем случае я должен убедиться, что поле имени пользователя в коллекции пользователей уникально, прежде чем писать в коллекцию. Для этого я пишу транзакцию, которая делает это:

  1. Выполняет запрос к коллекции пользователей, чтобы проверить, существует ли документ, где username = something
  2. Если он существует, сбой и возврат ошибка транзакции
  3. Если она не существует, просто запустите операцию записи для идентификатора пользователя, который я хочу обновить / создать.

Теперь проблема в том, что если два клиента одновременно попытаются чтобы запустить эту транзакцию, оба могут запросить коллекцию, и, поскольку коллекция не заблокирована, один клиент может вставить / обновить документ в коллекцию, а другой его не увидит.

Верно ли мое предположение? И если да, то как бороться с таким сценарием ios?

Ответы [ 2 ]

1 голос
/ 20 августа 2020

Альтернативой кодированию этих данных в do c id является использование отдельной коллекции как своего рода ручного индекса. Затем правила безопасности могут обеспечить уникальность индекса. Итак, что-то вроде этого:

/docs/${documentId} => {uniqueField: "foo", ...}
/docmap/${uniqueField} => {docId: "doc2"}

Идея здесь в том, что сначала нужно написать запись docmap, содержащую новый do c id, прежде чем им будет разрешено писать, он делает c. Поскольку docmap привязан к нашему уникальному полю, он обеспечивает уникальность.

Правила безопасности будут выглядеть примерно так:

  function getPath(childPath) {
     return path('/databases/'+database+'/documents/'+childPath)
  }

  // we can only write to our doc if the unique field exists in docmap/
  // and matches our doc id
  match /docs/{docid} {
     let docMapPath = 'docmap/' + request.resource.data.uniqueField;
     allow write: if getData(docMapPath).docId == docId;
     //todo validate data schema
  }

  // It is only possible to add a uniqueField to the docmap
  // if it doesn't already exist for another doc
  // we also validate that the doc id matches our schema
  match /docmap/{uniqueField} {
     allow write: if resource.data.size() == 0 && 
            request.resource.data.docId is string &&
            request.resource.data.docId.size() < 100
  }

А запись будет выглядеть примерно так:

 const db = firebase.firestore();
 db.doc('docmap/foo').set('doc2')
   .then(() => db.doc('docs/doc2').set({uniqueField: 'foo'})
   .then(doc => console.log("success"))
   .catch(e => console.error(e));

Вы также можете сделать это в транзакции или даже в пакетной операции, чтобы сделать его atomi c, но, вероятно, нет необходимости усложнять процесс; правила безопасности будут обеспечивать соблюдение ограничений.

1 голос
/ 04 августа 2020

То, что вы пытаетесь сделать, на самом деле невозможно сделать атомарно, так как невозможно безопасно выполнить транзакцию с документом, который вы не можете идентифицировать с помощью идентификатора. Проблема здесь в том, что транзакция является «безопасной» только в том случае, если вы можете get() указанный c документ добавить или изменить. Поскольку вы не можете get() документ, использующий значение поля в документе, вы в недоумении.

Если вы хотите гарантировать уникальность чего-либо в Firestore, эту уникальность нужно будет закодировать в сам идентификатор документа. В простейшем случае вы можете использовать имя пользователя как идентификатор документа в новой коллекции. Если вы это сделаете, ваша транзакция может просто get() требуемый документ по имени пользователя, проверить, существует ли он, а затем написать документ, если это не так. В противном случае транзакция может завершиться неудачно.

Имейте в виду, что, поскольку существуют ограничения для документирования идентификаторов в Firestore, вам может потребоваться экранировать или закодировать это имя пользователя, если ваши имена пользователей могут нарушать правила .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...