```html

Diagnosing and Fixing DynamoDB Scan Timeouts in High-Volume Crew Page Rebuild Pipeline

What Was Done

The crew page rebuild pipeline in JADA Charter Operations was failing silently due to DynamoDB scan timeouts when iterating over crew dispatch records. The root cause was twofold: the AWS CLI was not accessible in the script's PATH, and the DDB scan was attempting to pull hundreds of records in a single request without pagination strategy or timeout resilience. This post covers the systematic diagnosis and the implementation of chunked scanning to ensure consistent pipeline execution.

Technical Details: The DynamoDB Bottleneck

The rebuild.py script in /Users/cb/icloud-jada-ops/crew-pages/ performs a full scan of the jada-crew-dispatch DynamoDB table to generate crew assignment pages. Under normal conditions, this table contains dispatch records for every crew assignment over several years. The original implementation used the AWS SDK (via boto3) to execute a single COUNT scan operation, then fetched all items without pagination control.

During high-latency network conditions or when DDB itself is serving concurrent requests, a scan requesting 800+ items in one request would hit the Lambda/subprocess timeout boundary. The pipeline would fail with no explicit error message—just a silent hang followed by a timeout exception caught at the calling layer.

Diagnosis steps:

  • Verified AWS CLI availability by checking for aws binary in system PATH and within the Python subprocess environment
  • Timed sequential DDB COUNT scans with different pagination sizes: 50, 200, and 800 items per page
  • Identified that a single page beyond ~500 items consistently approached or exceeded the 60-second subprocess timeout
  • Performed a "poison item" analysis: walked the table in 50-item chunks, then fetched individual items from the slow page to isolate which records were expensive to deserialize

The Chunked Scanning Solution

Rather than requesting all records at once, the rebuild pipeline was refactored to iterate through the DDB table with explicit pagination:

# Pseudo-implementation of chunked scanning strategy
chunk_size = 50
last_evaluated_key = None
all_records = []

while True:
    response = dynamodb.scan(
        TableName='jada-crew-dispatch',
        Limit=chunk_size,
        ExclusiveStartKey=last_evaluated_key
    )
    all_records.extend(response.get('Items', []))
    
    last_evaluated_key = response.get('LastEvaluatedKey')
    if not last_evaluated_key:
        break

This approach offers several advantages:

  • Timeout resilience: Each chunk completes in 2–5 seconds instead of 60+ seconds, leaving headroom within the Lambda/subprocess execution window
  • Progressive visibility: The pipeline can log progress after each chunk, enabling early detection of hangs rather than silent failures
  • Operator control: The chunk size (50 items per request) is configurable, allowing tuning for different table sizes or network conditions without rewriting the scan logic
  • Cost neutrality: DynamoDB pricing is based on consumed read capacity units, not request count, so pagination adds no billing overhead

Infrastructure and PATH Resolution

The second failure mode emerged when the AWS CLI itself was not found. The rebuild.py script invokes AWS operations via subprocess calls to the aws command. In some execution contexts (particularly user-initiated cron jobs or CI runners), the script's Python environment inherits a restricted PATH that excludes /usr/local/bin where AWS CLI is typically installed.

Fix: The script now explicitly sources the AWS CLI from a known location or verifies the binary path before invoking subprocess commands:

import shutil
import subprocess

# Resolve aws binary path
aws_bin = shutil.which('aws')
if not aws_bin:
    aws_bin = '/usr/local/bin/aws'  # fallback to common install location

# Use absolute path in subprocess calls
result = subprocess.run(
    [aws_bin, 's3', 'cp', local_file, s3_path],
    capture_output=True,
    timeout=30
)

Additionally, the crew pages module was updated to import tools from a centralized location (icloud-repos) rather than assuming relative paths, ensuring consistent tool resolution across all execution contexts.

Monitoring and Resilience Improvements

Beyond the scan optimization, the pipeline was hardened with retry logic and explicit error reporting:

  • Retry-hardened AWS helper: Subprocess calls to aws now retry once on timeout with exponential backoff before failing
  • Dry-run validation: A --dry-run flag was added to the pipeline, allowing operators to verify the scan and DDB access work before triggering a real crew page rebuild and email notifications
  • Intermediate logging: Each chunk of 50 records is logged with timestamp, allowing operators to identify which page is slow without waiting for the full scan to complete

Key Decisions and Trade-offs

Why chunked scanning over a connection pool: Connection pooling would require refactoring the subprocess-based AWS CLI invocation into a boto3-native client. While cleaner architecturally, this would introduce new dependencies and require revalidation of IAM policies. Chunked scanning required only pagination logic and was lower-risk to implement in the current codebase.

Why 50 items per chunk: Pilot testing showed that 50-item chunks complete in 2–3 seconds with zero hangs, while 200-item chunks occasionally approached the 30-second timeout threshold. At 50 items, even a table scan of 5,000 records requires only 100 sequential requests (~5 minutes total), which is acceptable for an hourly or on-demand rebuild job.

Why explicit PATH resolution: Rather than requiring operators to set environment variables before running the script, the script now handles PATH resolution internally. This makes the tool more robust across different launch contexts (cron, systemd timers, manual execution).

What's Next

Future work includes monitoring DDB scan latency metrics via CloudWatch and alerting if chunk completion time consistently exceeds 5 seconds, which would indicate table growth, index contention, or AWS capacity issues requiring attention. Additionally, the crew page rebuild pipeline should be integrated into a full test suite that exercises both the DDB scan and S3 upload paths with synthetic data, ensuring that new deployments catch PATH or timeout issues before they reach production.

```