TL;DR
A budget with three published lines. Microsoft states a two-minute transaction time limit that includes the time to process standard and custom Dataverse plugins, a ceiling of 1,000 records per single transaction in the finance and operations to Dataverse direction, and a plan of record restricting dual-write to a one-to-one mapping. Every dual-write write spends from all three, and the bill arrives at posting time.
What does dual-write actually cost you in transaction budget?
It costs a transaction time limit of two minutes that your Dataverse plugins spend out of, a ceiling of 1,000 records per single transaction in one of the two directions, and a topology Microsoft describes as its current plan of record. All three are published. All three are spent on every write, and none of them appears in a demo.
Dual-write [GA] is not synchronisation you get for nothing. It is a budget, and this is the tariff.
Microsoft frames it as a budget on the first screen
Dual-write limits for live synchronization (page dated 2026-04-03) opens by naming the three meters:
"Finance and operations apps and Dataverse have many processes that span large numbers of records and complex, multitable transactions. Each environment has limits on the number of transactions, the number of records per transaction, and transaction time (that is, the time that is required to process the transaction)."
That is the whole article in one sentence from the vendor. What follows is what each meter reads and who else is drawing on it.
Line one: two minutes, and your Dataverse plugins spend out of the same two minutes
This is the sentence to put in front of a CIO. Microsoft describes what the timer covers:
"The whole time that is spent in Dataverse includes the time that is required to write, and also the time that is required to process the standard and custom plugins. If the transaction exceeds the time limit, the records aren't committed to Dataverse."
And what happens when it runs out:
"If a transaction doesn't complete before the transaction time limit, dual-write doesn't commit the records to finance and operations apps and Dataverse. In this case, dual-write rolls back the records in the transaction in both the finance and operations environment and the Dataverse environment."
Read those two together. A synchronous plugin (custom code Dataverse runs inside a write) that somebody else registered on the Dataverse side is inside your ERP's posting transaction, and if it is slow enough the posting is rolled back on both sides. The team that wrote the plugin does not necessarily know the ERP is downstream of it.
The clock is explicit about where it starts and stops: "the timer begins when the business logic in finance and operations apps is completed and the Dataverse process is started. The timer ends when the transaction is committed."
Microsoft's mitigation is aimed squarely at the plugins — "To reduce the likelihood of timeouts, explore optimization options in the Dataverse plugin" — and the limit is symmetrical: "The same principle applies when you use dual-write to write data in the other direction, from Dataverse to finance and operations apps."
One thing the page does not publish is how the two minutes divide between the two sides. It gives a total and a start point, not a split. Budget against the total.
Line two: 1,000 records — and check which direction you are writing in
The sync-limits page carries two separate tables, one per direction, and they do not say the same thing. That distinction is easy to lose, and it was lost in the notes this article started from.
Finance and Operations to Dataverse. "Number of records per single transaction" is 1,000 records, with the instruction: "If there are more than 1,000 records in a single transaction, consider splitting that transaction into multiple transactions."
Dataverse to Finance and Operations. There is no row count in that table. The constraint is payload size — "The limit is 116.85 megabytes (MB) per transaction" — and Microsoft says plainly why it cannot be restated as rows: "Multiple factors affect how you use this limit, such as entity complexity, the type of columns that you use, and mapped fields. Therefore, you can't express the limit as a simple number of records."
Exceed it and Dataverse rejects the message with a specific string:
Error Code: -2147220970 Error Message: Message size exceeded when sending context to Sandbox. Message size: ### MB
Both directions carry the time limit. Only one of them carries the row ceiling. If your design writes Dataverse to ERP and you have sized it against 1,000 rows, you have sized it against the wrong meter.
Line three: the topology, and the exact words Microsoft used
The Dual-write FAQ (page dated 2026-04-03) answers this under a question that names the architecture people actually want: "Do you plan to enable dual-write to use Dataverse as a hub between multiple finance and operations environments? If Dataverse is used as a hub, you can sync data between two or more finance and operations environments."
The answer, verbatim and complete:
"The current plan of record is to restrict dual-write to a one-to-one (1:1) mapping between a single finance and operations environment and a single Dataverse environment."
Keep the hedge. This is a plan-of-record statement about a roadmap, not a sentence declaring a permanent architectural impossibility, and repeating it as "dual-write only supports 1:1" quietly upgrades it. It is still binding on your design today — you should not plan a Dataverse hub fanning out to several ERP environments on dual-write — but it is a restriction Microsoft has framed as current, and it is worth re-reading that FAQ answer before you build a five-year architecture around either reading of it. Of the five dual-write pages opened for this article, that FAQ answer is the only place topology is addressed.
The budget, on one screen
- Transaction time limit — What Microsoft publishes: two minutes in both tables; the Finance and Operations-to-Dataverse table adds that transactions running longer "are aborted on the source and target side" · Which direction: Both · Where it is stated: sync-limits, both direction tables; the abort wording in the first only
- Records per single transaction — What Microsoft publishes: 1,000 records · Which direction: Finance and Operations to Dataverse only · Where it is stated: sync-limits, first direction table
- Payload per transaction — What Microsoft publishes: 116.85 MB, "you can't express the limit as a simple number of records" · Which direction: Dataverse to Finance and Operations only · Where it is stated: sync-limits, second direction table
- Legal entities — What Microsoft publishes: up to 250 per transaction for live synchronization; initial synchronization "supports only 40 legal entities" · Which direction: Live sync and initial sync · Where it is stated: sync-limits, Legal entities
- Number of transactions — What Microsoft publishes: Service protection API limits one way; priority-based throttling the other · Which direction: Differs by direction · Where it is stated: sync-limits, both direction tables
- Topology — What Microsoft publishes: "The current plan of record is to restrict dual-write to a one-to-one (1:1) mapping…" · Which direction: Environment pairing · Where it is stated: Dual-write FAQ, Dual-write setup
Where the bill lands: X++ you may not have written
The row ceiling is not paid by your integration. It is paid inside the ERP, in X++, by whoever owns the batch job. Microsoft's own remedy is a code shape, published as sample X++ under the heading Transactions with more than 1,000 records: split one ttsbegin/ttscommit block into several, committing in chunks. The customization guidance page states the design instruction directly — "Make sure changes are in a transaction. Evaluate the number of records per transaction based on your table design."
Microsoft opens the section by noting the case is normal, not exotic: "Scenarios where transactions have more than 1,000 records are common."
The same guidance page lists four items that "aren't handled by business events. Therefore, dual-write doesn't handle these items": the doUpdate method, the doInsert method, "Set-based operations (insert and update)", and "Records where skipBusinessEvents(true) is marked". A set-based update is exactly what an efficient batch job does, so on that list the fast path through your ERP is the path dual-write does not handle.
And the coupling is stated outright in the FAQ: "When the integration is in live synchronization mode, if the synchronization fails on one of the apps, the other app also fails, and users receive an error." That is by design, and it is the right design for a master-data flow. It is a large commitment for a flow that does not need it.
Where we would draw the line
We do not put an Optimizer's write path on dual-write, and the reason is this budget. Dual-write is tightly coupled by design — the overview says so — and it is well suited to keeping customer, product and vendor masters aligned between finance and operations apps and customer engagement apps. That is a job worth spending a transaction budget on.
An optimized safety-stock level, an optimized price, an optimized pick sequence is not that job. Putting a computed decision inside a two-minute shared transaction means an Optimizer can roll back a posting, which is precisely the coupling we exist to avoid. We surround the ERP; we do not sit inside its commit path. Our write-back goes through a loosely coupled surface, behind a human approval step, inside a named Security Role — and if the write fails, the ERP posting does not.
If dual-write is already in your estate for master data, keep it. The line is about what you add to it.
What to ask this week
- List every synchronous Dataverse plugin registered on a dual-write mapped table. Each one is inside your ERP's transaction budget. Ask who owns each and whether they know that.
- Name the direction of each dual-write flow. The row ceiling applies to one direction only, and the other is bounded by payload size that Microsoft says cannot be restated as rows.
- Find every batch job that writes to a mapped table, and check whether it commits in chunks or in one block. This is an X++ question, and the answer is in code you may not own.
- Re-read the FAQ topology answer against your target architecture before anyone commits to a Dataverse hub across environments. Read the words "current plan of record" as written.
About Cognilium Cognilium is the AI optimization layer for Dynamics 365 — complementary apps that optimize the pricing, inventory, warehouse and planning decisions your ERP manages but can't optimize. Built on Power Platform, Dataverse and Azure. https://cognilium.ai · https://www.linkedin.com/company/37180269/
If you are pricing a write path against your own estate, book a fifteen-minute call and we will walk the budget lines with you live — no deck. https://cognilium.ai
Sources
- Dual-write limits for live synchronization — page dated 2026-04-03
- Dual-write FAQ — page dated 2026-04-03
- Dual-write overview — page dated 2026-01-15
- Customization guidance for dual-write — page dated 2026-01-15
- Guidance for dual-write setup — page dated 2026-01-15
- Service protection API limits (Microsoft Dataverse) — page dated 2026-01-09
Sources
Share this article
Muhammad Mudassir
Founder & CEO, Cognilium AI
Muhammad Mudassir
Founder & CEO, Cognilium AI
Mudassir Marwat's argument is that ERP systems record decisions they never optimise.
