---
# === IDENTITY ===
id: business/erp-integration/dynamics-365-dual-write-capabilities/2026
canonical_question: "What is Dynamics 365 Dual-write — F&O to Dataverse sync, 1000-record limit, 2-minute timeout?"
aliases:
  - "Dynamics 365 dual-write record limits and timeout"
  - "D365 F&O Dataverse synchronization limits"
  - "Dual-write live sync constraints and initial sync guidance"
  - "Microsoft Dynamics dual-write capabilities and limitations"
entity_type: erp_integration
domain: business > erp-integration > dynamics-365-dual-write-capabilities
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === SYSTEM PROFILE ===
systems:
  - name: "Dynamics 365 Finance & Operations"
    vendor: "Microsoft"
    version: "10.0.40+ (PU64+)"
    edition: "All editions"
    deployment: "cloud"
    api_surface: "Dual-write (near-real-time sync)"
  - name: "Microsoft Dataverse"
    vendor: "Microsoft"
    version: "Platform 9.1+"
    edition: "All editions"
    deployment: "cloud"
    api_surface: "Dual-write (near-real-time sync)"

# === VERIFICATION ===
last_verified: 2026-03-02
confidence: 0.92
version: 1.0
first_published: 2026-03-01

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: volatile
  last_breaking_change: "2024-10 — Dual-write async mode preview (PU64)"
  next_review: 2026-08-29
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "F&O to Dataverse: max 1,000 records per single transaction"
  - "Both directions: 2-minute transaction timeout (hard limit, all transactions)"
  - "Dataverse to F&O: 116.85 MB payload size limit per transaction"
  - "Initial sync: max 500,000 rows per run per legal entity"
  - "Initial sync: max 40 legal entities during environment linking"
  - "Live sync: max 250 legal entities per transaction"
  - "Single-threaded tables: max 70,000 rows for initial sync from Dataverse to F&O"
  - "F&O to Dataverse initial sync: 5-minute export timeout for data entities"
  - "Initial sync from Dataverse: 24-hour import result timeout"
  - "1:1 mapping only — one F&O environment to one Dataverse environment"
  - "Both environments must be in the same Azure datacenter and Microsoft Entra tenant"
  - "Dual-write does not trigger on doInsert/doUpdate/doDelete — only Insert/Update/Delete events"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User needs batch/ETL data migration over 500K records"
    use_instead: "Use Data Import/Export Framework (DIXF) or Azure Data Factory"
  - condition: "User needs to sync between two F&O environments"
    use_instead: "Dual-write is 1:1 F&O-to-Dataverse only; use Data Integrator or custom APIs"
  - condition: "User needs Dynamics 365 Business Central integration"
    use_instead: "Business Central uses different integration (Virtual Tables, Power Automate)"

# === AGENT HINTS ===
inputs_needed:
  - key: integration_pattern
    question: "What integration pattern do you need?"
    type: choice
    options:
      - "real-time sync (individual records, <1s latency)"
      - "batch/bulk (scheduled, high volume)"
      - "event-driven (webhook, CDC, platform events)"
      - "file-based (CSV/XML import/export)"
  - key: data_volume
    question: "What's your daily data volume?"
    type: choice
    options:
      - "< 1,000 records/day"
      - "1,000-100,000 records/day"
      - "> 100,000 records/day"
  - key: direction
    question: "What's the data flow direction?"
    type: choice
    options:
      - "inbound (writing to F&O)"
      - "outbound (reading from F&O to Dataverse)"
      - "bidirectional sync"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/business/erp-integration/dynamics-365-dual-write-capabilities/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-03-01)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to: []
  solves: []
  alternative_to: []
  often_confused_with: []

# === SOURCES (8 authoritative sources) ===
sources:
  - id: src1
    title: "Dual-write limits for live synchronization"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/dual-write/sync-limits
    type: official_docs
    published: 2024-08-23
    reliability: authoritative
  - id: src2
    title: "Considerations for initial synchronization"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/dual-write/initial-sync-guidance
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src3
    title: "Guidance for dual-write setup"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/dual-write/connection-setup
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src4
    title: "Error management and alert notifications"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/dual-write/errors-and-alerts
    type: official_docs
    published: 2022-06-24
    reliability: authoritative
  - id: src5
    title: "System requirements for dual-write"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/dual-write/dual-write-system-req
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src6
    title: "Troubleshoot live synchronization issues"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/dual-write/dual-write-troubleshooting-live-sync
    type: official_docs
    published: 2026-01-15
    reliability: authoritative
  - id: src7
    title: "Dual-write FAQ"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/dual-write/dual-write-faq
    type: official_docs
    published: 2025-06-02
    reliability: authoritative
  - id: src8
    title: "Set up and manage dual-write async in finance and operations apps (preview)"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/dual-write/dual-write-async
    type: official_docs
    published: 2025-10-01
    reliability: high
---

# Dynamics 365 Dual-write Capabilities

## TL;DR

- **Bottom line**: Dual-write provides near-real-time bidirectional sync between D365 Finance & Operations and Dataverse, but is capped at 1,000 records per transaction (F&O to Dataverse) with a hard 2-minute timeout in both directions.
- **Key limit**: 1,000 records per single transaction from F&O to Dataverse; 116.85 MB payload limit from Dataverse to F&O; 500,000 rows per initial sync run per legal entity. [src1]
- **Watch out for**: Transactions exceeding the 2-minute timeout are rolled back on BOTH sides -- no partial commits. Split large transactions into 1,000-record batches. [src1]
- **Best for**: Near-real-time master data and transactional data synchronization between F&O apps (Finance, Supply Chain Management, Commerce, HR) and customer engagement apps (Sales, Customer Service, Field Service).
- **Authentication**: Dual-write uses two Microsoft Entra app registrations with application users in Dataverse; requires Dual-Write Runtime User and Dual-Write App User security roles. [src5]

## System Profile

Dual-write is Microsoft's built-in near-real-time synchronization framework that keeps data consistent between Dynamics 365 Finance & Operations (F&O) apps and Microsoft Dataverse (the platform underneath customer engagement apps like Sales, Customer Service, and Field Service). It is not an API in the traditional sense -- it is a platform-level integration that uses table maps to define field-level mappings between F&O data entities and Dataverse tables.

Dual-write operates as a tightly coupled synchronization: when a record changes on one side, the change is synchronously pushed to the other side within the same transaction scope. This means failures on either side cause rollback on both sides. Starting with PU64 (10.0.40), Microsoft introduced an async preview mode that decouples writes for higher throughput.

| Property | Value |
|---|---|
| **Vendor** | Microsoft |
| **System** | Dynamics 365 Finance & Operations + Microsoft Dataverse |
| **API Surface** | Dual-write (platform-level table map sync) |
| **Current Version** | F&O 10.0.40+ (PU64), Dataverse Platform 9.1+ |
| **Editions Covered** | All D365 F&O editions (Finance, Supply Chain Management, Commerce, Human Resources) |
| **Deployment** | Cloud only (both environments must be in the same Azure datacenter) |
| **API Docs URL** | [Dual-write overview](https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/dual-write/dual-write-overview) |
| **Status** | GA (live sync); Preview (async mode) |

## API Surfaces & Capabilities

Dual-write is not a traditional REST/OData API. It is a platform integration framework. However, it provides multiple synchronization modes:

| Sync Mode | Direction | Best For | Max Records/Transaction | Timeout | Real-time? | Bulk? |
|---|---|---|---|---|---|---|
| Live sync (F&O to Dataverse) | F&O -> Dataverse | Individual record CRUD, master data changes | 1,000 records | 2 minutes | Yes (near-real-time) | No |
| Live sync (Dataverse to F&O) | Dataverse -> F&O | CRM-originated changes flowing to ERP | 116.85 MB payload limit | 2 minutes | Yes (near-real-time) | No |
| Initial sync (F&O to Dataverse) | F&O -> Dataverse | First-time data load, migration | 500,000 rows/run/legal entity | 5-min export + processing | No | Yes |
| Initial sync (Dataverse to F&O) | Dataverse -> F&O | First-time data load from CRM | 500,000 rows/run/legal entity | 24-hour import result | No | Yes |
| Async mode (Preview) | Bidirectional | High-volume transactional data (e.g., >1,000 invoice lines) | No 1,000-record cap | Decoupled | No (eventual consistency) | Yes |

[src1, src2, src8]

## Rate Limits & Quotas

### Per-Transaction Limits (Live Sync)

| Limit Type | Value | Direction | Notes |
|---|---|---|---|
| Max records per transaction | 1,000 | F&O to Dataverse | Split larger transactions into 1,000-record batches [src1] |
| Max payload size per transaction | 116.85 MB | Dataverse to F&O | Depends on entity complexity and field types [src1] |
| Transaction timeout | 2 minutes | Both directions | Exceeded = rollback on both sides [src1] |
| Max legal entities (live sync) | 250 | Both directions | Per transaction [src1] |

### Initial Synchronization Limits

| Limit Type | Value | Applies To | Notes |
|---|---|---|---|
| Max rows per run | 500,000 | Per legal entity | Each legal entity runs separately [src2] |
| Max legal entities (linking) | 40 | During environment setup | Live sync supports 250 after setup [src2, src5] |
| Single-threaded table max rows | 70,000 | Dataverse to F&O initial sync | Tables like Customers V3, Vendors V2, Warehouses [src2] |
| F&O data export timeout | 5 minutes | F&O to Dataverse initial sync | Fails if virtual columns or unoptimized queries [src2] |
| Import result timeout | 24 hours | Dataverse to F&O initial sync | F&O must return import result within 24h [src2] |
| Max lookups per table map | 10 | Dataverse initial sync | Split lookups if >10 to avoid errors [src2] |

### Platform-Level Limits

| Limit Type | Value | Notes |
|---|---|---|
| Dataverse API calls | Service protection limits | Standard Dataverse API limits apply (per-org throttling) [src1] |
| F&O API throttling | Priority-based throttling | Prevents over-utilization of resources [src1] |
| Error log retention | 1 day | Catch-up error records purged after 24h [src4] |
| Top errors viewable (initial sync) | 5 | Only top 5 errors shown in initial sync log [src2] |

## Authentication

Dual-write authentication is platform-managed. No manual OAuth token handling is required -- the framework handles authentication between the linked environments.

| Requirement | Details | Notes |
|---|---|---|
| Microsoft Entra tenant | Both environments must share the same tenant | Cross-tenant not supported [src5] |
| App registrations | Two Microsoft Entra app registrations required | Set up during Lifecycle Services linking |
| Dataverse application users | Two application users in Dataverse | Must have System Admin role initially [src6] |
| Dual-Write Runtime User | Security role required for all dual-write users | Without this, row creation fails in Dataverse [src6] |
| Dual-Write App User | Security role for Company and Currency tables | All dual-write users need read access [src2] |

### Authentication Gotchas

- Missing the **Dual-Write Runtime User** security role causes `403 Forbidden` errors: `"The user isn't a member of the organization"`. Every dual-write user must have this role. [src6]
- Application users created in Dataverse must be granted System Admin role during initial setup; reduce permissions after configuration is complete. [src6]
- After a database refresh or environment copy, all dual-write configurations (DualWriteProjectConfiguration, DualWriteRuntimeConfiguration) must be cleaned up and environments re-linked. [src6]

## Constraints
<!-- Agents: read this section before recommending any integration approach from this unit.
     These are hard constraints that must not be violated. -->

- **1,000-record hard cap (F&O to Dataverse)**: Any single transaction with more than 1,000 records must be split into multiple transactions of 1,000 or fewer records each. [src1]
- **2-minute timeout (both directions)**: The entire Dataverse plugin processing (including custom plugins) must complete within 2 minutes, or the transaction rolls back on both sides. [src1]
- **1:1 environment mapping only**: A single F&O environment can only link to a single Dataverse environment. No hub-and-spoke topology. [src5, src7]
- **Same datacenter required**: F&O and Dataverse environments must be in the same Azure datacenter and the same Microsoft Entra tenant. [src5]
- **doInsert/doUpdate/doDelete not supported**: Dual-write only triggers on standard Insert/Update/Delete events. Custom X++ using `doInsert()`, `doUpdate()`, or `doDelete()` bypasses dual-write entirely. [src5]
- **No distributed transactions**: If a process is canceled mid-write, one side may have the record while the other does not. [src5]
- **No trial instance support**: Dual-write setup is not supported on trial instances of F&O apps. [src5]
- **No cross-company data sharing**: Dual-write does not support cross-company data sharing configurations. [src5]
- **Prospect to cash incompatibility**: Cannot run dual-write and the legacy Prospect to Cash Data Integrator solution simultaneously; must uninstall Prospect to Cash first. [src5]
- **Company entity pattern matching**: Both entities in a mapping must follow the same company scope pattern (both company-specific or both cross-company). Mixed patterns generate validation error DIPV1002. [src3]

## Integration Pattern Decision Tree

```
START -- Does your scenario need F&O <-> Dataverse sync?
|-- Is it a one-time data migration?
|   |-- Volume < 500K rows per legal entity?
|   |   |-- Multithreaded table? -> Run initial sync (Dataverse to F&O preferred)
|   |   +-- Single-threaded table AND < 70K rows? -> Run initial sync (F&O to Dataverse preferred)
|   |   +-- Single-threaded table AND > 70K rows? -> Migrate separately, skip initial sync
|   +-- Volume > 500K rows? -> Migrate to each app separately, skip initial sync
|
|-- Is it ongoing real-time sync?
|   |-- Records per transaction < 1,000?
|   |   +-- YES -> Use dual-write live sync (default)
|   |   +-- NO -> Split transactions into 1,000-record batches
|   |
|   |-- Transaction processing time < 2 minutes?
|   |   +-- YES -> Standard dual-write
|   |   +-- NO -> Optimize Dataverse plugins, or request 5-min timeout from Microsoft
|   |
|   |-- Need > 1,000 records without splitting?
|   |   +-- YES -> Evaluate dual-write async (preview, PU64+)
|   |   +-- NO -> Standard dual-write
|
|-- Need one-way data flow only?
|   +-- F&O -> Dataverse only -> Dual-write with filter (suppress reverse sync)
|   +-- Dataverse -> F&O only -> Dual-write with filter, or use Virtual Tables
|
+-- Need multi-environment hub? -> Dual-write does NOT support this. Use Azure Data Factory, Power Automate, or custom middleware.
```

## Quick Reference

### Single-Threaded Tables (Special Handling Required)

These tables do not support multithreading during initial sync import. Keep volumes under 70,000 rows when syncing from Dataverse to F&O: [src2]

| Dataverse Table | F&O Entity | Max Initial Sync Rows |
|---|---|---|
| msdyn_taxcodes | Sales tax codes | 70,000 |
| accounts | Customers V3 | 70,000 |
| msdyn_vendors | Vendors V2 | 70,000 |
| msdyn_warehouses | Warehouses | 70,000 |
| msdyn_productcategories | Product categories | 70,000 |
| cdm_employments | Employment | 70,000 |
| cdm_positionworkerassignmentmaps | Position worker assignments | 70,000 |
| msdyn_inventorylocations | Warehouse locations | 70,000 |
| msdyn_shipvias | Modes of delivery | 70,000 |

### Initial Sync Guidance Matrix

| F&O Instance | Dataverse Instance | Volume | Threading | Approach |
|---|---|---|---|---|
| New | New | < 500K | Single-threaded | Migrate to F&O, then initial sync |
| New | New | < 500K | Multithreaded | Migrate to Dataverse, then initial sync |
| New/Existing | New/Existing | > 500K | Any | Migrate separately, skip initial sync |
| New | Existing | < 70K | Single-threaded | Bootstrap Dataverse, then initial sync |
| New | Existing | > 70K | Single-threaded | Migrate separately, skip initial sync |
| Existing | Existing | < 500K | Multithreaded | Bootstrap Dataverse, then initial sync |

[src2]

## Step-by-Step Integration Guide

### 1. Verify system requirements and prerequisites

Ensure your F&O environment is on build 10.0.9 (PU33) or later and Dataverse platform version 9.1+ or later. Both environments must be in the same Azure datacenter and Entra tenant. [src5]

```
# Check F&O version: navigate in F&O to
# Settings > About > Platform version
# Required: Platform update 33 (10.0.383.20013) or later

# Check Dataverse platform version:
# Power Platform admin center > Environments > [Your env] > Version
# Required: 9.1.0000.11732 or later
```

**Verify**: F&O About page shows Platform update >= 33; Dataverse version >= 9.1.0000.11732.

### 2. Link environments via Lifecycle Services

Set up the dual-write connection from Lifecycle Services (LCS). This creates the app registrations, application users, and initial table map configurations. [src3]

```
# In LCS:
# 1. Go to your F&O environment details
# 2. Select Power Platform Integration > Setup
# 3. Select the target Dataverse environment
# 4. LCS provisions the connection and installs dual-write solutions
```

**Verify**: In F&O, navigate to Data Management > Dual-write. The connection status should show "Linked".

### 3. Assign security roles

Add all dual-write users to the Dual-Write Runtime User and Dual-Write App User security roles in Dataverse. [src6]

```
# In Power Platform admin center or Dynamics 365:
# Settings > Security > Security Roles
# Assign "Dual-Write Runtime User" to all integration users
# Assign "Dual-Write App User" to all users needing Company/Currency access
```

**Verify**: Create a test record in F&O -- it should sync to Dataverse without `403 Forbidden` errors.

### 4. Run initial synchronization

For tables with existing data, run initial sync. Follow the guidance matrix based on volume and threading. [src2]

```
# In F&O: Data Management > Dual-write
# Select the table map (e.g., Customers V3 - accounts)
# Click "Initial sync"
# Choose the master side (which side has the authoritative data)
# Monitor the execution log for errors
```

**Verify**: Check the Initial sync details tab -- error count should be 0. Both sides should have matching record counts.

## Code Examples

### X++: Split large transactions into 1,000-record batches

```xpp
// Input:  A set of records to create that exceeds 1,000
// Output: Records created in batches of 1,000, each in its own transaction

int i = 1;
boolean commitPending = false;

while (/* loop condition -- e.g., queryRun.next() */)
{
    if (i == 1)
    {
        ttsbegin;  // Start new transaction
        commitPending = true;
    }

    invoiceRecord.create();  // Or .update(), .delete()

    if (i == 1000)
    {
        ttscommit;  // Commit at 1,000 records
        commitPending = false;
        i = 0;
    }
    i++;
}

if (commitPending)
{
    ttscommit;  // Commit remaining records
}
```

[src1]

### PowerShell: Check dual-write connection status

```powershell
# Input:  Dataverse environment URL and admin credentials
# Output: List of dual-write table maps and their status

# Connect to Dataverse
Install-Module -Name Microsoft.Xrm.Tooling.CrmConnector.PowerShell -Force
$conn = Connect-CrmOnline -ServerUrl "https://yourorg.crm.dynamics.com"

# Query dual-write runtime config
$configs = Get-CrmRecords -EntityLogicalName "msdyn_dualwriteruntimeconfig" `
    -Fields "msdyn_name","msdyn_status" -conn $conn

foreach ($config in $configs.CrmRecords) {
    Write-Host "$($config.msdyn_name): $($config.msdyn_status)"
}
```

## Data Mapping

### Field Mapping Patterns

Dual-write uses table maps that define field-level mappings between F&O data entities and Dataverse tables. Key mapping patterns:

| F&O Entity | Dataverse Table | Key Fields | Mapping Type | Notes |
|---|---|---|---|---|
| CustCustomerV3Entity | accounts | AccountNumber <-> accountnumber | Bidirectional | Customer master data; includes address, contact info |
| VendVendorV2Entity | msdyn_vendors | VendorAccountNumber <-> msdyn_vendoraccountnumber | Bidirectional | Vendor master data |
| SalesOrderHeaderV2Entity | salesorders | SalesOrderNumber <-> ordernumber | Bidirectional | Requires Customers V3 map running first |
| InventProductEntity | products | ProductNumber <-> productnumber | F&O master | Product catalog sync |
| DirPartyBaseEntity | msdyn_parties | PartyNumber <-> msdyn_partynumber | Bidirectional | Party (person/organization) framework |

### Data Type Gotchas

- **Number sequences are independent**: F&O and Dataverse maintain separate number sequences. Plan distinct ranges (e.g., F0001-F4999 for F&O, C5001-C9999 for Dataverse) or create records in one system only. [src7]
- **Company striping**: Dataverse tables are global by default. To make them company-specific, you must add an N:1 relationship to the Company table and include the company foreign key in the table key. [src7]
- **Null vs. empty string**: F&O treats null and empty string differently in query filters. Dual-write filter expressions evaluate against the entity, not individual data sources, so `ParentProject == ""` does not match null values. [src6]
- **Double quotes in data**: Double quotes in source data fields cause initial sync to fail with `MalformedLineException`. Remove double quotes before sync. [src2]

## Error Handling & Failure Points

### Common Error Codes

| Error Code | Message | Cause | Resolution |
|---|---|---|---|
| 0x80072560 | "The user isn't a member of the organization" | Missing Dual-Write Runtime User security role | Assign Dual-Write Runtime User role to all integration users [src6] |
| -2147220970 | "Message size exceeded when sending context to Sandbox" | Payload > 116.85 MB from Dataverse to F&O | Split transaction into smaller batches [src1] |
| 0x80040265 | "A record that has the attribute values Location ID already exists" | Duplicate address key in Dataverse | Install dual-write orchestration package v2.2.2.60+ [src6] |
| DIPV1002 | Company entity pattern validation error | Mixed company-specific and cross-company entities in a mapping | Both entities must follow the same company scope pattern |
| Timeout | Transaction rolled back on both sides | Processing exceeded 2-minute window | Optimize Dataverse plugins; reduce records per transaction [src1] |
| 5 Attempts Failed | "5 Attempts to get data from https://...azure-apim.net... Failed" | Table map has >10 lookups during initial sync | Split lookups into batches of <=10, then re-add [src2] |

### Failure Points in Production

- **Custom Dataverse plugins consuming timeout budget**: The 2-minute timeout includes all plugin execution time. Heavy custom plugins on synced tables can exhaust the budget. Fix: `Profile and optimize all plugins on dual-write-synced tables; move heavy logic to async plugins.` [src1]
- **Initial sync bidirectional surprise**: Initial sync runs in two undocumented sequential steps -- first from master to target, then automatically in reverse. Even if step one fails, step two executes. Fix: `Understand that "master" only controls the first direction; validate data on both sides after sync.`
- **Record-level retry impossible for initial sync**: You cannot resync individual failed records during initial sync. First run is always a full push. Fix: `Fix root cause errors, then re-run full initial sync. Use change tracking for incremental subsequent runs.` [src2]
- **Silent dual-write bypass**: Any X++ code using `doInsert()`, `doUpdate()`, `doDelete()`, or `skipBusinessEvents(true)` silently bypasses dual-write. Fix: `Audit all custom X++ code for these methods; use standard Insert/Update/Delete.` [src5, src6]
- **Environment refresh breaks dual-write**: Database copy/refresh invalidates dual-write configurations. Fix: `Clean up DualWriteProjectConfiguration, DualWriteProjectFieldConfiguration, BusinessEventsDefinition in F&O and msdyn_dualwriteruntimeconfig in Dataverse. Relink environments and reactivate maps.` [src6, src7]
- **Error records purged after 24 hours**: Catch-up error records from paused table maps are automatically deleted after 1 day. Fix: `Monitor and resolve catch-up errors within 24 hours of resuming a paused map.` [src4]

## Anti-Patterns

### Wrong: Creating records in a single large transaction

```xpp
// BAD -- Single transaction with 5,000 records exceeds the 1,000-record limit
ttsbegin;
while (queryRun.next())
{
    salesLine.create();  // Could be 5,000+ records
}
ttscommit;
// Result: Transaction exceeds limit and times out; all records rolled back on BOTH sides
```

### Correct: Batch records into 1,000-record transactions

```xpp
// GOOD -- Split into 1,000-record transactions
int batchCount = 0;
ttsbegin;
while (queryRun.next())
{
    salesLine.create();
    batchCount++;
    if (batchCount == 1000)
    {
        ttscommit;
        ttsbegin;
        batchCount = 0;
    }
}
if (batchCount > 0)
{
    ttscommit;
}
// Result: Each batch commits independently; partial progress is preserved
```

[src1]

### Wrong: Using doInsert() for performance and expecting dual-write sync

```xpp
// BAD -- doInsert() bypasses dual-write entirely
while (queryRun.next())
{
    record.doInsert();  // Fast, but dual-write never triggers
}
// Result: Records exist in F&O but NOT in Dataverse -- silent data inconsistency
```

### Correct: Use standard insert() to trigger dual-write

```xpp
// GOOD -- Standard insert() triggers dual-write
while (queryRun.next())
{
    record.insert();  // Dual-write fires on each insert
}
// Result: Records synced to both F&O and Dataverse
```

[src5, src6]

### Wrong: Running initial sync with >10 lookups on a table map

```
# BAD -- Table map has 12 lookup fields
# Running initial sync from Dataverse
# Result: "5 Attempts to get data from https://...azure-apim.net... Failed"
```

### Correct: Split lookups into batches, sync iteratively

```
# GOOD -- Step 1: Remove non-mandatory lookups to get count <= 10
# Step 2: Run initial sync
# Step 3: Add remaining lookups (remove first batch), run again
# Step 4: Add all lookups back, run map with "skip initial sync"
```

[src2]

## Common Pitfalls

- **Activating dual-write maps before data migration is complete**: Migration performance drops dramatically when dual-write is active during import. Fix: `Complete all data migration first, then activate dual-write maps.` [src2]
- **Not planning number sequence ranges**: F&O and Dataverse have independent number sequences. If both systems create records, collisions occur. Fix: `Assign distinct number sequence ranges per system (e.g., F0001-F4999 for F&O, C5001-C9999 for Dataverse).` [src7]
- **Ignoring the 5-minute F&O export timeout**: Initial sync from F&O to Dataverse times out if data entities have virtual columns with expensive `postLoad` methods or missing indexes. Fix: `Optimize entity queries and add missing database indexes. Upgrade to PU37+ where this is supported.` [src2]
- **Assuming initial sync is one-directional**: Initial sync is actually bidirectional in two sequential steps regardless of which "master" you select. Fix: `Validate data in both systems after initial sync. Understand that step 2 runs in reverse even if step 1 fails.`
- **Not monitoring catch-up errors after resuming**: When you resume a paused table map, queued records may fail validation. Error records are purged after 24 hours. Fix: `Check the Catch-up errors tab immediately after resuming; resolve or delete within 24 hours.` [src4]
- **Using query filters that don't handle null values**: Dual-write filter expressions like `Field == ""` do not match null values. Fix: `Use computed columns with SysComputedColumn::isNullExpression() to convert nulls to empty strings before filtering.` [src6]
- **Configuration key dependencies**: If a configuration key is disabled for fields included in a dual-write entity (e.g., Retail key for Customers V3), live sync fails silently. Fix: `Enable all required configuration keys, or create a new entity with only fields from enabled keys.` [src6]

## Diagnostic Commands

```powershell
# Check dual-write map status in F&O
# Navigate to: Data Management > Dual-write > Select table map > View activity log

# Enable verbose error logging (F&O)
# Open DualWriteProjectConfigurationEntity via Excel add-in
# Set IsDebugMode = Yes on the project
# Verbose logs are written to the DualWriteErrorLog table
# Access at: https://{your-ao-url}/?mi=SysTableBrowser&tableName=DualWriteErrorLog

# Check Dataverse runtime configuration
# Advanced Find > DualWrite Runtime Configuration > View all records
# Delete stale records after environment refresh

# View error alerts configuration
# In F&O: Data Management > Dual-write > select map > Alert settings
# Configure thresholds (e.g., 10 Application errors in 15 minutes = pause)

# Verify security roles in Dataverse
# Settings > Security > Users > select user > View Roles
# Required: "Dual-Write Runtime User" + "Dual-Write App User"
```

[src4, src6]

## Version History & Compatibility

| Version | Release | Status | Key Changes | Notes |
|---|---|---|---|---|
| PU64 (10.0.40) | 2024-10 | Current | Async dual-write mode (preview) | Requires app foundation >= 7.0.7425.0 [src8] |
| PU37+ | 2023 | Supported | 5-minute export timeout support for initial sync | Required for F&O-to-Dataverse initial sync [src2] |
| PU33 (10.0.9) | 2021 | Minimum supported | Original dual-write GA | Build 10.0.383.20013+ required [src5] |
| Dataverse 9.1+ | Ongoing | Required | Platform baseline for dual-write | Version 9.1.0000.11732+ [src5] |
| Orchestration v2.2.2.60 | 2024 | Recommended | Fixed Location Key duplicate errors | Install to resolve 0x80040265 errors [src6] |

### Deprecation Policy

Dual-write solutions are distributed as managed solutions through One Version updates. Microsoft has not announced any deprecation timeline for dual-write. However, the async mode (preview) in PU64 signals a strategic direction toward decoupled synchronization for high-volume scenarios. [src5, src8]

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Near-real-time bidirectional sync between F&O and Dataverse | Data migration over 500K rows per legal entity | Data Import/Export Framework (DIXF), Azure Data Factory |
| Master data sync (customers, vendors, products, warehouses) | Need to sync between two F&O environments | Custom APIs, Data Integrator, or middleware |
| CRM-to-ERP transactional sync (Sales Orders, Invoices) | Trial F&O instances | Not supported on trial instances |
| < 1,000 records per transaction in steady state | > 1,000 records per transaction frequently | Dual-write async (preview) or batch integration |
| Both systems in same Azure datacenter and Entra tenant | Cross-datacenter or cross-tenant integration | Azure Data Factory, Power Automate, or custom middleware |
| Replacing legacy Prospect to Cash Data Integrator | Running alongside Prospect to Cash | Must uninstall Prospect to Cash first |

## Important Caveats

- The 1,000-record limit and 2-minute timeout are hard platform limits. They cannot be raised through configuration or support tickets. The only workaround for the timeout is a 5-minute extension that must be requested through Microsoft Support (for DV-to-F&O direction only). [src1]
- Dual-write async mode (PU64+) is in Preview as of early 2026. It removes the 1,000-record cap but introduces eventual consistency instead of transactional consistency. Evaluate carefully before using in production. [src8]
- Environment refreshes, database copies, and sandbox restores all break dual-write configurations. Plan for complete re-linking and map reactivation after any environment lifecycle operation. [src6, src7]
- Performance tuning is critical: every custom Dataverse plugin executing on a dual-write-synced table consumes time from the 2-minute timeout budget. Profile all plugins before go-live. [src1]
- Alert thresholds are your safety net: configure alerts to auto-pause dual-write before cascading failures. Recommended starting point: pause after 10 Application errors within 15 minutes. [src4]
- Regional availability is limited to Asia, Australia, Brazil, Canada, Europe, France, Germany, India, Japan, South America, Switzerland, UAE, UK, and US. Microsoft has stated there are no plans to support additional regions. [src5]

## Related Units

- Dual-write is often evaluated alongside Virtual Tables, which provide read-only access to F&O data from Dataverse without synchronization.
- For high-volume ETL, consider Azure Data Factory with D365 connectors instead of dual-write.
