DynamoDb条件插入

时间:2019-07-19 06:33:21

标签: c# amazon-dynamodb

我在我的应用程序中使用C#和DynamoDb。 我的设计假设只写-读。强烈禁止更新项目。仅插入新项目或阅读现有项目。
假设我的付款项目为

{
  "PaymentInvoice":"001", //PK
  "Status":"2019-07-10T00:00:00#Approved" //SK
}

现在有2个并发请求来自不同的客户:第一个尝试尝试Cancel付款,第二个尝试Settle(确认)付款。

2个插入是:

{
  "PaymentInvoice":"001", //PK
  "Status":"2019-07-10T00:01:00#Cancel" //SK
}

{
  "PaymentInvoice":"001", //PK
  "Status":"2019-07-10T00:01:00#Settle" //SK
}

这是比赛条件。
-如果付款被取消,您将无法结清
-如果已经结算,您将无法取消付款

明显的解决方案是:
1)创建交易
2)首先进行查询,然后检查由于业务规则是否可以插入
3)插入新项目

所以问题是: 1)是否可以锁定整个分区以防止从其他客户端插入新项目? 2)是否有诸如条件更新之类的内置选项,但对于插入

1 个答案:

答案 0 :(得分:1)

在开始之前请先澄清一下。我假设是一种分布式的,面向服务的体系结构,并且我假定对此DynamoDB表的所有读取和写入仅通过一项服务进行。我将使用“应用程序”来指代您正在构建的用于访问表的软件,使用“客户端”来指代所有属于您的应用程序的客户端。


是否有诸如条件更新之类的内置选项,但可用于插入项目?

简短的答案是“是”,使用基于optimistic lockingversion numbers

首先需要将排序键更改为顺序的事件编号。这将是用于对项目进行版本控制的属性,该属性使用条件更新,并且在解决您的问题的任何解决方案中所产生的额外开销最少。

让我们从查看所提议模式的一些样本数据开始。我可以自由添加更多状态类型。

invoiceId | eventNo | eventStatus |      datetime
----------|---------|-------------|---------------------
      111 |       0 | created     | 2019-07-11T00:01:00
      111 |       1 | approved    | 2019-07-11T00:02:00
      111 |       2 | modified    | 2019-07-12T00:03:00
      111 |       3 | approved    | 2019-07-12T00:04:00
      111 |       4 | settled     | 2019-07-13T00:05:00

乐观锁定的一般想法是,您先读取当前状态,然后通过插入新记录来更新状态,该记录的增量为eventNo(与AWS文档中的version相同),条件是该eventNo尚未出现invoiceId。之所以起作用,是因为当您读取现有状态时,您总是知道下一个eventNo应该是什么(不同于使用时间戳作为排序键)。

为了更具体一点,在2019-07-13上,当客户发送结算发票的请求时,您的应用程序读取最新状态,看到eventNo为3,而{{1} }是“已批准”的,因此它向DynamoDB提交了一个UpdateItem请求(翻译成普通英语),

  

仅当statusinvoiceId=111尚无其他状态更新时,才使用eventNo=4invoiceId=111插入状态更新

如果两个客户端尝试同时更新状态,则只有一个UpdateItem请求将成功,而另一个将返回ConditionalCheckFailedException

好的,那我该怎么编码?

十多年来我一直没有使用C#,因此请原谅可能出现的任何语法或格式错误。

eventNo=4

这是我提供的代码示例的一些相关文档。


是否可以锁定整个分区以防止从其他客户端插入新项目?

是的,但这不是数据库中实现的锁。锁定必须使用单独的锁定库在应用程序中进行,这会产生额外的开销,因此,除非没有其他选择,否则不应使用此方法。对于阅读了无法更改其表架构的问题的任何人,您可以将您的应用程序设置为使用DynamoDB lock client来锁定分区键,读取当前状态,执行写入(如果允许),然后释放锁。