无法使用 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 表的主要内容,如果未能解决你的问题,请参考以下文章

无法在{tableName}查询Dynamo

如何在 CloudFormation 中使用基础架构即代码实施 DynamoDB 全局二级索引

Ansible CloudFormation模块无法查看S3对象

无法在 Cloudformation 模板中定义 Cloudwatch 警报的数学表达式

无法通过自定义 cloudformation 资源调用 lambda 函数

无法在 serverless.yml 中引用 CloudFormation 资源。变量 UserPoolId 的变量引用语法无效