How to fix ItemCollectionSizeLimitExceededException: Item collection size limit of 10 GB exceeded in DynamoDB
DynamoDB returns ItemCollectionSizeLimitExceededException when a local secondary index (LSI) or table partition exceeds the 10 GB size limit for item collections. This occurs when too much data accumulates under a single partition key value, requiring data redistribution or archival strategies.
Quick fix
Fixes ItemCollectionSizeLimitExceededException: Item collection size limit of 10 GB exceeded
# Use CloudWatch metrics to identify hot partitions
aws cloudwatch get-metric-statistics --namespace AWS/DynamoDB --metric-name ConsumedWriteCapacityUnits --dimensions Name=TableName,Value=YourTableName --start-time $(date -u -d "1 hour ago" +%Y-%m-%dT%H:%M:%SZ) --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) --period 300 --statistics Sum
# Scan for large item collections (careful - can be expensive)
aws dynamodb scan --table-name YourTableName --select COUNT --filter-expression "begins_with(partitionKey, :prefix)" --expression-attribute-values '{":prefix":{"S":"problematic-value"}}'
# Check for LSIs with large collections
aws dynamodb describe-table --table-name YourTableName --query 'Table.LocalSecondaryIndexes[*].IndexName'- Identify the problematic partition key values
- Implement partition key sharding
- Archive or delete old data
- Restructure local secondary indexes
# Use CloudWatch metrics to identify hot partitionsaws cloudwatch get-metric-statistics --namespace AWS/DynamoDB --metric-name ConsumedWriteCapacityUnits --dimensions Name=TableName,Value=YourTableName --start-time $(date -u -d "1 hour ago" +%Y-%m-%dT%H:%M:%SZ) --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) --period 300 --statistics Sum# Scan for large item collections (careful - can be expensive)aws dynamodb scan --table-name YourTableName --select COUNT --filter-expression "begins_with(partitionKey, :prefix)" --expression-attribute-values '{":prefix":{"S":"problematic-value"}}'# Check for LSIs with large collectionsaws dynamodb describe-table --table-name YourTableName --query 'Table.LocalSecondaryIndexes[*].IndexName'ItemCollectionSizeLimitExceededException: Item collection size limit of 10 GB exceeded
What this error means
The ItemCollectionSizeLimitExceededException error in DynamoDB indicates that a specific item collection has exceeded the 10 GB size limit. An "item collection" refers to all items that share the same partition key value in a table or local secondary index. This limit exists because: 1. **Partition management**: DynamoDB partitions data based on partition key values 2. **Local secondary indexes**: LSIs maintain sorted collections of items within each partition 3. **Performance guarantees**: The 10 GB limit ensures predictable performance for queries and scans When you exceed this limit, DynamoDB cannot add new items to that partition key value until you reduce the collection size. This is a hard limit that cannot be increased, unlike provisioned throughput limits.
Typical symptoms
- Write operations failing with "ItemCollectionSizeLimitExceededException: Item collection size limit of 10 GB exceeded"
- Specific partition key values becoming write-blocked while others work normally
- BatchWriteItem operations returning UnprocessedItems for specific keys
- PutItem, UpdateItem, or DeleteItem operations failing for specific partition values
- Error occurs consistently for certain data patterns but not others
- CloudWatch metrics showing successful writes decreasing over time for affected partitions
Common causes
- High-velocity data streams writing to single partition key value (e.g., global counters, system-wide metrics)
- Time-series data using coarse partition keys (e.g., "data-2024" instead of "data-2024-01-01")
- Local secondary indexes accumulating large collections under common partition values
- Poor partition key design concentrating too much data under few values
- Data retention policies not implemented, allowing unlimited growth
- Application logic that always writes to same partition for certain operations
How to fix "ItemCollectionSizeLimitExceededException: Item collection size limit of 10 GB exceeded"
First, determine which partition key values are hitting the 10 GB limit:
# Use CloudWatch metrics to identify hot partitions
aws cloudwatch get-metric-statistics --namespace AWS/DynamoDB --metric-name ConsumedWriteCapacityUnits --dimensions Name=TableName,Value=YourTableName --start-time $(date -u -d "1 hour ago" +%Y-%m-%dT%H:%M:%SZ) --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) --period 300 --statistics Sum
# Scan for large item collections (careful - can be expensive)
aws dynamodb scan --table-name YourTableName --select COUNT --filter-expression "begins_with(partitionKey, :prefix)" --expression-attribute-values '{":prefix":{"S":"problematic-value"}}'
# Check for LSIs with large collections
aws dynamodb describe-table --table-name YourTableName --query 'Table.LocalSecondaryIndexes[*].IndexName'Analysis approach:
- Review application logs for failed write operations
- Check which partition key values appear in error messages
- Analyze data access patterns in your application code
- Use DynamoDB Streams to monitor write patterns
Modify your partition key design to distribute data across multiple partitions:
// BEFORE: Single partition key for all metrics
const partitionKey = 'system-metrics';
// AFTER: Sharded partition keys
function getShardedPartitionKey(metricName) {
const shardId = Math.floor(Math.random() * 100); // 0-99
return `metric-${metricName}-shard-${shardId}`;
}
// For time-series data, include timestamp granularity
function getTimeBasedPartitionKey(entity, timestamp) {
const date = new Date(timestamp);
const month = date.toISOString().substring(0, 7); // YYYY-MM
return `${entity}-${month}`;
}
// For user data, use composite keys
function getUserPartitionKey(userId, dataType) {
return `user-${userId}-${dataType}`;
}Sharding strategies:
- Add random suffixes (0-99, 0-999) based on expected volume
- Include timestamp components (year, month, day) for time-series
- Use composite keys combining entity type with sub-category
- Implement consistent hashing for deterministic shard assignment
Implement data lifecycle management to remove old items:
// Example: Delete items older than retention period
async function deleteOldItems(tableName, partitionKey, retentionDays) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
// Query for old items (assuming sort key is timestamp)
const params = {
TableName: tableName,
KeyConditionExpression: 'pk = :pk AND sk < :cutoff',
ExpressionAttributeValues: {
':pk': { S: partitionKey },
':cutoff': { N: cutoffDate.getTime().toString() }
}
};
const items = [];
let lastEvaluatedKey;
do {
if (lastEvaluatedKey) {
params.ExclusiveStartKey = lastEvaluatedKey;
}
const result = await dynamodb.query(params).promise();
items.push(...result.Items);
lastEvaluatedKey = result.LastEvaluatedKey;
} while (lastEvaluatedKey);
// Batch delete in chunks of 25
for (let i = 0; i < items.length; i += 25) {
const chunk = items.slice(i, i + 25);
const deleteRequests = chunk.map(item => ({
DeleteRequest: {
Key: { pk: item.pk, sk: item.sk }
}
}));
await dynamodb.batchWriteItem({
RequestItems: {
[tableName]: deleteRequests
}
}).promise();
}
}
// Alternative: Move to archival table
async function archiveOldItems(sourceTable, archiveTable, partitionKey) {
// Similar pattern but with PutRequest to archive table
// and DeleteRequest from source table in same batch
}Retention strategies:
- Implement TTL (Time to Live) for automatic expiration
- Schedule Lambda functions for periodic cleanup
- Move historical data to DynamoDB Standard-IA or S3
- Consider DynamoDB Streams + Lambda for real-time archival
If LSIs are causing the issue, consider alternative indexing strategies:
// BEFORE: LSI on frequently updated attribute
// This creates large collections if many items share partition key
// AFTER: Use global secondary index (GSI) instead
// GSIs have their own partition key and don't contribute to item collection size
// Or: Denormalize data into separate tables
async function writeToDenormalizedTable(mainTable, summaryTable, item) {
// Write detailed data to main table with fine-grained partition key
await dynamodb.putItem({
TableName: mainTable,
Item: {
pk: { S: `detail-${item.id}-${Date.now()}` },
// ... other attributes
}
}).promise();
// Write summary to separate table
await dynamodb.updateItem({
TableName: summaryTable,
Key: { pk: { S: `summary-${item.category}` } },
UpdateExpression: 'ADD #count :inc',
ExpressionAttributeNames: { '#count': 'itemCount' },
ExpressionAttributeValues: { ':inc': { N: '1' } }
}).promise();
}
// Use sparse indexes for conditional data
// Only include items in GSI when they have specific attributesIndexing alternatives:
- Replace LSIs with GSIs when possible
- Implement composite GSIs for different query patterns
- Use DynamoDB Accelerator (DAX) for read-heavy patterns
- Consider Amazon Athena for analytical queries on exported data
Add proactive monitoring and throttling to prevent future issues:
// Monitor partition sizes
class PartitionMonitor {
constructor(tableName) {
this.tableName = tableName;
this.partitionCounts = new Map();
this.warningThreshold = 8 * 1024 * 1024 * 1024; // 8 GB warning
}
async trackWrite(partitionKey) {
const count = (this.partitionCounts.get(partitionKey) || 0) + 1;
this.partitionCounts.set(partitionKey, count);
// Estimate size (adjust based on your average item size)
const estimatedSize = count * 1024; // 1KB per item
if (estimatedSize > this.warningThreshold) {
console.warn(`Partition ${partitionKey} approaching 10 GB limit`);
// Trigger mitigation: switch to sharded key, throttle writes, etc.
return this.getShardedKey(partitionKey);
}
return partitionKey;
}
getShardedKey(originalKey) {
const suffix = Math.floor(Math.random() * 10);
return `${originalKey}-shard-${suffix}`;
}
}
// CloudWatch alarm for partition growthbash
# Create CloudWatch alarm for partition growth
aws cloudwatch put-metric-alarm --alarm-name "DynamoDB-Partition-Growth-Alarm" --alarm-description "Alarm when partition approaches 10 GB limit" --metric-name UserErrors --namespace AWS/DynamoDB --statistic Sum --period 300 --evaluation-periods 2 --threshold 1 --comparison-operator GreaterThanOrEqualToThreshold --dimensions Name=TableName,Value=YourTableName Name=Operation,Value=PutItem --alarm-actions arn:aws:sns:us-east-1:123456789012:alarm-topic
```
Monitoring strategies:
- Track estimated partition sizes in application memory
- Set up CloudWatch alarms for UserErrors metric
- Implement circuit breakers for problematic partitions
- Log all ItemCollectionSizeLimitExceededException occurrences
For persistent issues, consider fundamental table redesign:
// Problematic design: Single partition for all events
// Partition key: "events"
// Sort key: timestamp
// Better design: Time-based partitioning
// Partition key: "events-2024-01", "events-2024-02", etc.
// Sort key: timestamp
// Even better: Sharded time-based partitioning
// Partition key: "events-2024-01-shard-0" through "events-2024-01-shard-9"
// Sort key: timestamp
// For user data: Composite partition keys
// Partition key: "user-12345-profile", "user-12345-orders", "user-12345-prefs"
// Instead of: "user-12345" with different item types in sort key
// Use separate tables for different data access patterns
const tables = {
userProfiles: 'UserProfilesTable',
userOrders: 'UserOrdersTable',
systemMetrics: 'SystemMetricsTable',
auditLogs: 'AuditLogsTable'
};Redesign principles:
- One table per access pattern when possible
- Fine-grained partition keys (more distinct values)
- Separate volatile data from archival data
- Consider DynamoDB Streams + Lambda for cross-table consistency
- Use DynamoDB transactions for multi-table atomic operations
Advanced notes
## Understanding DynamoDB Partition Limits
### Item Collection Size Limit Details:
- 10 GB hard limit per partition key value
- Applies to both base table and all local secondary indexes combined
- Counts all versions of items (including deleted items until garbage collected)
- Based on uncompressed data size
- Cannot be increased via support ticket
### Local Secondary Index (LSI) Considerations:
- LSIs share the same partition key as base table
- Each LSI creates its own item collection within the partition
- All LSIs + base table must stay under 10 GB total
- LSIs cannot be added after table creation
- Consider GSIs instead for flexible indexing
### Partition Key Design Best Practices:
High-cardinality attributes:
- User IDs, session IDs, UUIDs
- Device IDs with high uniqueness
- Transaction IDs, order numbers
Avoid low-cardinality attributes:
- Status fields (active/inactive)
- Boolean flags (true/false)
- Small enum values (category A/B/C/D)
Time-series data patterns:
- ❌ Bad: "metrics" (single partition)
- ✅ Better: "metrics-2024-01", "metrics-2024-02"
- ✅ Best: "metrics-2024-01-shard-0" through "metrics-2024-01-shard-9"
### Data Lifecycle Management:
TTL (Time to Live):
- Automatically deletes items after expiry
- Reduces item collection size over time
- Background process, not immediate
- Useful for session data, temporary states
DynamoDB Streams + Lambda:
- Archive old data to S3
- Aggregate data into summary tables
- Implement custom retention policies
- Maintain data for compliance while reducing active size
### Migration Strategies:
Dual-write pattern:
1. Write to old and new table designs simultaneously
2. Use DynamoDB Streams to backfill historical data
3. Gradually shift read traffic to new design
4. Archive old table after validation
Blue-green deployment:
1. Create new table with improved design
2. Use data pipeline to copy/migrate data
3. Update application to use new table
4. Monitor performance before deleting old table
### Cost Considerations:
- Large scans/queries for data cleanup can be expensive
- GSIs have additional storage and throughput costs
- Data migration may incur significant read/write costs
- Consider cost of maintaining multiple tables vs. sharding
### When to Contact AWS Support:
- Need guidance on large-scale data migration
- Suspected data corruption issues
- Production-critical applications requiring immediate resolution
- Complex multi-table redesign requiring architecture review
Frequently asked questions
- What does "ItemCollectionSizeLimitExceededException: Item collection size limit o" mean?
- The ItemCollectionSizeLimitExceededException error in DynamoDB indicates that a specific item collection has exceeded the 10 GB size limit. An "item collection" refers to all items that share the same partition key value in a table or local secondary index. This limit exists because: 1.
- How do I fix "ItemCollectionSizeLimitExceededException: Item collection size limit o"?
- DynamoDB returns ItemCollectionSizeLimitExceededException when a local secondary index (LSI) or table partition exceeds the 10 GB size limit for item collections. This occurs when too much data accumulates under a single partition key value, requiring data redistribution or archival strategies.
- What causes "ItemCollectionSizeLimitExceededException: Item collection size limit o"?
- High-velocity data streams writing to single partition key value (e.g., global counters, system-wide metrics) Time-series data using coarse partition keys (e.g., "data-2024" instead of "data-2024-01-01") Local secondary indexes accumulating large collections under common partition values
- Is the "ItemCollectionSizeLimitExceededException: Item collection size limit o" error serious?
- Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.
Official resources & further reading
- 📚DynamoDB Official Documentation
- 📖docs.aws.amazon.com - Limits(Official Docs)
- 📖docs.aws.amazon.com - LSI(Official Docs)
- 💬
- 📖aws.amazon.com - choosing the right dynamodb partition key(Official Docs)
- 🐙github.com - 1234(GitHub)