Skip to content

Instantly share code, notes, and snippets.

@fumin
Created April 14, 2017 18:48
Show Gist options
  • Save fumin/2351df13ca28588992455110408ed6b2 to your computer and use it in GitHub Desktop.
Save fumin/2351df13ca28588992455110408ed6b2 to your computer and use it in GitHub Desktop.
Demonstration that DynamoDB automatically collates String fields, which is undesirable.
package main
import (
"flag"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/pkg/errors"
"github.com/golang/glog"
)
func createClient() (*dynamodb.DynamoDB, error) {
awsConfig := &aws.Config{}
awsConfig.Region = aws.String("us-east-1")
awsConfig.Endpoint = aws.String("http://127.0.0.1:8000/")
awsConfig.Credentials = credentials.NewStaticCredentials("abcd", "unused", "")
awsConfig.CredentialsChainVerboseErrors = aws.Bool(true)
sess, err := session.NewSession(awsConfig)
if err != nil {
return nil, errors.Wrap(err, "NewSession")
}
ddbClient := dynamodb.New(sess)
return ddbClient, nil
}
func createTable(ddbClient *dynamodb.DynamoDB, tableName string) error {
input := &dynamodb.CreateTableInput{
TableName: aws.String(tableName),
AttributeDefinitions: []*dynamodb.AttributeDefinition{
{AttributeName: aws.String("I"), AttributeType: aws.String("S")},
},
KeySchema: []*dynamodb.KeySchemaElement{
{AttributeName: aws.String("I"), KeyType: aws.String("HASH")},
},
ProvisionedThroughput: &dynamodb.ProvisionedThroughput{
ReadCapacityUnits: aws.Int64(1),
WriteCapacityUnits: aws.Int64(1),
},
}
if _, err := ddbClient.CreateTable(input); err != nil {
return errors.Wrap(err, "CreateTable")
}
return nil
}
func getItem(ddbClient *dynamodb.DynamoDB, tableName string, id []byte) (*dynamodb.GetItemOutput, error) {
key := make(map[string]*dynamodb.AttributeValue)
key["I"] = &dynamodb.AttributeValue{S: aws.String(string(id))}
getInput := &dynamodb.GetItemInput{
TableName: aws.String(tableName),
Key: key,
}
output, err := ddbClient.GetItem(getInput)
if err != nil {
return nil, errors.Wrap(err, "GetItem")
}
return output, nil
}
func deleteItem(ddbClient *dynamodb.DynamoDB, tableName string, id []byte) (*dynamodb.DeleteItemOutput, error) {
key := make(map[string]*dynamodb.AttributeValue)
key["I"] = &dynamodb.AttributeValue{S: aws.String(string(id))}
input := &dynamodb.DeleteItemInput{
TableName: aws.String(tableName),
Key: key,
}
output, err := ddbClient.DeleteItem(input)
if err != nil {
return nil, errors.Wrap(err, "DeleteItem")
}
return output, nil
}
func foo() error {
ddbClient, err := createClient()
if err != nil {
return errors.Wrap(err, "createClient")
}
tableName := "qqoo"
if err := createTable(ddbClient, tableName); err != nil {
//return errors.Wrap(err, "createTable")
}
// Put an item that contains invalid utf-8 strings.
item := make(map[string]*dynamodb.AttributeValue)
invalidUTF8 := []byte{195, 40}
item["I"] = &dynamodb.AttributeValue{S: aws.String(string(invalidUTF8))}
item["Q"] = &dynamodb.AttributeValue{SS: aws.StringSlice([]string{string(invalidUTF8)})}
putInput := &dynamodb.PutItemInput{
TableName: aws.String(tableName),
Item: item,
}
_, err = ddbClient.PutItem(putInput)
if err != nil {
return errors.Wrap(err, "PutItem")
}
// Show that DynamoDB automatically collates the invalid utf-8 string,
// which is undesirable.
output, err := getItem(ddbClient, tableName, invalidUTF8)
if err != nil {
return errors.Wrap(err, "GetItem")
}
glog.Infof("output: %+v %+v", []byte(*output.Item["I"].S), []byte(*output.Item["Q"].SS[0]))
// Attempt to update the item using a string that is different
// than the original invalid utf-8 string.
key := make(map[string]*dynamodb.AttributeValue)
collated := []byte{239, 191, 189, 40}
key["I"] = &dynamodb.AttributeValue{S: aws.String(string(collated))}
updateEav := make(map[string]*dynamodb.AttributeValue)
updateEav[":q"] = &dynamodb.AttributeValue{SS: aws.StringSlice([]string{string(collated)})}
updateInput := &dynamodb.UpdateItemInput{
TableName: aws.String(tableName),
Key: key,
ExpressionAttributeValues: updateEav,
UpdateExpression: aws.String("DELETE Q :q"),
}
if _, err := ddbClient.UpdateItem(updateInput); err != nil {
return errors.Wrap(err, "UpdateItem")
}
// Show that we can use different strings to update StringSet fields in items.
output2, err := getItem(ddbClient, tableName, invalidUTF8)
if err != nil {
return errors.Wrap(err, "GetItem")
}
glog.Infof("output2: %+v %+v", []byte(*output2.Item["I"].S), output2.Item)
// Show than we can use different strings to delete items.
if _, err := deleteItem(ddbClient, tableName, collated); err != nil {
return errors.Wrap(err, "DelItem")
}
return nil
}
func main() {
flag.Parse()
if err := foo(); err != nil {
glog.Fatalf("%+v", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment