Blog

DynamoDB Transactions

28 Nov, 2018
Xebia Background Header Wave

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.

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts