×
AWS re:Invent 2018 DynamoDB Transactions

DynamoDB Transactions

Fork me on GitHub

At re:Invent 2018 AWS has released Amazon DynamoDB Support for Transactions. DynamoDB transactions is described in the AWS News Blog. Transactions provide atomicity, consistency, isolation, and durability (ACID) in DynamoDB, enabling you to maintain data correctness in your applications more easily. Using transactions, you can support sophisticated workflows and business logic that require adding, updating, or deleting multiple items as a single, all-or-nothing operation. Lets take a look!

New Operations

To support DynamoDB transactions, AWS has added two new operations to the API:

  • TransactWriteItems: a batch operation that contains a write set, with one or more PutItem, UpdateItem, and DeleteItem operations. TransactWriteItems can optionally check for prerequisite conditions that must be satisfied before making updates. These conditions may involve the same or different items than those in the write set. If any condition is not met, the transaction is rejected.
  • TransactGetItems: a batch operation that contains a read set, with one or more GetItem operations. If a TransactGetItems request is issued on an item that is part of an active write transaction, the read transaction is canceled. To get the previously committed value, you can use a standard read.

Example

The following example uses the Go API and writes to the table Owner and Cats using the TransactWriteItems API. After a succesful write, a TransactGetItems API call reads from the same two tables and writes the result to the console.

package main

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
	"log"
)

type Cat struct {
	ID      string
	Name    string
	Age     int
	OwnerID string
}

type Owner struct {
	ID     string
	Name   string
	Age    int
	CatIds []string
}

func CreateDynamoDBClient(region string) (*dynamodb.DynamoDB, error) {
	sess, err := session.NewSession()
	if err != nil {
		return nil, err
	}
	svc := dynamodb.New(sess, aws.NewConfig().WithRegion(region))
	return svc, nil
}

func main() {
	owner := Owner{
		ID:     "1",
		Name:   "Dennis",
		Age:    42,
		CatIds: []string{"1", "2"},
	}
	elsa := Cat{
		ID:      "1",
		Name:    "Elsa",
		Age:     16,
		OwnerID: "1",
	}
	tijger := Cat{
		ID:      "2",
		Name:    "Tijger",
		Age:     12,
		OwnerID: "1",
	}
	OwnerAV, err := dynamodbattribute.MarshalMap(owner)
	if err != nil {
		log.Fatal(err)
	}
	ElsaAV, err2 := dynamodbattribute.MarshalMap(elsa)
	if err2 != nil {
		log.Fatal(err)
	}
	TijgerAV, err3 := dynamodbattribute.MarshalMap(tijger)
	if err != nil {
		log.Fatal(err3)
	}
	svc, err := CreateDynamoDBClient("eu-west-1")
	if err != nil {
		log.Fatal(err)
	}
	_, err4 := svc.TransactWriteItems(&dynamodb.TransactWriteItemsInput{
		TransactItems: []*dynamodb.TransactWriteItem{
			{
				Put: &dynamodb.Put{
					TableName: aws.String("Owners"),
					Item:      OwnerAV,
				},
			},
			{
				Put: &dynamodb.Put{
					TableName: aws.String("Cats"),
					Item:      ElsaAV,
				},
			},
			{
				Put: &dynamodb.Put{
					TableName: aws.String("Cats"),
					Item:      TijgerAV,
				},
			},
		},
	})
	if err != nil {
		log.Fatal(err4)
	}
	res, err5 := svc.TransactGetItems(&dynamodb.TransactGetItemsInput{
		TransactItems: []*dynamodb.TransactGetItem{
			{
				Get: &dynamodb.Get{
					TableName: aws.String("Cats"),
					Key: map[string]*dynamodb.AttributeValue{
						"ID": {
							S: aws.String("1"),
						},
					},
				},
			},
			{
				Get: &dynamodb.Get{
					TableName: aws.String("Cats"),
					Key: map[string]*dynamodb.AttributeValue{
						"ID": {
							S: aws.String("2"),
						},
					},
				},
			},
			{
				Get: &dynamodb.Get{
					TableName: aws.String("Owners"),
					Key: map[string]*dynamodb.AttributeValue{
						"ID": {
							S: aws.String("1"),
						},
					},
				},
			},
		},

	})
	if err != nil {
		log.Fatal(err5)
	}

	for _, item := range res.Responses {
		fmt.Print(item.Item)
	}
}

Conclusion

DynamoDB Transactions is provided by the operations TransactWriteItems and TransactGetItems. By having ACID guarantees, DynamoDB can be used to keep two or more region-tables consistent and opens the way for new cloud scale data architectures using DynamoDB.

Picture of Dennis Vriend
Dennis Vriend
Cloud Consultant