Chapter 01 · MongoDB Atlas Stream Processing
Subchapter 1.2
references/development-workflow.mdMarkdown15 KBView on GitHub
Understanding stage categories helps compose valid pipelines. Stages must appear in this order:
| Category | Stages | Rules |
|---|---|---|
| Source (1, required) | $source | Must be first. One per pipeline. |
| Stateless Processing | $match, $project, $addFields, $unset, $unwind, $replaceRoot, $redact | Can appear anywhere after source. No state or memory overhead. |
| Enrichment | $lookup, $https | I/O-bound. Use parallelism setting. Place $https after windows to batch. |
| Stateful/Window | $tumblingWindow, $hoppingWindow, $sessionWindow | Accumulates state in memory. Monitor memoryUsageBytes. |
| Validation | $validate | Schema enforcement. Use validationAction: "dlq" (not "error"). Place early to catch bad data. |
| Custom Code | $function | JavaScript UDFs. Requires SP30+. |
| Output (1+, required for deployed) | $merge, $emit | Must be last. Required for persistent processors. Sinkless = ephemeral only. |
Key ordering principle: Place $match as early as possible (reduces volume for all downstream stages). Place $project after $match (reduces document size). Place $https after windows (batches API calls).
Goal: Workspace and connections ready.
Discover existing resources:
atlas-streams-discover → list-workspaces — see what already existsinspect-workspace to review configCreate workspace (if needed):
atlas-streams-build → resource: "workspace"tier: "SP10" for developmentincludeSampleData: true (default) gives you sample_stream_solar for testingVerify workspace:
atlas-streams-discover → inspect-workspace — confirm state and regionGoal: All data sources and sinks connected and verified.
Identify required connections:
$merge, Kafka for $emit, S3, Kinesis)$https, Cluster for $lookup)Create each connection:
atlas-streams-build → resource: "connection" for eachVerify connections:
atlas-streams-discover → list-connections — confirm all createdatlas-streams-discover → inspect-connection for each — verify state and configGoal: Working processor with validated pipeline.
BEFORE creating any processor, you MUST validate all connections referenced in your pipeline. This prevents silent failures and confusion about data destinations.
Step 1: List all connections in workspace
atlas-streams-discover → action: "list-connections", workspaceName: "<your-workspace>"Verify all required connections exist.
Step 2: Inspect EACH connection referenced in pipeline
For EVERY connectionName in your pipeline (source, sink, enrichment), inspect it:
atlas-streams-discover → action: "inspect-connection",
workspaceName: "<your-workspace>",
resourceName: "<connection-name>"Verify for each connection:
$source (change streams), $merge, $lookup$source, $emit$emit only$https enrichment or sink$externalFunction onlyclusterName field points to the intended clusterStep 3: Present validation summary to user
Always show the user what connections will be used:
"Before creating processor '<name>', I've verified your connections:
- ✅ sample_stream_solar → Sample data (READY)
- ⚠️ atlascluster → ClusterRestoreTest (READY)
Warning: Connection name 'atlascluster' doesn't match actual cluster 'ClusterRestoreTest'
- ✅ open-meteo-api → https://api.open-meteo.com/v1/... (READY)
Proceed with processor creation?"Step 4: Wait for user confirmation if warnings exist
If any connection name doesn’t match its target, ask the user to confirm before proceeding.
Step 5: Only then create the processor
This validation workflow prevents:
Follow incremental pipeline development — test at each step:
Step 1: Basic connectivity
[
{"$source": {"connectionName": "my-source"}},
{"$merge": {"into": {"connectionName": "my-sink", "db": "test", "coll": "step1"}}}
]Create with autoStart: true. Verify documents flow. Stop processor.
Step 2: Add filtering
[
{"$source": {"connectionName": "my-source"}},
{"$match": {"status": "active"}},
{"$merge": {"into": {"connectionName": "my-sink", "db": "test", "coll": "step2"}}}
]Modify pipeline (stop → modify-processor → start). Verify filtered output.
Step 3: Add transformations
[
{"$source": {"connectionName": "my-source"}},
{"$match": {"status": "active"}},
{"$addFields": {"processed_at": "$$NOW_NOT_VALID"}},
{"$project": {"userId": 1, "amount": 1, "processed_at": 1}},
{"$merge": {"into": {"connectionName": "my-sink", "db": "test", "coll": "step3"}}}
]Remember: $$NOW is NOT valid in streaming. Use a field from the source document or omit.
Step 4: Add windowing or enrichment (if needed)
Step 5: Add error handling
{"dlq": {"connectionName": "my-sink", "db": "streams_dlq", "coll": "failed_docs"}}$ifNull for optional enrichment fieldsonError: "dlq" on $https stagesGoal: Processor verified working correctly.
Confirm processor state:
atlas-streams-discover → inspect-processor — state should be STARTEDRun diagnostics:
atlas-streams-discover → diagnose-processor — full health reportVerify data flow:
count tool on output collection — documents arriving?find tool on output collection — data looks correct?count tool on DLQ collection — any errors?find tool to inspect failure reasonsClassify output volume:
Goal: Processor running at appropriate tier with monitoring.
Right-size the tier:
memoryUsageBytes from diagnostics$merge, $lookup, $httpsatlas-streams-manage → stop-processor, then start-processor with tier overrideEnsure DLQ is configured (mandatory for production)
Use descriptive processor names (e.g., fraud-detector, order-enricher, iot-rollup)
atlas-streams-discover → inspect-connection — check statebootstrapServers is a comma-separated string (not array)atlas-list-clusters)atlas-streams-discover → diagnose-processor — check state and errors$source, missing sink)$$NOW/$$ROOT/$$CURRENT used (not valid in streaming)$source missing topic fieldlist-connections firstfind tool on DLQ collection — inspect error messages$https enrichment failures (API down, auth expired)$addFields or $project expressionsstop-processor → modify-processor (fix pipeline) → start-processoratlas-streams-discover → diagnose-processor — check statsmemoryUsageBytes — if near 80% of tier RAM, upgrade tier$match is early in pipeline (reduces downstream volume)$https has parallelism setting (increase for I/O-bound enrichment)partitionIdleTimeout (idle Kafka partitions block windows)atlas-streams-discover → list-processorscount tooldiagnose-processor for each production processormemoryUsageBytes trends — approaching 80%?| Symptom | Likely cause | Action |
|---|---|---|
| Processor FAILED on start | Invalid pipeline syntax, missing connection, $$NOW used | diagnose-processor → read error → fix pipeline |
| DLQ filling up | Schema mismatch, $https failures, type errors | find on DLQ → fix pipeline or connection |
| Zero output (transformation) | Connection issue, wrong topic, filter too strict | Check source health → verify connections → check $match |
| Zero output (alert) | Probably normal — no anomalies detected | Verify with known test event |
| Windows not closing | Idle Kafka partitions | Add partitionIdleTimeout to $source (e.g., {"size": 30, "unit": "second"}) |
| OOM / processor crash | Tier too small for window state | diagnose-processor → check memoryUsageBytes → upgrade tier |
| Slow throughput | Low parallelism on I/O stages | Increase parallelism on $merge/$lookup/$https |
| 404 on workspace | Doesn’t exist or misspelled | discover → list-workspaces |
| 409 on create | Name already exists | Inspect existing resource or pick new name |
| 402 error on start | No billing configured | Do NOT retry. Add payment method in Atlas → Billing. Use sp.process() in mongosh as free alternative |
| “processor must be stopped” | Tried to modify running processor | manage → stop-processor first |
| bootstrapServers format | Passed as array instead of string | Use comma-separated string: "broker1:9092,broker2:9092" |
| “must choose at least one role” | Cluster connection without dbRoleToExecute | Defaults to readWriteAnyDatabase — or specify custom role |
| “No cluster named X” | Cluster doesn’t exist in project | atlas-list-clusters to verify |
| IAM role ARN not found | ARN not registered in project | Register via Atlas → Cloud Provider Access |
| dataProcessRegion format | Wrong region format | See region table above. If unsure, inspect an existing workspace |
| Processor PROVISIONING for minutes | Restart cycle with exponential backoff | Wait for FAILED state, or stop → restart. Check logs for repeated error |
| Parallelism exceeded | Tier too small for requested parallelism | Start with higher tier (see sizing-and-parallelism.md) |
| Networking change needed | Networking is immutable after creation | Delete connection and recreate with new networking config |
| 401 / 403 on API call | Invalid or expired Atlas API credentials | Verify apiClientId/apiClientSecret and project-level permissions |
| 429 rate limit | Too many API calls | Wait and retry; avoid tight loops of discover calls |
Before creating a processor, verify:
atlas-streams-discover → action: "list-connections" to list all connections in workspaceatlas-streams-discover → action: "inspect-connection" for EACH connection referenced in pipelinesearch-knowledge was called to validate sink/source field names$source and ends with $merge, $emit, $https, or $externalFunction (async)$$NOW, $$ROOT, or $$CURRENT in the pipeline$source includes a topic field$source with windowed pipeline includes partitionIdleTimeout (prevents windows from stalling on idle partitions)$https enrichment or sink stages, not in $source$https stages use onError: "dlq" (not "fail")$externalFunction stages use onError: "dlq" and execution is explicitly setAfter creating and starting a processor:
atlas-streams-discover → action: "inspect-processor" — confirm state is STARTEDatlas-streams-discover → action: "diagnose-processor" — check for errors in the health reportcount tool on the DLQ collection — verify no errors accumulatingfind tool on the output collection — verify documents are arriving