šŸŽ‰ 75% of content is free forever — Unlock Premium from $10/mo →
CW
šŸ’¼ Servicesā„¹ļø Aboutāœ‰ļø ContactView Pricing Plansfrom $10

Amazon DocumentDB for Data Engineers

AWS Data EngineeringDocumentDB Architecture & NoSQL Analytics⭐ Premium

Advertisement

Amazon DocumentDB

Master MongoDB-compatible NoSQL database with serverless scaling, flexible schemas, and enterprise-grade performance.

18 min readIntermediate

What is Amazon DocumentDB?

Amazon DocumentDB (with MongoDB compatibility) is a fast, reliable, and fully managed database service that makes it easy to set up, operate, and scale MongoDB-compatible databases in the cloud. It implements the JSON document model and is designed from the ground up to be highly available with self-healing storage.

Core Concepts

DocumentDB uses a JSON-based document model that allows flexible schemas, making it ideal for content management, catalogs, user profiles, and semi-structured data workloads.

ConceptDescription
ClusterA logical grouping of instances and storage
InstanceA compute node that processes queries (primary or replica)
StorageAuto-scaling, SSD-backed distributed storage layer
CollectionA group of documents (analogous to a table in RDBMS)
DocumentA JSON object stored in a collection (analogous to a row)
ShardA partition of data for horizontal scaling (available in DocDB 5.0+)

DocumentDB Architecture

Amazon DocumentDB ArchitectureApplication LayerMongoDB DriversCluster EndpointRead/Write SplittingDocumentDB ClusterMongoDB 4.0/5.0 CompatibleClient ToolsCompass, Shell, SDKPrimary InstanceWrite OperationsQuery ProcessingIndex ManagementLogs to Storage EngineReplica Instance 1Read OperationsReplica Set MemberAutomated FailoverRead Replica for QueriesReplica Instance 2Read OperationsReplica Set MemberAutomated FailoverRead Replica for QueriesDistributed Storage Layer6-way replication across 3 AZs | Auto-scaling up to 128 TB | 6x data durabilityTransaction logs | Data pages | Change stream | Automated backups | Point-in-time recoveryContinuous backups | Daily snapshots | Cross-region copy | Restore to any second in retention periodCloudWatch MetricsCPU, Memory, IOPS, ConnectionsEvent NotificationsFailover | Backup | Restore | ScalingParameter GroupsEngine Config | Performance Tuning

How DocumentDB Works

DocumentDB separates compute from storage. The primary instance receives write operations, which are synchronously written to the distributed storage layer across three availability zones. Read replicas asynchronously apply changes from the transaction log. This architecture provides 6 copies of data for durability and automatic failover.

Real-World Project Structure

Architecture Diagram
documentdb-production/
ā”œā”€ā”€ cluster/
│   ā”œā”€ā”€ cluster-config.json
│   ā”œā”€ā”€ parameter-groups/
│   │   ā”œā”€ā”€ prod-parameters.json
│   │   └── analytics-parameters.json
│   └── subnet-groups/
│       └── vpc-subnet-group.json
ā”œā”€ā”€ schemas/
│   ā”œā”€ā”€ collections/
│   │   ā”œā”€ā”€ users.json
│   │   ā”œā”€ā”€ orders.json
│   │   ā”œā”€ā”€ products.json
│   │   └── events.json
│   └── indexes/
│       ā”œā”€ā”€ users-indexes.json
│       ā”œā”€ā”€ orders-indexes.json
│       └── products-indexes.json
ā”œā”€ā”€ etl-pipeline/
│   ā”œā”€ā”€ glue-jobs/
│   │   ā”œā”€ā”€ docdb-to-s3-export.py
│   │   ā”œā”€ā”€ s3-to-docdb-import.py
│   │   └── change-stream-processor.py
│   └── lambda-functions/
│       ā”œā”€ā”€ export-to-s3.py
│       └── stream-processor.py
ā”œā”€ā”€ monitoring/
│   ā”œā”€ā”€ cloudwatch-alarms.yaml
│   └── dashboards/
│       ā”œā”€ā”€ cluster-health.json
│       └── query-performance.json
└── security/
    ā”œā”€ā”€ iam-roles/
    │   ā”œā”€ā”€ docdb-service-role.json
    │   └── lambda-access-role.json
    └── kms-encryption/
        └── encryption-key.yaml

Production Python Code

import boto3
import json
import logging
from datetime import datetime
from typing import Dict, List, Optional
from pymongo import MongoClient
from pymongo.errors import (
    ConnectionFailure,
    OperationFailure,
    BulkWriteError
)

logger = logging.getLogger(__name__)

class DocumentDBManager:
    """Production-grade Amazon DocumentDB manager."""

    def __init__(
        self,
        cluster_endpoint: str,
        port: int = 27017,
        database: str = 'admin',
        username: str = None,
        password: str = None,
        replica_set: str = 'rs0',
        read_preference: str = 'secondaryPreferred'
    ):
        self.connection_string = (
            f"mongodb://{username}:{password}@{cluster_endpoint}:{port}"
            f"/?replicaSet={replica_set}"
            f"&readPreference={read_preference}"
            f"&retryWrites=false"
            f"&ssl=true"
            f"&tlsAllowInvalidCertificates=false"
        )
        self.client = MongoClient(self.connection_string)
        self.db = self.client[database]

    def insert_document(
        self,
        collection: str,
        document: Dict
    ) -> str:
        """Insert a single document with error handling."""
        try:
            result = self.db[collection].insert_one(document)
            logger.info(f"Inserted document: {result.inserted_id}")
            return str(result.inserted_id)
        except OperationFailure as e:
            logger.error(f"Insert failed: {e}")
            raise

    def bulk_insert(
        self,
        collection: str,
        documents: List[Dict],
        ordered: bool = False
    ) -> Dict:
        """Insert multiple documents in bulk with error handling."""
        results = {'success': 0, 'failed': 0, 'errors': []}
        try:
            result = self.db[collection].insert_many(
                documents,
                ordered=ordered
            )
            results['success'] = len(result.inserted_ids)
        except BulkWriteError as e:
            results['success'] = e.details['nInserted']
            results['failed'] = len(e.details['writeErrors'])
            results['errors'] = [
                err['errmsg'] for err in e.details['writeErrors']
            ]
            logger.warning(
                f"Bulk insert: {results['success']} OK, "
                f"{results['failed']} failed"
            )
        except Exception as e:
            logger.error(f"Bulk insert failed: {e}")
            raise

        return results

    def find_with_aggregation(
        self,
        collection: str,
        pipeline: List[Dict],
        max_time_ms: int = 30000
    ) -> List[Dict]:
        """Execute aggregation pipeline with timeout."""
        try:
            cursor = self.db[collection].aggregate(
                pipeline,
                maxTimeMS=max_time_ms,
                allowDiskUse=True
            )
            return list(cursor)
        except OperationFailure as e:
            logger.error(f"Aggregation failed: {e}")
            raise

    def create_indexes(
        self,
        collection: str,
        indexes: List[Dict]
    ) -> List[str]:
        """Create indexes on a collection."""
        index_names = []
        try:
            for index in indexes:
                name = self.db[collection].create_index(
                    index['keys'],
                    unique=index.get('unique', False),
                    name=index.get('name'),
                    background=True
                )
                index_names.append(name)
                logger.info(f"Created index: {name}")
        except Exception as e:
            logger.error(f"Index creation failed: {e}")
            raise
        return index_names

    def watch_change_stream(
        self,
        collection: str,
        resume_token: Optional[Dict] = None,
        pipeline: Optional[List[Dict]] = None
    ):
        """Watch for changes using change streams."""
        try:
            change_stream = self.db[collection].watch(
                pipeline=pipeline or [],
                resume_after=resume_token,
                full_document='updateLookup'
            )
            for change in change_stream:
                yield change
                resume_token = change_cluster._resume_token
        except ConnectionFailure as e:
            logger.error(f"Change stream lost connection: {e}")
            raise


class DocumentDBProvisioner:
    """Provision and manage DocumentDB clusters using boto3."""

    def __init__(self, region: str = 'us-east-1'):
        self.client = boto3.client('docdb', region_name=region)

    def create_cluster(
        self,
        cluster_id: str,
        master_username: str,
        master_password: str,
        instance_class: str = 'db.r5.large',
        instance_count: int = 3,
        backup_retention_days: int = 7,
        encryption_enabled: bool = True,
        enable_cloudwatch_logs: bool = True
    ) -> Dict:
        """Create a production DocumentDB cluster."""
        try:
            # Create subnet group
            self.client.create_db_subnet_group(
                DBSubnetGroupName=f'{cluster_id}-subnet-group',
                DBSubnetGroupDescription=f'Subnet group for {cluster_id}',
                SubnetIds=['subnet-xxx', 'subnet-yyy', 'subnet-zzz']
            )

            # Create cluster
            response = self.client.create_db_cluster(
                DBClusterIdentifier=cluster_id,
                Engine='docdb',
                MasterUsername=master_username,
                MasterUserPassword=master_password,
                DBSubnetGroupName=f'{cluster_id}-subnet-group',
                BackupRetentionPeriod=backup_retention_days,
                StorageEncrypted=encryption_enabled,
                EnableCloudwatchLogsExports=[
                    'audit', 'profiler'
                ] if enable_cloudwatch_logs else [],
                Tags=[
                    {'Key': 'Environment', 'Value': 'production'},
                    {'Key': 'Service', 'Value': 'documentdb'}
                ]
            )

            # Create instances
            for i in range(instance_count):
                self.client.create_db_instance(
                    DBInstanceIdentifier=f'{cluster_id}-instance-{i+1}',
                    DBClusterIdentifier=cluster_id,
                    DBInstanceClass=instance_class,
                    Engine='docdb'
                )

            logger.info(f"Created cluster: {cluster_id}")
            return response['DBCluster']
        except Exception as e:
            logger.error(f"Failed to create cluster: {e}")
            raise

    def create_read_replica(
        self,
        cluster_id: str,
        replica_id: str,
        instance_class: str = 'db.r5.large'
    ) -> Dict:
        """Create a read replica instance."""
        try:
            response = self.client.create_db_instance(
                DBInstanceIdentifier=replica_id,
                DBClusterIdentifier=cluster_id,
                DBInstanceClass=instance_class,
                Engine='docdb',
                Tags=[
                    {'Key': 'Role', 'Value': 'read-replica'}
                ]
            )
            logger.info(f"Created read replica: {replica_id}")
            return response['DBInstance']
        except Exception as e:
            logger.error(f"Failed to create replica: {e}")
            raise

    def enable_cloudwatch_export(
        self,
        cluster_id: str,
        log_types: List[str] = ['audit', 'profiler']
    ) -> Dict:
        """Enable CloudWatch log exports for a cluster."""
        try:
            response = self.client.modify_db_cluster(
                DBClusterIdentifier=cluster_id,
                EnableCloudwatchLogsExports=log_types,
                ApplyImmediately=True
            )
            logger.info(f"Enabled log exports for: {cluster_id}")
            return response['DBCluster']
        except Exception as e:
            logger.error(f"Failed to enable logs: {e}")
            raise


# Production usage
if __name__ == '__main__':
    provisioner = DocumentDBProvisioner()
    cluster = provisioner.create_cluster(
        cluster_id='prod-documentdb',
        master_username='admin',
        master_password='SecureP@ss123!',
        instance_class='db.r5.2xlarge',
        instance_count=4,
        backup_retention_days=14
    )
    print(f"Cluster ARN: {cluster['DBClusterArn']}")

Production Bash Commands

#!/bin/bash
# DocumentDB cluster backup and export script

set -euo pipefail

CLUSTER_ID="${1:-prod-documentdb}"
REGION="${AWS_REGION:-us-east-1}"
BACKUP_BUCKET="${BACKUP_BUCKET:-my-docdb-backups}"
EXPORT_FORMAT="${2:-json}"

echo "=== DocumentDB Export & Backup ==="
echo "Cluster: ${CLUSTER_ID}"
echo "Region: ${REGION}"
echo "Timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)"

# Get cluster details
CLUSTER_INFO=$(aws docdb describe-db-clusters \
  --db-cluster-identifier "${CLUSTER_ID}" \
  --region "${REGION}" \
  --query 'DBClusters[0]' \
  --output json)

ENDPOINT=$(echo "$CLUSTER_INFO" | jq -r '.Endpoint')
READ_ENDPOINT=$(echo "$CLUSTER_INFO" | jq -r '.ReaderEndpoint')
STATUS=$(echo "$CLUSTER_INFO" | jq -r '.Status')
ENGINE=$(echo "$CLUSTER_INFO" | jq -r '.EngineVersion')
BACKUP_RETENTION=$(echo "$CLUSTER_INFO" | jq -r '.BackupRetentionPeriod')

echo "Status: ${STATUS}"
echo "Engine: ${ENGINE}"
echo "Endpoint: ${ENDPOINT}"
echo "Reader Endpoint: ${READ_ENDPOINT}"
echo "Backup Retention: ${BACKUP_RETENTION} days"

# Check instance health
INSTANCES=$(aws docdb describe-db-instances \
  --filters "Name=db-cluster-id,Values=${CLUSTER_ID}" \
  --region "${REGION}" \
  --query 'DBInstances[*].[DBInstanceIdentifier,DBInstanceClass,Status]' \
  --output table)

echo ""
echo "=== Cluster Instances ==="
echo "$INSTANCES"

# Monitor IOPS and storage
METRICS=$(aws cloudwatch get-metric-statistics \
  --namespace AWS/DocDB \
  --metric-name FreeStorageSpace \
  --dimensions Name=DBClusterIdentifier,Value="${CLUSTER_ID}" \
  --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 Average \
  --region "${REGION}")

STORAGE_GB=$(echo "$METRICS" | jq '.Datapoints[-1].Average // 0' | awk '{printf "%.2f", $1/1073741824}')
echo ""
echo "=== Storage Metrics ==="
echo "Free Storage: ${STORAGE_GB} GB"

# Export collection to S3 using Glue
echo ""
echo "=== Starting Glue Export Job ==="
aws glue start-job-run \
  --job-name "docdb-export-${CLUSTER_ID}" \
  --arguments "{
    \"--cluster_endpoint\": \"${ENDPOINT}\",
    \"--database\": \"analytics_db\",
    \"--s3_output\": \"s3://${BACKUP_BUCKET}/exports/\",
    \"--format\": \"${EXPORT_FORMAT}\",
    \"--timestamp\": \"$(date -u +%Y%m%d%H%M%S)\"
  }" \
  --region "${REGION}"

echo "Export job started. Monitor in Glue console."

# Create manual snapshot
SNAPSHOT_ID="${CLUSTER_ID}-manual-$(date -u +%Y%m%d%H%M%S)"
aws docdb create-db-cluster-snapshot \
  --db-cluster-snapshot-identifier "${SNAPSHOT_ID}" \
  --db-cluster-identifier "${CLUSTER_ID}" \
  --region "${REGION}"

echo ""
echo "=== Manual Snapshot Created ==="
echo "Snapshot: ${SNAPSHOT_ID}"

# Cleanup old snapshots (keep last 5 manual)
echo ""
echo "=== Cleaning Up Old Snapshots ==="
SNAPSHOTS=$(aws docdb describe-db-cluster-snapshots \
  --snapshot-type manual \
  --region "${REGION}" \
  --query "DBClusterSnapshots[?starts_with(DBClusterSnapshotIdentifier, '${CLUSTER_ID}-manual')].DBClusterSnapshotIdentifier" \
  --output json)

SNAPSHOT_COUNT=$(echo "$SNAPSHOTS" | jq length)
if [ "$SNAPSHOT_COUNT" -gt 5 ]; then
  echo "Cleaning up $(($SNAPSHOT_COUNT - 5)) old snapshots..."
  echo "$SNAPSHOTS" | jq -r ".[:-5][]" | while read -r snap; do
    aws docdb delete-db-cluster-snapshot \
      --db-cluster-snapshot-identifier "$snap" \
      --region "${REGION}"
    echo "Deleted: $snap"
  done
fi

echo ""
echo "Backup and export complete."

Why This Matters

Amazon DocumentDB provides a fully managed, MongoDB-compatible database that eliminates the operational overhead of managing database infrastructure. Its distributed storage layer with 6x replication ensures high durability, while automatic scaling and serverless options reduce operational complexity. For data engineers, DocumentDB integrates with S3 via Glue, enabling ETL pipelines that combine NoSQL flexibility with data lake analytics.

Mathematical Formulas

Performance Considerations

FactorRecommendationImpact
Instance ClassUse memory-optimized (r5/r6g) for read-heavyHigh - query performance
Read ReplicasAdd replicas for read scalingHigh - horizontal read scaling
IndexesCreate compound indexes for common queriesHigh - query optimization
Connection PoolingUse connection pooling in applicationsMedium - reduce connection overhead
Bulk OperationsBatch inserts and updates when possibleMedium - reduce round trips
Aggregation PipelinesUse allowDiskUse for large aggregationsMedium - prevent memory overflow
Change StreamsUse for real-time data captureHigh - event-driven architectures
Storage ScalingEnable auto-scaling for storageMedium - avoid manual intervention

Security Considerations

  • Encryption at Rest: Enable KMS encryption for all DocumentDB clusters
  • Encryption in Transit: Use TLS/SSL for all client connections
  • VPC Deployment: Deploy within VPC for network isolation
  • IAM Integration: Use IAM authentication for database access
  • Parameter Groups: Control database configuration parameters
  • Audit Logging: Enable audit logs for compliance requirements
  • CloudWatch Logs: Export logs for monitoring and analysis
  • Snapshots: Encrypt manual and automated snapshots

Common Pitfalls

PitfallConsequenceSolution
Using MongoDB 5.0 features on older versionsCompatibility errorsCheck engine version support
No connection poolingConnection exhaustion under loadUse pymongo with pool_size
Missing compound indexesSlow queries on large collectionsAnalyze query patterns and add indexes
Disabling backupsData loss riskAlways enable automated backups
Using small instance classesQuery timeoutsMatch instance to workload requirements
Ignoring read preferencesOverloading primary instanceUse secondaryPreferred for reads
No monitoringUndetected performance issuesSet up CloudWatch alarms
Not using TLSSecurity vulnerabilitiesAlways enforce TLS connections

Interview Questions & Answers

Q1: What is Amazon DocumentDB and how does it differ from MongoDB?

Answer: Amazon DocumentDB is a fully managed, MongoDB-compatible database service. It implements the MongoDB 3.6/4.0/5.0 API but uses a different storage engine designed for the cloud. Key differences include: DocumentDB has a distributed storage layer with 6x replication for durability, automatic storage scaling up to 128 TB, and deeper AWS integration with IAM, VPC, and CloudWatch. It does not support all MongoDB features, so check compatibility requirements.

Q2: How does DocumentDB achieve high availability?

Answer: DocumentDB achieves HA through: (1) Data stored across 3 AZs with 6 copies for durability. (2) Automated failover within 30 seconds if the primary fails. (3) Read replicas that can be promoted to primary. (4) Continuous backups with point-in-time recovery. (5) Storage auto-scaling to prevent out-of-space failures.

Q3: When should you choose DocumentDB over DynamoDB?

Answer: Choose DocumentDB when you need: MongoDB compatibility, complex aggregation queries, ad-hoc queries with varying patterns, relational-like query patterns on JSON documents, or JOIN operations across collections. Choose DynamoDB for: simple key-value access, single-digit millisecond latency at any scale, serverless auto-scaling, and predictable performance.

Q4: How do you optimize DocumentDB query performance?

Answer: Key optimizations: (1) Create compound indexes for frequently used query patterns. (2) Use covering indexes to avoid fetching full documents. (3) Limit result sets with .limit() and project only needed fields. (4) Use read replicas to offload read traffic from primary. (5) Monitor slow query logs and optimize aggregation pipelines. (6) Use $match early in aggregation pipelines.

Q5: What are the limitations of DocumentDB change streams?

Answer: Change streams limitations: (1) Resume tokens are valid for 24 hours by default. (2) Streams cannot be used across different collections in a single watcher. (3) Large documents in update operations may be truncated. (4) Schema operations (create/drop collections) may not appear in change streams. (5) Retention period for change events is limited.

Q6: How does DocumentDB handle storage scaling?

Answer: DocumentDB automatically scales storage in 10 GB increments up to 128 TB without downtime. The storage layer is distributed across 3 AZs with 6 copies. You do not need to pre-provision storage. Monitor storage usage via CloudWatch and set alarms to avoid unexpected scaling.

Q7: Can you migrate from MongoDB to DocumentDB?

Answer: Yes, AWS provides the DocumentDB Migration Tool (based on mongodump/mongorestore) and the AWS Database Migration Service (DMS). For large datasets, use DMS with CDC for near-zero downtime migration. Test application compatibility first, as not all MongoDB features are supported in DocumentDB.

Q8: How do you implement encryption in DocumentDB?

Answer: DocumentDB supports: (1) Encryption at rest using AWS KMS with automatic or customer-managed keys. (2) Encryption in transit using TLS/SSL enforced at the cluster level. (3) Snapshot encryption for backups. Enable encryption at cluster creation time, as it cannot be added after creation.

QuizBox

See Also

šŸ”’

Premium Content

Amazon DocumentDB for Data Engineers

You've previewed the first section. Unlock this full lesson and 900+ advanced tutorials with a Premium plan.

šŸŽÆEnd-to-end Projects
šŸ’¼Interview Prep
šŸ“œCertificates
šŸ¤Community Access

Already a member? Log in

Advertisement