是否可以使用 Azure 表存储进行条件插入

发布于 2025-01-01 02:29:12 字数 474 浏览 1 评论 0原文

是否可以使用 Windows Azure 表存储服务进行条件插入

基本上,我想要做的是将新行/实体插入到表存储服务的分区中,当且仅当自我上次查看以来该分区中没有任何更改时。

如果您想知道,我想到的是事件溯源,但我认为问题比这更普遍。

基本上我想读取部分或整个分区,并根据数据内容做出决定。为了确保自加载数据以来分区中没有任何变化,插入的行为应该像正常的乐观并发一样:只有在分区中没有任何变化时插入才应该成功 - 没有添加、更新或删除行。

通常在 REST 服务中,我希望使用 ETag 来控制并发,但据我所知,分区没有 ETag。

我能想到的最佳解决方案是为表中的每个分区维护一个行/实体,其中包含时间戳/ETag,然后使所有插入也成为由插入组成的批次的一部分作为此“时间戳实体”的有条件更新。然而,这听起来有点麻烦和脆弱。

Azure 表存储服务可以实现这一点吗?

Is it possible to make a conditional insert with the Windows Azure Table Storage Service?

Basically, what I'd like to do is to insert a new row/entity into a partition of the Table Storage Service if and only if nothing changed in that partition since I last looked.

In case you are wondering, I have Event Sourcing in mind, but I think that the question is more general than that.

Basically I'd like to read part of, or an entire, partition and make a decision based on the content of the data. In order to ensure that nothing changed in the partition since the data was loaded, an insert should behave like normal optimistic concurrency: the insert should only succeed if nothing changed in the partition - no rows were added, updated or deleted.

Normally in a REST service, I'd expect to use ETags to control concurrency, but as far as I can tell, there's no ETag for a partition.

The best solution I can come up with is to maintain a single row/entity for each partition in the table which contains a timestamp/ETag and then make all inserts part of a batch consisting of the insert as well as a conditional update of this 'timestamp entity'. However, this sounds a little cumbersome and brittle.

Is this possible with the Azure Table Storage Service?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

妳是的陽光 2025-01-08 02:29:12

千英尺的风景

我可以和你分享一个小故事吗......

曾几何时,有人想要保留聚合事件(来自领域驱动设计名声)以响应给定的命令。此人希望确保聚合仅创建一次,并且可以检测到任何形式的乐观并发。

为了解决第一个问题(聚合只能创建一次),他在事务介质中进行了插入,当检测到重复聚合(或更准确地说是其主键)时,该插入会抛出异常。他插入的是作为主键的聚合标识符和变更集的唯一标识符。处理命令时聚合产生的事件的集合,就是这里的变更集的意思。如果有人或其他事打败了他,他会考虑已经创建的聚合并保留它。变更集将预先存储在他选择的介质中。该媒介必须做出的唯一承诺是在被要求时按原样返回已存储的内容。任何存储变更集的失败都将被视为整个操作的失败。

为了解决第二个问题 - 在聚合的进一步生命周期中检测乐观并发 - 在编写另一个变更集之后,当且仅当没有人在他背后更新它时,他才会更新事务介质中的聚合记录(即与他在执行命令之前最后读取的内容进行比较)。如果发生这样的事情,交易媒介会通知他。这将导致他重新启动整个操作,重新读取聚合(或其变更集)以使命令这次成功。

当然,现在他解决了写作问题,随之而来的是阅读问题。人们如何能够读取构成其历史的聚合的所有变更集?毕竟,他只有与该事务介质中的聚合标识符相关联的最后提交的变更集。因此,他决定嵌入一些元数据作为每个变更集的一部分。在元数据中(作为变更集的一部分并不罕见)将是先前最后提交的变更集的标识符。这样他就可以“走线”他的聚合的变更集,可以说就像一个链接列表。

作为额外的好处,他还将命令消息标识符存储为变更集元数据的一部分。这样,当读取变更集时,他可以提前知道他要在聚合上执行的命令是否已经是其历史记录的一部分。

一切都好,结局好......

PS
1.事务介质和变更集存储介质可以相同,
2. 变更集标识符不能是命令标识符,
3. 随意在故事中挖洞:-),
4. 虽然与 Azure 表存储没有直接关系,但我已经使用 AWS DynamoDB 和 AWS S3 成功实现了上述故事。

The view from a thousand feet

Might I share a small tale with you...

Once upon a time someone wanted to persist events for an aggregate (from Domain Driven Design fame) in response to a given command. This person wanted to ensure that an aggregate would only be created once and that any form of optimistic concurrency could be detected.

To tackle the first problem - that an aggregate should only be created once - he did an insert into a transactional medium that threw when a duplicate aggregate (or more accurately the primary key thereof) was detected. The thing he inserted was the aggregate identifier as primary key and a unique identifier for a changeset. A collection of events produced by the aggregate while processing the command, is what is meant by changeset here. If someone or something else beat him to it, he would consider the aggregate already created and leave it at that. The changeset would be stored beforehand in a medium of his choice. The only promise this medium must make is to return what has been stored as-is when asked. Any failure to store the changeset would be considered a failure of the whole operation.

To tackle the second problem - detection of optimistic concurrency in the further life-cycle of the aggregate - he would, after having written yet another changeset, update the aggregate record in the transactional medium if and only if nobody had updated it behind his back (i.e. compared to what he last read just before executing the command). The transactional medium would notify him if such a thing happened. This would cause him to restart the whole operation, rereading the aggregate (or changesets thereof) to make the command succeed this time.

Of course, now he had solved the writing problems, along came the reading problems. How would one be able to read all the changesets of an aggregate that made up its history? Afterall, he only had the last committed changeset associated with the aggregate identifier in that transactional medium. And so he decided to embed some metadata as part of each changeset. Among the meta data - which is not so uncommon to have as part of a changeset - would be the identifier of the previous last committed changeset. This way he could "walk the line" of changesets of his aggregate, like a linked list so to speak.

As an additional perk, he would also store the command message identifier as part of the metadata of a changeset. This way, when reading changesets, he could know in advance if the command he was about to execute on the aggregate was already part of its history.

All's well that ends well ...

P.S.
1. The transactional medium and changeset storage medium can be the same,
2. The changeset identifier MUST not be the command identifier,
3. Feel free to punch holes in the tale :-),
4. Although not directly related to Azure Table Storage, I've implemented the above tale successfully using AWS DynamoDB and AWS S3.

携余温的黄昏 2025-01-08 02:29:12

如何将每个事件存储在基于 AggregateId/AggregateVersion 创建的“PartitionKey/RowKey”中?其中 AggregateVersion 是基于聚合已有事件数量的序列号。

这是非常确定的,因此当向聚合添加新事件时,您将确保使用的是最新版本,否则您将收到一条错误消息,指出该分区的行已存在。此时,您可以删除当前操作并重试,或者尝试确定如果聚合的新更新与您刚刚执行的操作不冲突,是否可以合并该操作。

How about storing each event at "PartitionKey/RowKey" created based on AggregateId/AggregateVersion?where AggregateVersion is a sequential number based on how many events the aggregate already has.

This is very deterministic, so when adding a new event to the aggregate, you will make sure that you were using the latest version of it, because otherwise you'll get an error saying that the row for that partition already exists. At this time you can drop the current operation and retry, or try to figure out if you could merge the operation anyways if the new updates to the aggregate do not conflict to the operation you just did.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文