在路径不确定且可变的情况下,在多路径更新期间具有一致性 [英] Having consistency during multi path updates when the paths are not deterministic and are variable

查看:173
本文介绍了在路径不确定且可变的情况下,在多路径更新期间具有一致性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我们对扇出数据进行多路径更新时,我需要帮助。当我们计算路径的数量然后更新时,如果在某个地方添加了新的路径,那么在新添加的路径中的数据将会不一致。



下面的例子是博客文章的数据。帖子可以用tag1,tag2等多个词汇来标记。为了找到有多少帖子用一个特定的标签进行标记,我可以把帖子数据分成标签路径路径:

  / posts / postid1:{Title:Title 1,body:关于Firebase,tags:{tag1:true,tag2:true}} 
/ tags / tag1 / postid1:{Title:Title 1,body:关于Firebase}
/ tags / tag2 / postid1:{Title:Title 1,body: }

现在考虑一下,

1a):User1想要修改postid1的标题,然后他建立以下多路径更新:

  / posts / postid1 / Title:Title 1 modified
/ tags / tag1 / postid1 / Title:Title 1 modified
/ tags / tag2 / postid1 / Title:Title 1 modified

<1b> 同时User2想要将tag3添加到postid1并构建以下多路径更新:

  / pos ts / postid1 / tags:{tag1:true,tag2:true,tag3:true} 
/ tags / tag3 / postid1:{Title:Title 1,body关于Firebase}

所以显然这两个更新可以一个接一个地成功,我们可以有tag / tag3 / postid1数据不同步,因为它有旧的标题。

我可以想到安全规则来处理这个,但不知道这是否正确或将工作。



像我们可以有updatedAt和lastUpdatedAt字段,我们检查是否正在更新我们自己的帖子版本:



< pre $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $'$' b.validate:
newData.hasChildren(['userId','updatedAt','lastUpdated','Title'])&& (
!data.exists()||
data.child('updatedAt').val()=== newData.child('lastUpdated')。val())
}
}

另外,对于标签,我们不想再次检查,我们可以检查如果/ tags / $ tag / $ postid / updatedAt和/ posts / $ postid / updatedAt是一样的。

 tags: {
$ tag:{
$ postid:{
.write:true,
.read:true,
.validate :
newData.hasChildren(['userId','updatedAt','lastUpdated','Title'])&& (
newData.child('updatedAt')。val()=== root.child('posts')。child('$ postid')。val()。child('updatedAt')。val ))
}
}
}

/ posts / $ postid中有并发控制,用户可以写自己的读取
也/ posts / $ postid成为真相的来源,并休息其他扇出路径检查updatedAt字段是否匹配它的主要来源真理的道路。


这会带来一致性还是存在问题?或者在大规模完成时可以降低性能?



多路径更新和规则原子在一起,我的意思是一条规则或两个规则孤立地评估多路径更新,如1a和1b以上?

解决方案

不幸的是,Firebase不提供任何保证或机制来提供您正在寻找的确定性级别。我已经有了最好的运气,用API堆栈(GCF和Lambda都是非常简单的,无服务器的方法)。更新可以在该层进行,甚至在绝对必要时进行序列化。但是在Firebase本身并没有安全的方法。

有许多可以应用的黑客选项。例如,你可以有一个简单的锁定机制,使用专用的集合来跟踪写锁。在执行写入操作之前,客户端可以发布到锁集合,然后验证其密钥是该集合的唯一成员。但是我希望你们同意我这样的合作系统有太多潜在的边缘情况,潜在的安全问题等等。在Firebase中,最好设计这个组件不是首要的要求。

I need help in a scenario when we do multipath updates to a fan-out data. When we calculate the number of paths and then update, in between that, if a new path is added somewhere, the data would be inconsistent in the newly added path.

For example below is the data of blog posts. The posts can be tagged by multiple terms like "tag1", "tag2". In order to find how many posts are tagged with a specific tag I can fanout the posts data to the tags path path as well:

/posts/postid1:{"Title":"Title 1",  "body": "About Firebase", "tags": {"tag1:true, "tag2": true}}
/tags/tag1/postid1: {"Title":"Title 1",  "body": "About Firebase"}
/tags/tag2/postid1: {"Title":"Title 1",  "body": "About Firebase"}

Now consider concurrently,

1a) that User1 wants to modify title of postid1 and he builds following multi-path update:

/posts/postid1/Title : "Title 1 modified"
/tags/tag1/postid1/Title : "Title 1 modified"
/tags/tag2/postid1/Title : "Title 1 modified"

1b) At the same time User2 wants to add tag3 to the postid1 and build following multi-path update:

/posts/postid1/tags : {"tag1:true, "tag2": true, "tag3": true}
/tags/tag3/postid1: {"Title":"Title 1",  "body": "About Firebase"}

So apparently both updates can succeed one after other and we can have tags/tag3/postid1 data out of sync as it has old title.

I can think of security rules to handle this but then not sure if this is correct or will work.

Like we can have updatedAt and lastUpdatedAt fields and we have check if we are updating our own version of post that we read:

posts":{
  "$postid":{
    ".write":true,
    ".read":true,
    ".validate": "
      newData.hasChildren(['userId', 'updatedAt', 'lastUpdated', 'Title']) && (
        !data.exists() || 
        data.child('updatedAt').val() === newData.child('lastUpdated').val())"
  } 
}

Also for tags we do not want to check that again and we can check if /tags/$tag/$postid/updatedAt is same as /posts/$postid/updatedAt.

"tags":{
  "$tag":{
    "$postid":{
      ".write":true,
      ".read":true,
      ".validate": "
        newData.hasChildren(['userId', 'updatedAt', 'lastUpdated', 'Title']) && (
        newData.child('updatedAt').val() === root.child('posts').child('$postid').val().child('updatedAt').val())"            
    }
  }     
}

By this "/posts/$postid" has concurrency control in it and users can write their own reads Also /posts/$postid" becomes source of truth and rest other fan-out paths check if updatedAt fields matches with it the primary source of truth path.

Will this bring in consistency or there are still problems? Or can bring performance down when done at scale?

Are multi path updates and rules atomic together by that I mean a rule or both rules are evaluated separately in isolation for multi path updates like 1a and 1b above?

解决方案

Unfortunately, Firebase does not provide any guarantees, or mechanisms, to provide the level of determinism you're looking for. I have had the best luck front-ending such updates with an API stack (GCF and Lambda are both very easy, server-less methods of doing this). The updates can be made in that layer, and even serialized if absolutely necessary. But there isn't a safe way to do this in Firebase itself.

There are numerous "hack" options you could apply. You could, for example, have a simple lock mechanism using a dedicated collection for tracking write locks. Clients could post to a lock collection, then verify that their key was the only member of that collection, before performing a write. But I hope you'll agree with me that such cooperative systems have too many potential edge cases, potential security issues, and so on. In Firebase, it is best to design such that this component is not a requirement in the first place.

这篇关于在路径不确定且可变的情况下,在多路径更新期间具有一致性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆