Create and manage DynamoDB global tables demonstrating MREC using an Amazon SDK
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.
- Java
-
- 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; } } }
-
For API details, see the following topics in Amazon SDK for Java 2.x API Reference.
-
For a complete list of Amazon SDK developer guides and code examples, see Using DynamoDB with an Amazon SDK. This topic also includes information about getting started and details about previous SDK versions.