Handling Real-Time Data Synchronization with Cosmos DB Change Feed
Stop polling your database for updates. Learn how to use the Cosmos DB Change Feed to build event‑driven, real‑time data pipelines with a concrete Azure Functions example.
01 Jul 2026, 09:07 UTC

The Polling Problem in Distributed Data
\nMany developers attempt to synchronize data between a primary database and a downstream service (like an elasticsearch index or a cache) by polling the database for updated timestamps. This approach creates a \"noisy neighbor\" problem: as the dataset grows, polling queries consume more Request Units (RUs), increase latency, and often miss rapid updates that occur between poll intervals.
\nThe solution is the Cosmos DB Change Feed. Instead of asking the database \"what changed?\", the Change Feed pushes a persistent, ordered log of changes to your application. This allows you to build event‑driven pipelines that react to data changes in milliseconds without scanning the entire container.
\n\nHow the Change Feed Operates
\nThe Change Feed is a record of changes to a container's documents. It specifically tracks inserts and updates. When a document is created or modified, the Change Feed records the latest version of that document.
\nCrucially, the feed is scoped to the logical partition key. While the feed guarantees that changes within a single partition are processed in the order they occurred, there is no global ordering across different partitions. This design allows the Change Feed to scale horizontally; multiple processor instances can handle different partitions simultaneously without locking the entire database.
\n\nWorked Example: Streaming Orders to Analytics
\nConsider a scenario where an e‑commerce application stores orders in Cosmos DB (SQL API) and needs to update a real‑time dashboard. Using an Azure Function with a Cosmos DB trigger is the most efficient way to implement this.
\n\nConfiguration Example
\nIn your function.json or via C# attributes, you bind the function to the container. The function will automatically wake up when changes are detected.
[FunctionName(\\"ProcessOrderChanges\\")]\npublic static async Task Run(\n [CosmosDBTrigger(\n databaseName: \\\"StoreDatabase\\\",\n containerName: \\\"Orders\\\",\n Connection = \\\"CosmosDBConnection\\\",\n LeaseContainerName = \\\"leases\\\",\n CreateLeaseContainerIfNotExists = true)]\n IReadOnlyList<Order> input,\n ILogger log)\n{\n if (input != null && input.Count > 0)\n {\n log.LogInformation($\\"Processing {input.Count} order updates.\\");\n foreach (var order in input)\n {\n // Push to downstream analytics or cache\n await UpdateDashboardAsync(order);\n }\n }\n}\n\nOperational Details
\n- \n
- Run Location: This code runs within the Azure Functions runtime. \n
- Permissions: The connection string must have read access to the source container and read/write access to the
leasescontainer. \n - The Lease Container: This is a separate Cosmos DB container used as a checkpoint. It stores the \"state\" of the feed, ensuring that if the function restarts, it picks up exactly where it left off. \n
- Risk: If the
leasescontainer is deleted or corrupted, the processor may restart from the beginning of the feed (depending on configuration), leading to duplicate processing of old data. \n
Critical Limitations and Trade‑offs
\nWhile powerful, the Change Feed is not a full Change Data Capture (CDC) tool in the traditional relational sense. You must account for two primary limitations:
\n\nThe Deletion Gap
\nThe Change Feed does not emit events for deletes. If a document is deleted from the container, the Change Feed remains silent. To synchronize deletes to a downstream system, you must implement a soft‑delete pattern: instead of deleting the record, update a property (e.g., \\"isDeleted\\": true). This update triggers the Change Feed, allowing the downstream service to remove the corresponding record.
RU Consumption
\nAlthough the Change Feed is decoupled from your primary application queries, the processor still consumes RUs to read the log. In environments with extremely high write volumes, the RU cost of the Change Feed processor can become significant. You should monitor the RU usage of the lease container and the source container separately to ensure the pipeline isn't throttling your primary API calls.
\n\nVerifying Your Implementation
\nTo verify that your pipeline is functioning and handling the limitations correctly, perform these three checks:
\n- \n
- Insert Test: Insert a new document via the Data Explorer. Check the Azure Function logs to confirm the document appears in the
inputlist immediately. \n - Update Test: Modify a field in an existing document. Verify that the updated version of the document is emitted. \n
- Delete Test: Perform a hard delete of a document. Confirm that no event is triggered in your function. Then, perform a soft‑delete (update
isDeleted = true) and confirm the event is captured. \n
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.