Change Tracking for Synchronization
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
✦ Skip the page breaks and see fewer ads — read each lesson on a single page with Pro
Lesson: Mastering Change Tracking for Dataverse Synchronization
Introduction: The Challenge of Data Synchronization
In modern enterprise architecture, Dataverse often serves as the "source of truth" for customer relationship management (CRM) data. However, this data rarely exists in a vacuum. It must flow to external systems—such as ERPs, data warehouses, or custom web portals—to support broader business processes. A common mistake developers make when building these integrations is attempting to synchronize data by querying the entire dataset repeatedly. This approach, known as "full polling," is inefficient, puts unnecessary load on the Dataverse API, and significantly increases the latency of your integration.
Change tracking is the architectural solution to this problem. Instead of asking, "What does the entire table look like right now?", change tracking allows your integration to ask, "What has changed since the last time I checked?" By capturing only the incremental updates—the inserts, updates, and deletes—you reduce the volume of data transferred, minimize API consumption, and ensure that your downstream systems remain in sync with minimal overhead.
This lesson explores the mechanics of change tracking in Dataverse. We will move beyond the basic concepts to understand how to enable change tracking, how to query for changes using the Web API and SDK, and how to manage the lifecycle of synchronization tokens. Whether you are building a custom integration service or using a middleware tool, understanding these fundamentals is critical to building performant and reliable data pipelines.
Understanding the Mechanics of Change Tracking
At its core, change tracking in Dataverse is a feature that maintains a record of modifications to a specific table. When you enable change tracking on an entity (table), Dataverse begins tracking every transaction that modifies the records in that table. This metadata is stored internally, and the system provides a specific API mechanism to retrieve these changes based on a "version" or "token."
When you query for changes, you receive a collection of records that have changed, along with a new "delta token." You store this token on your side. In your next synchronization cycle, you pass that stored token back to Dataverse. The system then compares your token to the current version of the table and returns only the records modified since that specific point in time.
Why Is This Better Than Polling?
- Bandwidth Efficiency: You only transmit the records that actually changed. If you have a million records but only ten changed in the last hour, you transfer ten records instead of a million.
- Performance: The Dataverse engine is highly optimized to retrieve these deltas. It does not need to scan the entire database table, which keeps your queries fast regardless of total table size.
- Deleted Record Handling: Standard queries cannot see records that have been deleted. Change tracking specifically includes a "deleted records" list in the response, allowing your integration to remove records from the destination system that no longer exist in the source.
Callout: Change Tracking vs. Polling When you use a "Last Modified" timestamp field for synchronization, you often encounter two problems: missing deleted records and missing updates that occur within the same millisecond as your query. Change tracking solves this by using internal system version numbers, ensuring that no transaction is missed and providing an explicit list of deletions.
Enabling Change Tracking in Dataverse
Before you can utilize the change tracking API, you must explicitly enable it on the entity within the Dataverse environment. This is a metadata configuration change that does not affect existing data but initiates the tracking process moving forward.
Steps to Enable Change Tracking
- Access the Power Apps Portal: Navigate to the environment where your solution resides.
- Locate the Entity: Go to "Data" and then "Tables." Select the table you want to synchronize.
- Edit Table Properties: Click on the "Settings" or "Advanced Options" for that table.
- Enable Change Tracking: Look for the "Advanced options" section. You will see a checkbox labeled "Change tracking." Ensure this is checked.
- Publish: Save your changes and ensure you publish the customizations.
Note: Enabling change tracking on a table that contains a massive amount of data may take a few moments to initialize. Once enabled, Dataverse starts tracking changes immediately, but it cannot retrospectively generate a change log for actions that occurred before the feature was enabled.
Implementing Change Tracking via the Web API
The Dataverse Web API provides a clean, OData-compliant way to retrieve changes. This is the most common approach for external applications, such as those written in Node.js, Python, or C#.
The Initial Request
To get the initial state of the data, you perform a standard query. However, to prepare for future synchronization, you should request the @odata.nextLink or specific change tracking headers. The most effective way is to use the RetrieveEntityChanges function.
Code Example: Fetching Changes (C# / HttpClient)
// Define the request to retrieve changes
// The 'EntityName' is the logical name of your table
string requestUrl = $"{baseUrl}/api/data/v9.2/RetrieveEntityChanges(EntityName='account', DataVersion=null, PageSize=5000)";
// Send the request
HttpResponseMessage response = await httpClient.GetAsync(requestUrl);
string jsonResponse = await response.Content.ReadAsStringAsync();
// The response will contain:
// 1. A list of 'EntityChanges' (the modified records)
// 2. A 'DataToken' (the version marker you must save)
Handling the Data Token
The DataToken returned in the response is the most important piece of data for your integration. You must persist this token in your destination system’s database or a configuration store.
When you run your integration again, you pass this token back to the API:
// Use the token from the previous run
string previousToken = "1234567!12345";
string requestUrl = $"{baseUrl}/api/data/v9.2/RetrieveEntityChanges(EntityName='account', DataVersion='{previousToken}', PageSize=5000)";
If the API returns a new token, discard the old one and store the new one. If the API returns the same token, it means no changes have occurred since your last check.
Advanced Synchronization Patterns
Synchronization is rarely just a "fetch and update" operation. You often need to handle relationships, complex data types, and potential conflicts.
Handling Deleted Records
One of the primary benefits of using RetrieveEntityChanges is the inclusion of deleted records. In the JSON response, you will see a property called RemovedEntities. This is a collection of objects that contain the ID of the record that was deleted.
Your integration logic should look something like this:
- Parse the
EntityChangeslist to upsert or update records in the target system. - Parse the
RemovedEntitieslist to identify records that must be deleted in the target system. - Execute the deletions in your target database.
Pagination in Change Tracking
When dealing with large datasets, a single request might not return all changes. The API supports pagination. If the response contains a @odata.nextLink, you must follow that link to fetch the next "page" of changes. You continue this process until the @odata.nextLink is null. Only after you have processed all pages should you store the final DataToken.
Warning: Never store an intermediate
DataTokenif you are still paginating through a result set. If your process crashes in the middle of a multi-page sync, you might miss records. Always ensure the entire batch is processed before updating your persistent token store.
Best Practices for Reliable Synchronization
Building a synchronization service requires a "defensive programming" mindset. Because networks fail and APIs have rate limits, your integration must be resilient.
1. Idempotency is Key
Your integration should be idempotent, meaning that running the same synchronization task multiple times should result in the same state in the target system. If you receive the same record update twice, your code should handle it gracefully without creating duplicates or errors. Always use the Dataverse Primary Key as the unique identifier in your destination system to ensure updates happen in the right place.
2. Implement Exponential Backoff
Dataverse has API limits. If your synchronization service hits a rate limit (HTTP 429), you should not immediately retry. Implement an exponential backoff strategy where you wait for a short period, then double that time for the next retry, and so on. This prevents your integration from overwhelming the platform during busy periods.
3. Log Everything
Since synchronization happens in the background, you will not see errors as they occur. Log every successful synchronization, every failure, and the count of records processed. Include the DataToken in your logs so you can trace specific synchronization cycles if data appears to be missing or mismatched.
4. Monitor Token Staleness
If your integration stops running for an extended period, the DataToken might become "stale." Dataverse only keeps change logs for a limited window (usually 30 days). If your token is older than the retention window, the API will return an error. Your code must be able to detect this "token expired" error and trigger a full re-sync (a "fresh start") automatically.
Comparison: Synchronization Strategies
| Strategy | Performance | Complexity | Deleted Records | Best For |
|---|---|---|---|---|
| Full Polling | Very Low | Very Simple | No | Tiny datasets (<100 rows) |
| Timestamp Polling | Medium | Medium | No | Systems without tracking |
| Change Tracking | Very High | High | Yes | Production-grade integrations |
Common Pitfalls and How to Avoid Them
Pitfall 1: Ignoring the "Change Tracking Window"
As mentioned, change tracking is not infinite. If your integration is offline for several weeks, the version token will expire. Developers often ignore this, resulting in a system that simply stops working.
- Solution: Always implement a "fallback" logic. If the API returns an error indicating the token is invalid or expired, clear the local token, log a warning, and perform a full data pull to reset the synchronization.
Pitfall 2: Overloading the API
Some developers attempt to fetch changes for every table in the system simultaneously. This can lead to throttling and performance degradation.
- Solution: Stagger your synchronization tasks. Use a queue or a job scheduler to process one table at a time or use a distributed task runner that respects API concurrency limits.
Pitfall 3: Failing to Handle Data Types
Dataverse returns data in specific formats (e.g., Lookups, OptionSets, Money). When synchronizing, your code must translate these types into the format expected by the target system.
- Solution: Create a transformation layer in your code that maps Dataverse types to your target schema. Do not attempt to write raw Dataverse JSON directly to your database.
Step-by-Step: Building a Basic Sync Engine
If you are tasked with building a sync engine, follow this roadmap:
- Define the Mapping: Create a configuration file that maps Dataverse entity fields to your destination database columns.
- Initialize the Token: Check your storage (e.g., SQL Server, Redis) for the last known
DataToken. If null, perform a full query to seed the initial state. - Fetch Changes: Call
RetrieveEntityChangesusing the stored token. - Process Changes:
- Iterate through
EntityChanges. Map the data and perform anUPSERTon the destination. - Iterate through
RemovedEntities. Perform aDELETEon the destination.
- Iterate through
- Save Token: Once all pages are processed, update your storage with the latest
DataTokenreturned by the final API response. - Error Handling: Wrap the entire operation in a
try-catchblock. If an error occurs, send an alert to your monitoring system.
Tip: If you are using C#, utilize the
Microsoft.Xrm.Sdklibraries, which abstract much of the complexity of the Web API. TheRetrieveEntityChangesRequestclass is designed specifically for this purpose and handles many of the lower-level details for you.
FAQ: Common Questions about Change Tracking
Q: Does change tracking affect the performance of Dataverse itself? A: Enabling change tracking has a negligible impact on the performance of standard Dataverse operations. The overhead is managed by the platform and is designed to be highly efficient.
Q: Can I use change tracking for custom entities? A: Yes, change tracking can be enabled for both system entities (like Accounts and Contacts) and custom entities you create.
Q: What happens if I make a schema change to the table? A: Schema changes (adding or removing fields) do not break change tracking. However, if you remove a field that your integration relies on, your mapping logic will need to be updated.
Q: How long does the change log persist? A: The default retention period is 30 days. This means if you do not sync your data at least once every 30 days, your token will expire, and you will need to perform a full re-sync.
Key Takeaways
- Efficiency is Paramount: Never use full-table polling for production integrations. Change tracking is the industry-standard way to minimize API load and maximize performance.
- Token Management: The
DataTokenis the heartbeat of your integration. Treat it as a critical piece of state that must be persisted reliably. - Handle Deletions: Unlike timestamp-based approaches, change tracking provides an explicit list of deleted records. This is vital for maintaining data integrity in downstream systems.
- Expect Failure: Network issues, API throttling, and token expiration are normal. Build your integration to be self-healing, with automated logic for re-syncing when tokens expire.
- Pagination is Mandatory: Always check for
@odata.nextLinkin your API responses. Assuming all changes will fit in a single response will eventually lead to data loss as your dataset grows. - Maintain Mapping Logic: Keep your data transformation layer separate from your integration logic. This makes it easier to update field mappings without rewriting your synchronization engine.
- Monitor Your Syncs: Integration is a background process. If it fails, you won't know unless you have robust logging and alerting in place.
By following these principles, you move from building fragile, "quick-and-dirty" scripts to creating professional, scalable data pipelines. Change tracking is a powerful tool in the Dataverse developer's toolkit—use it wisely, and your integrations will remain performant even as your data volumes scale.
Continue the course
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons