无法使用 CloudFormation 将 GSI 添加到 DynamoDB 表
Posted
技术标签:
【中文标题】无法使用 CloudFormation 将 GSI 添加到 DynamoDB 表【英文标题】:Unable to add GSI to DynamoDB table using CloudFormation 【发布时间】:2016-08-23 10:31:29 【问题描述】:我有一个现有的 DynamoDB 表,它被定义为 CloudFormation 堆栈的一部分。根据CFN AWS::DynamoDB::Table documentation GlobalSecondaryIndexes 属性不需要替换。它甚至详细介绍了以下警告。
您可以不间断地删除或添加一个全局二级索引。
还有以下...
如果您更新表以包含新的全局二级索引,AWS CloudFormation 启动索引创建,然后继续 堆栈更新。 AWS CloudFormation 不会等待索引 完成创建,因为回填阶段可能需要很长时间, 取决于桌子的大小。
但是,实际上,当我尝试执行更新时,会收到以下错误消息:
CloudFormation cannot update a stack when a custom-named resource requires replacing. Rename mytablename and update the stack again.
由于我添加了一个使用新属性的 GSI,我不得不修改 AttributeDefinitions,它说它确实需要替换。但是,即使我尝试添加仅在 AttributeDefinitions 中定义的现有属性的 GSI,我仍然会收到相同的错误消息。
这是我的表的原始 CFN 定义中的 sn-p:
"myTable":
"Type": "AWS::DynamoDB::Table",
"Properties":
"TableName": "mytablename",
"AttributeDefinitions": [
"AttributeName": "entryId",
"AttributeType": "S"
,
"AttributeName": "entryName",
"AttributeType": "S"
,
"AttributeName": "appId",
"AttributeType": "S"
],
"KeySchema": [
"KeyType": "HASH",
"AttributeName": "entryId"
,
"KeyType": "RANGE",
"AttributeName": "entryName"
],
"ProvisionedThroughput":
"ReadCapacityUnits":
"Ref": "readThroughput"
,
"WriteCapacityUnits":
"Ref": "writeThroughput"
,
"GlobalSecondaryIndexes": [
"IndexName": "appId-index",
"KeySchema": [
"KeyType": "HASH",
"AttributeName": "appId"
],
"Projection":
"ProjectionType": "KEYS_ONLY"
,
"ProvisionedThroughput":
"ReadCapacityUnits":
"Ref": "readThroughput"
,
"WriteCapacityUnits":
"Ref": "writeThroughput"
]
这是我想要更新的内容:
"myTable":
"Type": "AWS::DynamoDB::Table",
"Properties":
"TableName": "mytablename",
"AttributeDefinitions": [
"AttributeName": "entryId",
"AttributeType": "S"
,
"AttributeName": "entryName",
"AttributeType": "S"
,
"AttributeName": "appId",
"AttributeType": "S"
,
"AttributeName": "userId",
"AttributeType": "S"
],
"KeySchema": [
"KeyType": "HASH",
"AttributeName": "entryId"
,
"KeyType": "RANGE",
"AttributeName": "entryName"
],
"ProvisionedThroughput":
"ReadCapacityUnits":
"Ref": "readThroughput"
,
"WriteCapacityUnits":
"Ref": "writeThroughput"
,
"GlobalSecondaryIndexes": [
"IndexName": "appId-index",
"KeySchema": [
"KeyType": "HASH",
"AttributeName": "appId"
],
"Projection":
"ProjectionType": "KEYS_ONLY"
,
"ProvisionedThroughput":
"ReadCapacityUnits":
"Ref": "readThroughput"
,
"WriteCapacityUnits":
"Ref": "writeThroughput"
,
"IndexName": "userId-index",
"KeySchema": [
"KeyType": "HASH",
"AttributeName": "userId"
],
"Projection":
"ProjectionType": "KEYS_ONLY"
,
"ProvisionedThroughput":
"ReadCapacityUnits":
"Ref": "readThroughput"
,
"WriteCapacityUnits":
"Ref": "writeThroughput"
]
但是,就像我之前提到的那样,即使我没有在 AttributeDefinitions 中定义 userId 并在新的 GSI 定义中使用现有属性,它也不起作用并且失败并显示相同的错误消息。
【问题讨论】:
我可以确认,当我尝试添加 GSI 而不对 AttributeDefinitions 进行任何更改时,也会发生同样的情况。该文档还指出:“更新要求:不支持更新。除了以下例外:如果仅更新全局二级索引的预置吞吐量值,则可以不间断地更新表。您可以删除或添加一个全局二级索引索引而不会中断。如果您在同一更新中同时执行这两项操作(例如,通过更改索引的逻辑 ID),更新将失败。" 【参考方案1】:我今天遇到了同样的错误,并得到了亚马逊技术支持的答复。问题是您提供了一个 TableName 字段。 CloudFormation 希望负责为您命名表。显然,当您为他们提供自己的名字时,这是您在替换表的更新时遇到的错误(不确定为什么需要替换,但这就是文档所说的)
对我来说,这使得 CloudFormation 对维护我的 DynamoDB 表毫无用处。我必须在配置中进行构建,以便我的代码可以动态判断 CloudFormation 为我生成的随机表名称是什么。
【讨论】:
我在亚马逊方面不明白的是 CloudFormation 如何可能对 Dynamo 有用。当然,我想选择一张我命名的桌子……对我来说根本没有意义。 您可以在运行时通过 CloudFormation 的逻辑 ID(CFN 模板中的名称)来获取表名。【参考方案2】:AWS 支持对我 FWIW 的回应:
解决方法 A
-
从表中导出数据到s3
使用添加了 gsi 的新表名 (tablename2) 更新堆栈
注意这会丢失所有当前条目,所以一定要先备份到 s3!
再次更新堆栈,重新使用 dynamodb 表中的 tablename1
从 s3 导入数据 这可以通过使用数据管道来缓解,请参阅
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBPipeline.html
优点是,应用代码可以继续使用固定名称。但是更新堆栈两次并导出/导入数据需要一些工作才能在自定义脚本中自动化。
解决方法 B
-
备份数据
让 CloudFormation 为表命名
使用 AWS SDK 检索表名,方法是通过逻辑 ID 描述堆栈资源获取名称,并从输出中获取表名。
虽然我认为这避免了额外的堆栈更新(仍然认为需要导出/导入数据),但缺点是代码中的网络调用来获取表名。看 * http://docs.aws.amazon.com/AWSjavascriptSDK/latest/AWS/CloudFormation.html#describeStackResource-property
同样,这是一个已知问题,支持正在推动服务团队前进,因为我们知道这是一个非常常见的用例和痛点。请先在测试环境中尝试解决方法,然后再进行生产测试。
【讨论】:
【参考方案3】:这里的问题是怎么发生的? 对我来说,在 dynamoDB 控制台中手动删除 GSI,然后通过 cloudformation 添加 GSI,update-stack 出现此错误。
解决方法:将cloudformation中的GSI去掉,执行update-stack,然后重新添加GSI,再次执行update-stack,就可以了。
猜测 cloudformation 有自己的缓存,无法告诉您在控制台中手动完成的更改。
【讨论】:
这对我有用。自定义表名应该不是问题。 有同样的问题,我必须先做一个sls remove
然后sls deploy -v
才能有一个干净的状态:)【参考方案4】:
我的场景是我想通过更改其范围键来更新 GSI。 - 首先,您必须删除正在更新的 GSI,还记得删除由于删除 GSI (即索引名称等)可能不再需要的任何 AttributeDefinition。通过 CloudFormation 上传模板以应用更改。 - 然后将所需的属性和“更新的”GSI 添加到模板中。
【讨论】:
“还记得删除任何可能不再需要的 AttributeDefinition”,这就是我所缺少的。如果该属性没有在 gsi 中使用,那么它不应该存在于 AttributeDefinitions【参考方案5】:备份 DynamoDB 中的所有数据,然后,如果您使用的是无服务器,请执行以下任一命令:
单独删除:
node ./node_modules/serverless/bin/serverless remove
全局删除:
serverless remove
并通过运行再次部署它:
node ./node_modules/serverless/bin/serverless deploy -v
或
serverless deploy
【讨论】:
如果这样做,那么您可以将备份的数据恢复到新创建的表中吗?以上是关于无法使用 CloudFormation 将 GSI 添加到 DynamoDB 表的主要内容,如果未能解决你的问题,请参考以下文章
如何在 CloudFormation 中使用基础架构即代码实施 DynamoDB 全局二级索引
Ansible CloudFormation模块无法查看S3对象
无法在 Cloudformation 模板中定义 Cloudwatch 警报的数学表达式
无法通过自定义 cloudformation 资源调用 lambda 函数
无法在 serverless.yml 中引用 CloudFormation 资源。变量 UserPoolId 的变量引用语法无效