Tutorials: Creating global tables - Amazon DynamoDB
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China (PDF).

Tutorials: Creating global tables

This section provides step-by-step instructions for creating DynamoDB global tables configured for your preferred consistency mode. Choose either Multi-Region Eventual Consistency (MREC) or Multi-Region Strong Consistency (MRSC) modes based on your application's requirements.

MREC global tables provide lower write latency with eventual consistency across Amazon Web Services Regions. MRSC global tables provide strongly consistent reads across Regions with slightly higher write latencies than MREC. Choose the consistency mode that best meets your application's needs for data consistency, latency, and availability.

Creating a global table configured for MREC

This section shows how to create a global table with Multi-Region Eventual Consistency (MREC) mode. MREC is the default consistency mode for global tables and provides low-latency writes with asynchronous replication across Amazon Web Services Regions. Changes made to an item in one region are typically replicated to all other regions within a second. This makes MREC ideal for applications that prioritize low write latency and can tolerate brief periods where different Regions may return slightly different versions of data.

You can create MREC global tables with replicas in any Amazon Region where DynamoDB is available and add or remove replicas at any time. The following examples show how to create an MREC global table with replicas in multiple regions.

Follow these steps to create a global table using the Amazon Web Services Management Console. The following example creates a global table with replica tables in the United States and Europe.

  1. Sign in to the Amazon Web Services Management Console and open the DynamoDB console at https://console.amazonaws.cn/dynamodb/.

  2. For this example, choose US East (Ohio) from the Region selector in the navigation bar.

  3. In the navigation pane on the left side of the console, choose Tables.

  4. Choose Create Table.

  5. On the Create table page:

    1. For Table name, enter Music.

    2. For Partition key, enter Artist.

    3. For Sort key, enter SongTitle.

    4. Keep the other default settings and choose Create table.

      This new table serves as the first replica table in a new global table. It is the prototype for other replica tables that you add later.

  6. After the table becomes active:

    1. Select the Music table from the tables list.

    2. Choose the Global tables tab.

    3. Choose Create replica.

  7. From the Available replication Regions dropdown list, choose US West (Oregon) us-west-2.

    The console ensures that a table with the same name doesn't exist in the selected Region. If a table with the same name does exist, you must delete the existing table before you can create a new replica table in that Region.

  8. Choose Create replica. This starts the table creation process in the US West (Oregon) us-west-2 Region.

    The Global tables tab for the Music table (and for any other replica tables) shows that the table has been replicated in multiple Regions.

  9. Add another region by repeating the previous steps, but choose Europe (Frankfurt) eu-central-1 as the region.

  10. To test replication:

    1. Make sure you're using the Amazon Web Services Management Console in the US East (Ohio) Region.

    2. Choose Explore table items.

    3. Choose Create item.

    4. Enter item_1 for Artist and Song Value 1 for SongTitle.

    5. Choose Create item.

  11. Verify replication by switching to the other regions:

    1. From the Region selector in the upper-right corner, choose Europe (Frankfurt).

    2. Verify that the Music table contains the item you created.

    3. Repeat the verification for US West (Oregon).

CLI

The following code example shows how to manage DynamoDB global tables with multi-Region replication with eventual consistency (MREC).

  • Create a table with multi-Region replication (MREC).

  • Put and get items from replica tables.

  • Remove replicas one-by-one.

  • Clean up by deleting the table.

Amazon CLI with Bash script

Create a table with multi-Region replication.

# Step 1: Create a new table (MusicTable) in US East (Ohio), with DynamoDB Streams enabled (NEW_AND_OLD_IMAGES) aws dynamodb create-table \ --table-name MusicTable \ --attribute-definitions \ AttributeName=Artist,AttributeType=S \ AttributeName=SongTitle,AttributeType=S \ --key-schema \ AttributeName=Artist,KeyType=HASH \ AttributeName=SongTitle,KeyType=RANGE \ --billing-mode PAY_PER_REQUEST \ --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \ --region us-east-2 # Step 2: Create an identical MusicTable table in US East (N. Virginia) aws dynamodb update-table --table-name MusicTable --cli-input-json \ '{ "ReplicaUpdates": [ { "Create": { "RegionName": "us-east-1" } } ] }' \ --region us-east-2 # Step 3: Create a table in Europe (Ireland) aws dynamodb update-table --table-name MusicTable --cli-input-json \ '{ "ReplicaUpdates": [ { "Create": { "RegionName": "eu-west-1" } } ] }' \ --region us-east-2

Describe the multi-Region table.

# Step 4: View the list of replicas created using describe-table aws dynamodb describe-table \ --table-name MusicTable \ --region us-east-2 \ --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*].{Region:RegionName,Status:ReplicaStatus}}'

Put items in a replica table.

# Step 5: To verify that replication is working, add a new item to the Music table in US East (Ohio) aws dynamodb put-item \ --table-name MusicTable \ --item '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \ --region us-east-2

Get items from replica tables.

# Step 6: Wait for a few seconds, and then check to see whether the item has been # successfully replicated to US East (N. Virginia) and Europe (Ireland) aws dynamodb get-item \ --table-name MusicTable \ --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \ --region us-east-1 aws dynamodb get-item \ --table-name MusicTable \ --key '{"Artist": {"S":"item_1"},"SongTitle": {"S":"Song Value 1"}}' \ --region eu-west-1

Remove replicas.

# Step 7: Delete the replica table in Europe (Ireland) Region aws dynamodb update-table --table-name MusicTable --cli-input-json \ '{ "ReplicaUpdates": [ { "Delete": { "RegionName": "eu-west-1" } } ] }' \ --region us-east-2 # Delete the replica table in US East (N. Virginia) Region aws dynamodb update-table --table-name MusicTable --cli-input-json \ '{ "ReplicaUpdates": [ { "Delete": { "RegionName": "us-east-1" } } ] }' \ --region us-east-2

Clean up by deleting the table.

# Clean up: Delete the primary table aws dynamodb delete-table --table-name MusicTable --region us-east-2 echo "Global table demonstration complete."
Java

The following code example shows how to create and manage DynamoDB global tables with replicas across multiple Regions.

  • Create a table with Global Secondary Index and DynamoDB Streams.

  • Add replicas in different Regions to create a global table.

  • Remove replicas from a global table.

  • Add test items to verify replication across Regions.

  • Describe global table configuration and replica status.

SDK for Java 2.x

Create a table with Global Secondary Index and DynamoDB Streams using Amazon SDK for Java 2.x.

public static CreateTableResponse createTableWithGSI( final DynamoDbClient dynamoDbClient, final String tableName, final String indexName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (indexName == null || indexName.trim().isEmpty()) { throw new IllegalArgumentException("Index name cannot be null or empty"); } try { LOGGER.info("Creating table: " + tableName + " with GSI: " + indexName); CreateTableRequest createTableRequest = CreateTableRequest.builder() .tableName(tableName) .attributeDefinitions( AttributeDefinition.builder() .attributeName("Artist") .attributeType(ScalarAttributeType.S) .build(), AttributeDefinition.builder() .attributeName("SongTitle") .attributeType(ScalarAttributeType.S) .build()) .keySchema( KeySchemaElement.builder() .attributeName("Artist") .keyType(KeyType.HASH) .build(), KeySchemaElement.builder() .attributeName("SongTitle") .keyType(KeyType.RANGE) .build()) .billingMode(BillingMode.PAY_PER_REQUEST) .globalSecondaryIndexes(GlobalSecondaryIndex.builder() .indexName(indexName) .keySchema(KeySchemaElement.builder() .attributeName("SongTitle") .keyType(KeyType.HASH) .build()) .projection( Projection.builder().projectionType(ProjectionType.ALL).build()) .build()) .streamSpecification(StreamSpecification.builder() .streamEnabled(true) .streamViewType(StreamViewType.NEW_AND_OLD_IMAGES) .build()) .build(); CreateTableResponse response = dynamoDbClient.createTable(createTableRequest); LOGGER.info("Table creation initiated. Status: " + response.tableDescription().tableStatus()); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to create table: " + tableName + " - " + e.getMessage()); throw e; } }

Wait for a table to become active using Amazon SDK for Java 2.x.

public static void waitForTableActive(final DynamoDbClient dynamoDbClient, final String tableName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } try { LOGGER.info("Waiting for table to become active: " + tableName); try (DynamoDbWaiter waiter = DynamoDbWaiter.builder().client(dynamoDbClient).build()) { DescribeTableRequest request = DescribeTableRequest.builder().tableName(tableName).build(); waiter.waitUntilTableExists(request); LOGGER.info("Table is now active: " + tableName); } } catch (DynamoDbException e) { LOGGER.severe("Failed to wait for table to become active: " + tableName + " - " + e.getMessage()); throw e; } }

Add a replica to create or extend a global table using Amazon SDK for Java 2.x.

public static UpdateTableResponse addReplica( final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion, final String indexName, final Long readCapacity) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } if (indexName == null || indexName.trim().isEmpty()) { throw new IllegalArgumentException("Index name cannot be null or empty"); } if (readCapacity == null || readCapacity <= 0) { throw new IllegalArgumentException("Read capacity must be a positive number"); } try { LOGGER.info("Adding replica in region: " + replicaRegion.id() + " for table: " + tableName); // Create a ReplicationGroupUpdate for adding a replica ReplicationGroupUpdate replicationGroupUpdate = ReplicationGroupUpdate.builder() .create(builder -> builder.regionName(replicaRegion.id()) .globalSecondaryIndexes(ReplicaGlobalSecondaryIndex.builder() .indexName(indexName) .provisionedThroughputOverride(ProvisionedThroughputOverride.builder() .readCapacityUnits(readCapacity) .build()) .build()) .build()) .build(); UpdateTableRequest updateTableRequest = UpdateTableRequest.builder() .tableName(tableName) .replicaUpdates(replicationGroupUpdate) .build(); UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest); LOGGER.info("Replica addition initiated in region: " + replicaRegion.id()); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to add replica in region: " + replicaRegion.id() + " - " + e.getMessage()); throw e; } }

Remove a replica from a global table using Amazon SDK for Java 2.x.

public static UpdateTableResponse removeReplica( final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } try { LOGGER.info("Removing replica in region: " + replicaRegion.id() + " for table: " + tableName); // Create a ReplicationGroupUpdate for removing a replica ReplicationGroupUpdate replicationGroupUpdate = ReplicationGroupUpdate.builder() .delete(builder -> builder.regionName(replicaRegion.id()).build()) .build(); UpdateTableRequest updateTableRequest = UpdateTableRequest.builder() .tableName(tableName) .replicaUpdates(replicationGroupUpdate) .build(); UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest); LOGGER.info("Replica removal initiated in region: " + replicaRegion.id()); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to remove replica in region: " + replicaRegion.id() + " - " + e.getMessage()); throw e; } }

Add test items to verify replication using Amazon SDK for Java 2.x.

public static PutItemResponse putTestItem( final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (artist == null || artist.trim().isEmpty()) { throw new IllegalArgumentException("Artist cannot be null or empty"); } if (songTitle == null || songTitle.trim().isEmpty()) { throw new IllegalArgumentException("Song title cannot be null or empty"); } try { LOGGER.info("Adding test item to table: " + tableName); Map<String, software.amazon.awssdk.services.dynamodb.model.AttributeValue> item = new HashMap<>(); item.put( "Artist", software.amazon.awssdk.services.dynamodb.model.AttributeValue.builder() .s(artist) .build()); item.put( "SongTitle", software.amazon.awssdk.services.dynamodb.model.AttributeValue.builder() .s(songTitle) .build()); PutItemRequest putItemRequest = PutItemRequest.builder().tableName(tableName).item(item).build(); PutItemResponse response = dynamoDbClient.putItem(putItemRequest); LOGGER.info("Test item added successfully"); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to add test item to table: " + tableName + " - " + e.getMessage()); throw e; } }

Describe global table configuration and replicas using Amazon SDK for Java 2.x.

public static DescribeTableResponse describeTable(final DynamoDbClient dynamoDbClient, final String tableName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } try { LOGGER.info("Describing table: " + tableName); DescribeTableRequest request = DescribeTableRequest.builder().tableName(tableName).build(); DescribeTableResponse response = dynamoDbClient.describeTable(request); LOGGER.info("Table status: " + response.table().tableStatus()); if (response.table().replicas() != null && !response.table().replicas().isEmpty()) { LOGGER.info("Number of replicas: " + response.table().replicas().size()); response.table() .replicas() .forEach(replica -> LOGGER.info( "Replica region: " + replica.regionName() + ", Status: " + replica.replicaStatus())); } return response; } catch (ResourceNotFoundException e) { LOGGER.severe("Table not found: " + tableName + " - " + e.getMessage()); throw e; } catch (DynamoDbException e) { LOGGER.severe("Failed to describe table: " + tableName + " - " + e.getMessage()); throw e; } }

Complete example of global table operations using Amazon SDK for Java 2.x.

public static void exampleUsage(final Region sourceRegion, final Region replicaRegion) { String tableName = "Music"; String indexName = "SongTitleIndex"; Long readCapacity = 15L; // Create DynamoDB client for the source region try (DynamoDbClient dynamoDbClient = DynamoDbClient.builder().region(sourceRegion).build()) { try { // Step 1: Create the initial table with GSI and streams LOGGER.info("Step 1: Creating table in source region: " + sourceRegion.id()); createTableWithGSI(dynamoDbClient, tableName, indexName); // Step 2: Wait for table to become active LOGGER.info("Step 2: Waiting for table to become active"); waitForTableActive(dynamoDbClient, tableName); // Step 3: Add replica in destination region LOGGER.info("Step 3: Adding replica in region: " + replicaRegion.id()); addReplica(dynamoDbClient, tableName, replicaRegion, indexName, readCapacity); // Step 4: Wait a moment for replica creation to start Thread.sleep(5000); // Step 5: Describe table to view replica information LOGGER.info("Step 5: Describing table to view replicas"); describeTable(dynamoDbClient, tableName); // Step 6: Add a test item to verify replication LOGGER.info("Step 6: Adding test item to verify replication"); putTestItem(dynamoDbClient, tableName, "TestArtist", "TestSong"); LOGGER.info("Global table setup completed successfully!"); LOGGER.info("You can verify replication by checking the item in region: " + replicaRegion.id()); // Step 7: Remove replica and clean up table LOGGER.info("Step 7: Removing replica from region: " + replicaRegion.id()); removeReplica(dynamoDbClient, tableName, replicaRegion); DeleteTableResponse deleteTableResponse = dynamoDbClient.deleteTable( DeleteTableRequest.builder().tableName(tableName).build()); LOGGER.info("MREC global table demonstration completed successfully!"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Thread was interrupted", e); } catch (DynamoDbException e) { LOGGER.severe("DynamoDB operation failed: " + e.getMessage()); throw e; } } }

Creating a global table configured for MRSC

This section shows you how to create a Multi-Region Strong Consistency (MRSC) global table. MRSC global tables synchronously replicate item changes across Regions, ensuring that strongly consistent read operations on any replica always return the latest version of an item.

You can configure a MRSC global table with three replicas, or two replicas and one witness. When creating a MRSC global table, you choose the Regions where replicas and an optional witness are deployed. The following example creates an MRSC global table with replicas in the US East (N. Virginia) and US East (Ohio) Regions, with a witness in the US West (Oregon) Region.

Follow these steps to create about MRSC global table using the Amazon Web Services Management Console.

  1. Sign in to the Amazon Web Services Management Console and open the DynamoDB console at https://console.amazonaws.cn/dynamodb/.

  2. From the Region selector in the navigation bar, choose a Region where global tables with MRSC is supported, such as us-west-2.

  3. In the navigation pane, choose Tables.

  4. Choose Create table.

  5. On the Create table page:

    1. For Table name, enter Music.

    2. For Partition key, enter Artist and keep the default String type.

    3. For Sort key, enter SongTitle and keep the default String type.

    4. Keep the other default settings and choose Create table

      This new table serves as the first replica table in a new global table. It is the prototype for other replica tables that you add later.

  6. Wait for the table to become active, then select it from the tables list.

  7. Choose the Global tables tab, then choose Create replica.

  8. On the Create replica page:

    1. Under Multi-Region Consistency, choose Strong consistency.

    2. For Replication Region 1, choose US East (N. Virginia) us-east-1.

    3. For Replication Region 2, choose US West (Oregon) us-west-2.

    4. Check Configure as Witness for the US West (Oregon) region.

    5. Choose Create replicas.

  9. Wait for the replica and witness creation process to complete. The replica status will show as Active when the table is ready to use.

Before you start, ensure that the IAM principal has the required permissions to create a MRSC global table with a witness Region. The IAM principal must be authorized to invoke: CreateGlobalTableWitness, CreateTable, and CreateTableReplica.

Here is a sample IAM policy to successfully create a DynamoDB table (MusicMRSC) with replica in US East (N. Virginia) and a witness Region in US West (Oregon). :

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:UpdateTable", "dynamodb:CreateTable", "dynamodb:CreateTableReplica", "dynamodb:CreateGlobalTableWitness" ], "Resource": [ "arn:aws:dynamodb:us-west-2:123456789123:table/MusicMRSC", "arn:aws:dynamodb:us-east-1:123456789123:table/MusicMRSC" ] } ] }

The following code examples show how to create and manage DynamoDB global tables with Multi-Region Strong Consistency (MRSC).

  • Create a table with Multi-Region Strong Consistency.

  • Verify MRSC configuration and replica status.

  • Test strong consistency across Regions with immediate reads.

  • Perform conditional writes with MRSC guarantees.

  • Clean up MRSC global table resources.

Bash
Amazon CLI with Bash script

Create a table with Multi-Region Strong Consistency.

# Step 1: Create a new table in us-east-2 (primary region for MRSC) # Note: Table must be empty when enabling MRSC aws dynamodb create-table \ --table-name MusicTable \ --attribute-definitions \ AttributeName=Artist,AttributeType=S \ AttributeName=SongTitle,AttributeType=S \ --key-schema \ AttributeName=Artist,KeyType=HASH \ AttributeName=SongTitle,KeyType=RANGE \ --billing-mode PAY_PER_REQUEST \ --region us-east-2 # Wait for table to become active aws dynamodb wait table-exists --table-name MusicTable --region us-east-2 # Step 2: Add replica and witness with Multi-Region Strong Consistency # MRSC requires exactly three replicas in supported regions aws dynamodb update-table \ --table-name MusicTable \ --replica-updates '[{"Create": {"RegionName": "us-east-1"}}]' \ --global-table-witness-updates '[{"Create": {"RegionName": "us-west-2"}}]' \ --multi-region-consistency STRONG \ --region us-east-2

Verify MRSC configuration and replica status.

# Verify the global table configuration and MRSC setting aws dynamodb describe-table \ --table-name MusicTable \ --region us-east-2 \ --query 'Table.{TableName:TableName,TableStatus:TableStatus,MultiRegionConsistency:MultiRegionConsistency,Replicas:Replicas[*],GlobalTableWitnesses:GlobalTableWitnesses[*].{Region:RegionName,Status:ReplicaStatus}}'

Test strong consistency with immediate reads across Regions.

# Write an item to the primary region aws dynamodb put-item \ --table-name MusicTable \ --item '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"},"Album": {"S":"The Beatles 1967-1970"},"Year": {"N":"1968"}}' \ --region us-east-2 # Read the item from replica region to verify strong consistency (cannot read or write to witness) # No wait time needed - MRSC provides immediate consistency echo "Reading from us-east-1 (immediate consistency):" aws dynamodb get-item \ --table-name MusicTable \ --key '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"}}' \ --consistent-read \ --region us-east-1

Perform conditional writes with MRSC guarantees.

# Perform a conditional update from a different region # This demonstrates that conditions work consistently across all regions aws dynamodb update-item \ --table-name MusicTable \ --key '{"Artist": {"S":"The Beatles"},"SongTitle": {"S":"Hey Jude"}}' \ --update-expression "SET #rating = :rating" \ --condition-expression "attribute_exists(Artist)" \ --expression-attribute-names '{"#rating": "Rating"}' \ --expression-attribute-values '{":rating": {"N":"5"}}' \ --region us-east-1

Clean up MRSC global table resources.

# Remove replica tables (must be done before deleting the primary table) aws dynamodb update-table \ --table-name MusicTable \ --replica-updates '[{"Delete": {"RegionName": "us-east-1"}}]' \ --global-table-witness-updates '[{"Delete": {"RegionName": "us-west-2"}}]' \ --region us-east-2 # Wait for replicas to be deleted echo "Waiting for replicas to be deleted..." sleep 30 # Delete the primary table aws dynamodb delete-table \ --table-name MusicTable \ --region us-east-2
Java
SDK for Java 2.x

Create a regional table ready for MRSC conversion using Amazon SDK for Java 2.x.

public static CreateTableResponse createRegionalTable(final DynamoDbClient dynamoDbClient, final String tableName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } try { LOGGER.info("Creating regional table: " + tableName + " (must be empty for MRSC)"); CreateTableRequest createTableRequest = CreateTableRequest.builder() .tableName(tableName) .attributeDefinitions( AttributeDefinition.builder() .attributeName("Artist") .attributeType(ScalarAttributeType.S) .build(), AttributeDefinition.builder() .attributeName("SongTitle") .attributeType(ScalarAttributeType.S) .build()) .keySchema( KeySchemaElement.builder() .attributeName("Artist") .keyType(KeyType.HASH) .build(), KeySchemaElement.builder() .attributeName("SongTitle") .keyType(KeyType.RANGE) .build()) .billingMode(BillingMode.PAY_PER_REQUEST) .build(); CreateTableResponse response = dynamoDbClient.createTable(createTableRequest); LOGGER.info("Regional table creation initiated. Status: " + response.tableDescription().tableStatus()); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to create regional table: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to create regional table: " + tableName) .cause(e) .build(); } }

Convert a regional table to MRSC with replicas and witness using Amazon SDK for Java 2.x.

public static UpdateTableResponse convertToMRSCWithWitness( final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion, final Region witnessRegion) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } if (witnessRegion == null) { throw new IllegalArgumentException("Witness region cannot be null"); } try { LOGGER.info("Converting table to MRSC with replica in " + replicaRegion.id() + " and witness in " + witnessRegion.id()); // Create replica update using ReplicationGroupUpdate ReplicationGroupUpdate replicaUpdate = ReplicationGroupUpdate.builder() .create(CreateReplicationGroupMemberAction.builder() .regionName(replicaRegion.id()) .build()) .build(); // Create witness update GlobalTableWitnessGroupUpdate witnessUpdate = GlobalTableWitnessGroupUpdate.builder() .create(CreateGlobalTableWitnessGroupMemberAction.builder() .regionName(witnessRegion.id()) .build()) .build(); UpdateTableRequest updateTableRequest = UpdateTableRequest.builder() .tableName(tableName) .replicaUpdates(List.of(replicaUpdate)) .globalTableWitnessUpdates(List.of(witnessUpdate)) .multiRegionConsistency(MultiRegionConsistency.STRONG) .build(); UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest); LOGGER.info("MRSC conversion initiated. Status: " + response.tableDescription().tableStatus()); LOGGER.info("UpdateTableResponse full object: " + response); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to convert table to MRSC: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to convert table to MRSC: " + tableName) .cause(e) .build(); } }

Describe an MRSC global table configuration using Amazon SDK for Java 2.x.

public static DescribeTableResponse describeMRSCTable(final DynamoDbClient dynamoDbClient, final String tableName) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } try { LOGGER.info("Describing MRSC global table: " + tableName); DescribeTableRequest request = DescribeTableRequest.builder().tableName(tableName).build(); DescribeTableResponse response = dynamoDbClient.describeTable(request); LOGGER.info("Table status: " + response.table().tableStatus()); LOGGER.info("Multi-region consistency: " + response.table().multiRegionConsistency()); if (response.table().replicas() != null && !response.table().replicas().isEmpty()) { LOGGER.info("Number of replicas: " + response.table().replicas().size()); response.table() .replicas() .forEach(replica -> LOGGER.info( "Replica region: " + replica.regionName() + ", Status: " + replica.replicaStatus())); } if (response.table().globalTableWitnesses() != null && !response.table().globalTableWitnesses().isEmpty()) { LOGGER.info("Number of witnesses: " + response.table().globalTableWitnesses().size()); response.table() .globalTableWitnesses() .forEach(witness -> LOGGER.info( "Witness region: " + witness.regionName() + ", Status: " + witness.witnessStatus())); } return response; } catch (ResourceNotFoundException e) { LOGGER.severe("Table not found: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Table not found: " + tableName) .cause(e) .build(); } catch (DynamoDbException e) { LOGGER.severe("Failed to describe table: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to describe table: " + tableName) .cause(e) .build(); } }

Add test items to verify MRSC strong consistency using Amazon SDK for Java 2.x.

public static PutItemResponse putTestItem( final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle, final String album, final String year) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (artist == null || artist.trim().isEmpty()) { throw new IllegalArgumentException("Artist cannot be null or empty"); } if (songTitle == null || songTitle.trim().isEmpty()) { throw new IllegalArgumentException("Song title cannot be null or empty"); } try { LOGGER.info("Adding test item to MRSC global table: " + tableName); Map<String, AttributeValue> item = new HashMap<>(); item.put("Artist", AttributeValue.builder().s(artist).build()); item.put("SongTitle", AttributeValue.builder().s(songTitle).build()); if (album != null && !album.trim().isEmpty()) { item.put("Album", AttributeValue.builder().s(album).build()); } if (year != null && !year.trim().isEmpty()) { item.put("Year", AttributeValue.builder().n(year).build()); } PutItemRequest putItemRequest = PutItemRequest.builder().tableName(tableName).item(item).build(); PutItemResponse response = dynamoDbClient.putItem(putItemRequest); LOGGER.info("Test item added successfully with strong consistency"); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to add test item to table: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to add test item to table: " + tableName) .cause(e) .build(); } }

Read items with consistent reads from MRSC replicas using Amazon SDK for Java 2.x.

public static GetItemResponse getItemWithConsistentRead( final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (artist == null || artist.trim().isEmpty()) { throw new IllegalArgumentException("Artist cannot be null or empty"); } if (songTitle == null || songTitle.trim().isEmpty()) { throw new IllegalArgumentException("Song title cannot be null or empty"); } try { LOGGER.info("Reading item from MRSC global table with consistent read: " + tableName); Map<String, AttributeValue> key = new HashMap<>(); key.put("Artist", AttributeValue.builder().s(artist).build()); key.put("SongTitle", AttributeValue.builder().s(songTitle).build()); GetItemRequest getItemRequest = GetItemRequest.builder() .tableName(tableName) .key(key) .consistentRead(true) .build(); GetItemResponse response = dynamoDbClient.getItem(getItemRequest); if (response.hasItem()) { LOGGER.info("Item found with strong consistency - no wait time needed"); } else { LOGGER.info("Item not found"); } return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to read item from table: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to read item from table: " + tableName) .cause(e) .build(); } }

Perform conditional updates with MRSC guarantees using Amazon SDK for Java 2.x.

public static UpdateItemResponse performConditionalUpdate( final DynamoDbClient dynamoDbClient, final String tableName, final String artist, final String songTitle, final String rating) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (artist == null || artist.trim().isEmpty()) { throw new IllegalArgumentException("Artist cannot be null or empty"); } if (songTitle == null || songTitle.trim().isEmpty()) { throw new IllegalArgumentException("Song title cannot be null or empty"); } if (rating == null || rating.trim().isEmpty()) { throw new IllegalArgumentException("Rating cannot be null or empty"); } try { LOGGER.info("Performing conditional update on MRSC global table: " + tableName); Map<String, AttributeValue> key = new HashMap<>(); key.put("Artist", AttributeValue.builder().s(artist).build()); key.put("SongTitle", AttributeValue.builder().s(songTitle).build()); Map<String, String> expressionAttributeNames = new HashMap<>(); expressionAttributeNames.put("#rating", "Rating"); Map<String, AttributeValue> expressionAttributeValues = new HashMap<>(); expressionAttributeValues.put( ":rating", AttributeValue.builder().n(rating).build()); UpdateItemRequest updateItemRequest = UpdateItemRequest.builder() .tableName(tableName) .key(key) .updateExpression("SET #rating = :rating") .conditionExpression("attribute_exists(Artist)") .expressionAttributeNames(expressionAttributeNames) .expressionAttributeValues(expressionAttributeValues) .build(); UpdateItemResponse response = dynamoDbClient.updateItem(updateItemRequest); LOGGER.info("Conditional update successful - demonstrates strong consistency"); return response; } catch (ConditionalCheckFailedException e) { LOGGER.warning("Conditional check failed: " + e.getMessage()); throw e; } catch (DynamoDbException e) { LOGGER.severe("Failed to perform conditional update: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to perform conditional update: " + tableName) .cause(e) .build(); } }

Wait for MRSC replicas and witnesses to become active using Amazon SDK for Java 2.x.

public static void waitForMRSCReplicasActive( final DynamoDbClient dynamoDbClient, final String tableName, final int maxWaitTimeSeconds) throws InterruptedException { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (maxWaitTimeSeconds <= 0) { throw new IllegalArgumentException("Max wait time must be positive"); } try { LOGGER.info("Waiting for MRSC replicas and witnesses to become active: " + tableName); final long startTime = System.currentTimeMillis(); final long maxWaitTimeMillis = maxWaitTimeSeconds * 1000L; int backoffSeconds = 5; // Start with 5 second intervals final int maxBackoffSeconds = 30; // Cap at 30 seconds while (System.currentTimeMillis() - startTime < maxWaitTimeMillis) { DescribeTableResponse response = describeMRSCTable(dynamoDbClient, tableName); boolean allActive = true; StringBuilder statusReport = new StringBuilder(); if (response.table().multiRegionConsistency() == null || !MultiRegionConsistency.STRONG .toString() .equals(response.table().multiRegionConsistency().toString())) { allActive = false; statusReport .append("MultiRegionConsistency: ") .append(response.table().multiRegionConsistency()) .append(" "); } if (response.table().replicas() == null || response.table().replicas().isEmpty()) { allActive = false; statusReport.append("No replicas found. "); } if (response.table().globalTableWitnesses() == null || response.table().globalTableWitnesses().isEmpty()) { allActive = false; statusReport.append("No witnesses found. "); } // Check table status if (!"ACTIVE".equals(response.table().tableStatus().toString())) { allActive = false; statusReport .append("Table: ") .append(response.table().tableStatus()) .append(" "); } // Check replica status if (response.table().replicas() != null) { for (var replica : response.table().replicas()) { if (!"ACTIVE".equals(replica.replicaStatus().toString())) { allActive = false; statusReport .append("Replica(") .append(replica.regionName()) .append("): ") .append(replica.replicaStatus()) .append(" "); } } } // Check witness status if (response.table().globalTableWitnesses() != null) { for (var witness : response.table().globalTableWitnesses()) { if (!"ACTIVE".equals(witness.witnessStatus().toString())) { allActive = false; statusReport .append("Witness(") .append(witness.regionName()) .append("): ") .append(witness.witnessStatus()) .append(" "); } } } if (allActive) { LOGGER.info("All MRSC replicas and witnesses are now active: " + tableName); return; } LOGGER.info("Waiting for MRSC components to become active. Status: " + statusReport.toString()); LOGGER.info("Next check in " + backoffSeconds + " seconds..."); tempWait(backoffSeconds); // Exponential backoff with cap backoffSeconds = Math.min(backoffSeconds * 2, maxBackoffSeconds); } throw DynamoDbException.builder() .message("Timeout waiting for MRSC replicas to become active after " + maxWaitTimeSeconds + " seconds") .build(); } catch (DynamoDbException | InterruptedException e) { LOGGER.severe("Failed to wait for MRSC replicas to become active: " + tableName + " - " + e.getMessage()); throw e; } }

Clean up MRSC replicas and witnesses using Amazon SDK for Java 2.x.

public static UpdateTableResponse cleanupMRSCReplicas( final DynamoDbClient dynamoDbClient, final String tableName, final Region replicaRegion, final Region witnessRegion) { if (dynamoDbClient == null) { throw new IllegalArgumentException("DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } if (witnessRegion == null) { throw new IllegalArgumentException("Witness region cannot be null"); } try { LOGGER.info("Cleaning up MRSC replicas and witnesses for table: " + tableName); // Remove replica using ReplicationGroupUpdate ReplicationGroupUpdate replicaUpdate = ReplicationGroupUpdate.builder() .delete(DeleteReplicationGroupMemberAction.builder() .regionName(replicaRegion.id()) .build()) .build(); // Remove witness GlobalTableWitnessGroupUpdate witnessUpdate = GlobalTableWitnessGroupUpdate.builder() .delete(DeleteGlobalTableWitnessGroupMemberAction.builder() .regionName(witnessRegion.id()) .build()) .build(); UpdateTableRequest updateTableRequest = UpdateTableRequest.builder() .tableName(tableName) .replicaUpdates(List.of(replicaUpdate)) .globalTableWitnessUpdates(List.of(witnessUpdate)) .build(); UpdateTableResponse response = dynamoDbClient.updateTable(updateTableRequest); LOGGER.info("MRSC cleanup initiated - removing replica and witness. Response: " + response); return response; } catch (DynamoDbException e) { LOGGER.severe("Failed to cleanup MRSC replicas: " + tableName + " - " + e.getMessage()); throw DynamoDbException.builder() .message("Failed to cleanup MRSC replicas: " + tableName) .cause(e) .build(); } }

Complete MRSC workflow demonstration using Amazon SDK for Java 2.x.

public static void demonstrateCompleteMRSCWorkflow( final DynamoDbClient primaryClient, final DynamoDbClient replicaClient, final String tableName, final Region replicaRegion, final Region witnessRegion) throws InterruptedException { if (primaryClient == null) { throw new IllegalArgumentException("Primary DynamoDB client cannot be null"); } if (replicaClient == null) { throw new IllegalArgumentException("Replica DynamoDB client cannot be null"); } if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("Table name cannot be null or empty"); } if (replicaRegion == null) { throw new IllegalArgumentException("Replica region cannot be null"); } if (witnessRegion == null) { throw new IllegalArgumentException("Witness region cannot be null"); } try { LOGGER.info("=== Starting Complete MRSC Workflow Demonstration ==="); // Step 1: Create an empty single-Region table LOGGER.info("Step 1: Creating empty single-Region table"); createRegionalTable(primaryClient, tableName); // Use the existing GlobalTableOperations method for basic table waiting LOGGER.info("Intermediate step: Waiting for table [" + tableName + "] to become active before continuing"); GlobalTableOperations.waitForTableActive(primaryClient, tableName); // Step 2: Convert to MRSC with replica and witness LOGGER.info("Step 2: Converting to MRSC with replica and witness"); convertToMRSCWithWitness(primaryClient, tableName, replicaRegion, witnessRegion); // Wait for MRSC conversion to complete using MRSC-specific waiter LOGGER.info("Waiting for MRSC conversion to complete..."); waitForMRSCReplicasActive(primaryClient, tableName); LOGGER.info("Intermediate step: Waiting for table [" + tableName + "] to become active before continuing"); GlobalTableOperations.waitForTableActive(primaryClient, tableName); // Step 3: Verify MRSC configuration LOGGER.info("Step 3: Verifying MRSC configuration"); describeMRSCTable(primaryClient, tableName); // Step 4: Test strong consistency with data operations LOGGER.info("Step 4: Testing strong consistency with data operations"); // Add test item to primary region putTestItem(primaryClient, tableName, "The Beatles", "Hey Jude", "The Beatles 1967-1970", "1968"); // Immediately read from replica region (no wait needed with MRSC) LOGGER.info("Reading from replica region immediately (strong consistency):"); GetItemResponse getResponse = getItemWithConsistentRead(replicaClient, tableName, "The Beatles", "Hey Jude"); if (getResponse.hasItem()) { LOGGER.info("✓ Strong consistency verified - item immediately available in replica region"); } else { LOGGER.warning("✗ Item not found in replica region"); } // Test conditional update from replica region LOGGER.info("Testing conditional update from replica region:"); performConditionalUpdate(replicaClient, tableName, "The Beatles", "Hey Jude", "5"); LOGGER.info("✓ Conditional update successful - demonstrates strong consistency"); // Step 5: Cleanup LOGGER.info("Step 5: Cleaning up resources"); cleanupMRSCReplicas(primaryClient, tableName, replicaRegion, witnessRegion); // Wait for cleanup to complete using basic table waiter LOGGER.info("Waiting for replica cleanup to complete..."); GlobalTableOperations.waitForTableActive(primaryClient, tableName); // "Halt" until replica/witness cleanup is complete DescribeTableResponse cleanupVerification = describeMRSCTable(primaryClient, tableName); int backoffSeconds = 5; // Start with 5 second intervals while (cleanupVerification.table().multiRegionConsistency() != null) { LOGGER.info("Waiting additional time (" + backoffSeconds + " seconds) for MRSC cleanup to complete..."); tempWait(backoffSeconds); // Exponential backoff with cap backoffSeconds = Math.min(backoffSeconds * 2, 30); cleanupVerification = describeMRSCTable(primaryClient, tableName); } // Delete the primary table deleteTable(primaryClient, tableName); LOGGER.info("=== MRSC Workflow Demonstration Complete ==="); LOGGER.info(""); LOGGER.info("Key benefits of Multi-Region Strong Consistency (MRSC):"); LOGGER.info("- Immediate consistency across all regions (no eventual consistency delays)"); LOGGER.info("- Simplified application logic (no need to handle eventual consistency)"); LOGGER.info("- Support for conditional writes and transactions across regions"); LOGGER.info("- Consistent read operations from any region without waiting"); } catch (DynamoDbException | InterruptedException e) { LOGGER.severe("MRSC workflow failed: " + e.getMessage()); throw e; } }