DynamoDB Transactions

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.

<span style="font-weight:bold">package</span> main

<span style="font-weight:bold">import</span> (
    <span style="color:#b84">"fmt"</span>
    <span style="color:#b84">"github.com/aws/aws-sdk-go/aws"</span>
    <span style="color:#b84">"github.com/aws/aws-sdk-go/aws/session"</span>
    <span style="color:#b84">"github.com/aws/aws-sdk-go/service/dynamodb"</span>
    <span style="color:#b84">"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"</span>
    <span style="color:#b84">"log"</span>
)

<span style="font-weight:bold">type</span> Cat <span style="font-weight:bold">struct</span> {
    ID      <span style="color:#458;font-weight:bold">string</span>
    Name    <span style="color:#458;font-weight:bold">string</span>
    Age     <span style="color:#458;font-weight:bold">int</span>
    OwnerID <span style="color:#458;font-weight:bold">string</span>
}

<span style="font-weight:bold">type</span> Owner <span style="font-weight:bold">struct</span> {
    ID     <span style="color:#458;font-weight:bold">string</span>
    Name   <span style="color:#458;font-weight:bold">string</span>
    Age    <span style="color:#458;font-weight:bold">int</span>
    CatIds []<span style="color:#458;font-weight:bold">string</span>
}

<span style="font-weight:bold">func</span> <span style="color:#900;font-weight:bold">CreateDynamoDBClient</span>(region <span style="color:#458;font-weight:bold">string</span>) (<span style="font-weight:bold">*</span>dynamodb.DynamoDB, <span style="color:#458;font-weight:bold">error</span>) {
    sess, err <span style="font-weight:bold">:=</span> session.<span style="color:#900;font-weight:bold">NewSession</span>()
    <span style="font-weight:bold">if</span> err <span style="font-weight:bold">!=</span> <span style="font-weight:bold">nil</span> {
        <span style="font-weight:bold">return</span> <span style="font-weight:bold">nil</span>, err
    }
    svc <span style="font-weight:bold">:=</span> dynamodb.<span style="color:#900;font-weight:bold">New</span>(sess, aws.<span style="color:#900;font-weight:bold">NewConfig</span>().<span style="color:#900;font-weight:bold">WithRegion</span>(region))
    <span style="font-weight:bold">return</span> svc, <span style="font-weight:bold">nil</span>
}

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

    })
    <span style="font-weight:bold">if</span> err <span style="font-weight:bold">!=</span> <span style="font-weight:bold">nil</span> {
        log.<span style="color:#900;font-weight:bold">Fatal</span>(err5)
    }

    <span style="font-weight:bold">for</span> _, item <span style="font-weight:bold">:=</span> <span style="font-weight:bold">range</span> res.Responses {
        fmt.<span style="color:#900;font-weight:bold">Print</span>(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.

Share this article: Tweet this post / Post on LinkedIn