我有一些DynamoDB数据,看起来像:
PK: SK:
subscriptionId | changeId | status | scheduled_for |
--------------------------------------------------------------
A | 19695a80-e3... | DONE | 2009-01-01 |
A | 3327f283-a1... | DONE | 2012-11-13 |
X | aebb5fe4-78... | DONE | 2019-06-24 |
X | 8982f726-69... | PENDING | 2022-01-01 |
如果有新的更改请求,我当前取消旧的更改并挂起新的更改,如下所示:
PK: SK:
subscriptionId | changeId | status | scheduled_for |
--------------------------------------------------------------
A | 19695a80-e3... | DONE | 2009-01-01 |
A | 3327f283-a1... | DONE | 2012-11-13 |
X | aebb5fe4-78... | DONE | 2019-06-24 |
X | 8982f726-69... | CANCELLED | 2022-01-01 |
X | 1f3380aa-f2... | PENDING | 2022-02-01 |
有一个(很小的)可能性,两个请求相同的订阅同时进入,所以我认为保证只有一个'PENDING'记录的最好方法是在事务中使用ConditionCheck并避免任何往返竞争条件-例如
// Only insert if there is no PENDING item for the subscriptionId
writeItems := []*dynamodb.TransactWriteItem{
{
ConditionCheck: &dynamodb.ConditionCheck{
TableName: aws.String("subscription_changes"),
ConditionExpression: aws.String("attribute_not_exists(changeId)"),
Key: map[string]*dynamodb.AttributeValue{
"status": {S: aws.String("PENDING")},
"subscriptionId": {S: aws.String(subscriptionId)},
},
},
},
{
Put: &dynamodb.Put{
TableName: aws.String("subscription_changes"),
Item: attributesMap,
},
},
}
…然而,DynamoDB希望我在我的条件中包含Sort Key (changeId),如果不查询数据,我就无法知道(因此打开了两个线程"获胜"的竞争条件的可能性,并且我们在表中有两个PENDING项)。
有办法做到这一点吗?
在SQL中是这样的:
INSERT subscription_items
(subscriptionId, changeId, status, ...)
SELECT 'X', '1f3380aa-f2...', 'PENDING', ...
WHERE NOT EXISTS (SELECT 1
FROM subscription_items
WHERE subscriptionId = ‘X’ AND status = 'PENDING')
事实上,Dynamo一致性的一个限制是你需要告诉Dynamo它应该操作哪些项目(PK/SK)。正如你所提到的,这在SQL中是相对微不足道的,但与Dynamo一样,另一方面是,它也相对不可能使SQL的方式做到web规模…:)
解决这个问题的一种方法是添加额外的"tracking"项目在你的tx,与PKX
和SKcurrent_pending
或类似的东西。在您的TX中,添加此项目,并验证它是否已经存在。这两个跟踪项目本身是相互独立的,但它们不能都成功地创建,因为只有其中一个将不会已经有current_pending
SK到位。然后,当一个项目不再挂起时,也删除同一TX中的current_pending
项目以释放"锁"。
Dynamo中的许多事务和一致性问题通过添加更多项目来解决!