1
votes

I am trying to build a DynamoDB table using boto,which will save the various aspects of an IAM policy in the table. I have defined the attributes for keyschema, I do not understand the error.I am very new to DYanmoDB and AWS. This is my code:

table =dynamodb.create_table(
    TableName='GoodTable',
            KeySchema=[
            {
                'AttributeName': 'Name',
                'KeyType': 'HASH'  
            },
            {
                'AttributeName': 'Instance Type',
                'KeyType': 'RANGE'  
            },
            {
                'AttributeName': 'Region',
                'KeyType': 'RANGE'  
            },
            {
                'AttributeName': 'Volume Size',
                'KeyType': 'RANGE'  
            },
        ],
    AttributeDefinitions=[
    {
      "AttributeName": "Name",
      "AttributeType": "S"
    },
    {
      "AttributeName": "Instance Type",
      "AttributeType": "S"
    },
    {
      "AttributeName": "Region",
      "AttributeType": "S"
    },
    {
      "AttributeName": "Volume Size",
      "AttributeType": "N"
    }
    ],
  ProvisionedThroughput={
    "ReadCapacityUnits": 1,
    "WriteCapacityUnits": 1
  }
)
time.sleep(20)
table = dynamodb.Table('GoodTable')
response = table.put_item(
    Item= {
        'Name': 'GoodName', 
    }
)
response = table.put_item(
    Item= {
        'Instance Type': 't2.micro',
    }
)
response = table.put_item(
    Item= {
        'Region': 'us-east-1',
    }
)
response = table.put_item(
    Item= {
        'Volume Size': '20',
    }
)

This is the error I am getting:

botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the CreateTable operation: 1 validation error detected: Value '[com.amazonaws.dynamodb.v20120810.KeySchemaElement@ad4dcbcd, com.amazonaws.dynamodb.v20120810.KeySchemaElement@126b7ad8, com.amazonaws.dynamodb.v20120810.KeySchemaElement@ca666a07, com.amazonaws.dynamodb.v20120810.KeySchemaElement@6478bc3a]' at 'keySchema' failed to satisfy constraint: Member must have length less than or equal to 2

3

3 Answers

1
votes

You can only have 2 fields as a primary key in DynamoDB. You can have only one Hash Key and One Range Key Max.

CreateTable

For a composite primary key (partition key and sort key), you must provide exactly two elements, in this order: The first element must have a KeyType of HASH, and the second element must have a KeyType of RANGE.

You can setup Secondary Indexes in DynamoDB

1
votes

There are two issues in your code:

  1. As already pointed out, you can't have more then two key attributes unless using global or local secondary indies.
  2. dynamodb.Table('GoodTable') is incorrect as you need resource.

You can check the modified code:

table = dynamodb.create_table(
    TableName='GoodTable',
            KeySchema=[
            {
                'AttributeName': 'Name',
                'KeyType': 'HASH'  
            }
        ],
    AttributeDefinitions=[
    {
      "AttributeName": "Name",
      "AttributeType": "S"
    }   
    ],
  ProvisionedThroughput={
    "ReadCapacityUnits": 1,
    "WriteCapacityUnits": 1
  }
)
import time
time.sleep(20)



table = boto3.resource('dynamodb').Table('GoodTable')

response = table.put_item(
    Item= {
        'Name': 'GoodName', 
        'Instance Type': 't2.micro',
    }
)
response = table.put_item(
    Item= {
        'Name': 'GoodName2',       
        'Instance Type': 't2.micro',
    }
)
response = table.put_item(
    Item= {
        'Name': 'GoodName3',       
        'Region': 'us-east-1',
    }
)
response = table.put_item(
    Item= {
        'Name': 'GoodName4',       
        'Volume Size': '20',
    }
)

1
votes

The field name is "KeySchema" not "TableSchema" or anything else, it defines only the key. The table is "schema-less", which means that each record can have a different structure and there is no need to define it. You must define only the key. In DynamoDB the key is either only a HASH column or HASH + RANGE columns. You should think about which of those two possibilities you want to use. If you use HASH + RANGE you have to query the table with both as well. Reading many records is costly.

So think a bit about what you want to store and how you would query that. Design the hash key accordingly.

There is a big argument from the single table complex hash key data model by the AWS Principal NoSQL technologist Rick Houlihan https://youtu.be/HaEPXoXVf2k?t=2573 . When I watched the video I started designing my DynamoDB tables differently and it improved my life.

Then natural tendency is to select one column which is kind of unique and use it as a hash key, but it really limits your query options. A well designed hash key can help you querying without additional indexes, so your solution is cheaper and more efficient.

As I mentioned above - beside the key, there is no structure defined. But it does not mean that each record should be completely random. However, it does make sense to store multiple item types in one table where each item type has the same structure - see the video, it's worth it.

In your case - using the instance name as a hash can be risky, because it may not be unique across the regions. You can even have two instances with the same name in the same region, because the name is just a tag. If you do not know or do not want to store the instance ID you have to come up with some other clever solution.

For example the hash can be: INSTANCE:: and the sort key can be instance creation time. There is an additional work to compose and decompose the key for each record. I solved it by creating a python class which wraps put_item/get_item in a method which handles the keys.