在Cosmos DB中是否可以创建文档的单链接列表?

时间:2018-11-17 02:30:58

标签: sql json database azure-cosmosdb document-database

我通常要解决的一个问题是保留文档的不变版本而不是编辑文档。当要求提供文档时,请获取最新版本。

一种方法是使用时间戳:

doc 0:

{
   id: "e69e0bea-77ea-4d97-bedf-d3cca27ae4b6",
   correlationId: "d00be916-10e3-415c-aaf6-9acb7c70cf4f",
   created: "11/17/2018 2:20:25 AM",
   value: "foo"
}

doc 1:

{
   id: "37ef6f99-bc87-45bb-87ae-a1b81070cc91",
   correlationId: "d00be916-10e3-415c-aaf6-9acb7c70cf4f",
   created: "11/17/2018 2:20:44 AM",
   value: "bar"
}

doc 2:

{
   id: "93fc913e-5ecc-4c59-a130-0e577ed4f2fb",
   correlationId: "d00be916-10e3-415c-aaf6-9acb7c70cf4f",
   created: "11/17/2018 2:21:51 AM",
   value: "baz"
}

使用时间戳的缺点是您必须按时间戳(O(n*log(n)))进行排序才能获得第N个最新版本。

我希望通过存储指向先前版本的指针来制作O(n),例如

{
   id: "e69e0bea-77ea-4d97-bedf-d3cca27ae4b6",
   previousId: null,
   correlationId: "d00be916-10e3-415c-aaf6-9acb7c70cf4f",
   created: "11/17/2018 2:20:25 AM",
   value: "foo"
}

doc 1:

{
   id: "37ef6f99-bc87-45bb-87ae-a1b81070cc91",
   previousId: "e69e0bea-77ea-4d97-bedf-d3cca27ae4b6",
   correlationId: "d00be916-10e3-415c-aaf6-9acb7c70cf4f",
   created: "11/17/2018 2:20:44 AM",
   value: "bar"
}

doc 2:

{
   id: "93fc913e-5ecc-4c59-a130-0e577ed4f2fb",
   previousId: "37ef6f99-bc87-45bb-87ae-a1b81070cc91",
   correlationId: "d00be916-10e3-415c-aaf6-9acb7c70cf4f",
   created: "11/17/2018 2:21:51 AM",
   value: "baz"
}

所以它是一个类似的链接列表

NULL <- doc0 <- doc1 <- doc2

阻止我执行此操作的唯一方法是,要创建新版本,我需要某种锁定机制,例如(使用伪代码)

lock correlationId
   get latest
   new.previousId = latest.id
   insert new

但是我不确定在数据库级别是否可行。

2 个答案:

答案 0 :(得分:0)

没有锁定的概念,但是在您的情况下,您可以利用唯一的键约束:

  • 使用correlationId作为逻辑分区键创建一个分区集合
  • 添加唯一的键约束,其键基于previousId

这时,对于给定的correlationId,如果尝试在列表中创建一个新链接,而又以某种方式在之前创建了另一个链接,则会在previousId上遇到冲突,然后您就可以使用刚刚创建的previousId的文档ID来重新执行操作。

注意:每个文档都有一个 ETag,如果您决定在某个时候使用更新,则在更新文档时该功能有助于并发。

答案 1 :(得分:-1)

您是否考虑过Cosmos DB Graph API。链接列表实际上是图的一种非常基本的形式。

您正在做的事情看起来不错,但是更新相关ID可能会很混乱。有了图谱API,就不会出现问题了。

在第一条评论之后更新答案:

这是我们可以使用SQL API进行的操作。

该链可以建模为:

NULL <- Doc1 <- Doc2 <- Doc3 <- Head.

Head与其他版本的文档具有相同的correlationId。另外,correlationId必须是集合的分区键,以便将同一文档的所有版本都放在同一物理分区中。

现在,我们可以使用存储过程来更新文档的版本。请注意,存储过程是在分区键范围内进行事务处理的(这是我们希望correlationId成为分区键的原因)。

下面是存储过程的伪代码。

Add New version: 
    Read the Head(H) Document 
    save the _etag of the Head Document 
    Follow H to read the current most recent version (CMRV)
    Add a document for the new most recent version (NMRV)
    Point H to NRMV and NMRV to CMRV
    Update H with some dummy information (say number of version) using the _etag saved before

整个片段都是原子的。如果另一个并发线程已成功更新H,则当前存储的proc将失败,并显示“ Precondition”失败错误(由于_etag不匹配),并且整个存储的proc将回滚。