Understanding Migration Concurrency
A common source of confusion for customers using MRS and Migration Service to perform migrations into Exchange Online is migration concurrency. The most basic definition of migration concurrency is the number of moves that are allowed to run at the same time against a particular migration endpoint.
It makes sense that all on-premises environments are different and so the tenant admin needs to configure the number of moves to run at the same time that his particular on-premises environment can support. This value is not intended to be a throttle that keeps customers from being able to migrate to the service; instead it is a configuration setting that allows the tenant admin to tune the amount of migrations (and thus load) specific to his environment.
Migration Endpoints are a representation in Office 365 of on-premises MRS Proxy endpoints. For many customers, a single migration endpoint will be sufficient, but others will have multiple migration endpoints when they are migrating from multiple geographic locations. For example, there may be a US endpoint, a France endpoint, and a Japan endpoint.
Migration concurrency is controlled per-endpoint. There are two relevant values here: MaxConcurrentMigrations and MaxConcurrentIncrementalSyncs.
MaxConcurrentMigrations: Controls the approximate number of concurrent moves that will run against this endpoint during the initial data copy phase (Initial Sync).
MaxConcurrentIncrementalSyncs: Controls the approximate number of concurrent moves that will run against this endpoint during phases after the initial data copy including incremental sync and finalization.
These are independent buckets and one does not take away from another. If a move has not yet completed initial sync, it uses a slot from MaxConcurrentMigrations, otherwise it uses a slot from MaxConcurrentIncrementalSyncs.
There is also a value called MaxConcurrentMigrations that is visible in Get-MigrationConfig in Office365. This value does not impact migrations directly. Instead, it simply controls what values you can set on your individual migration endpoints using the Exchange Admin Center or Set-MigrationEndpoint. By default, this value is 300 which is sufficient for the vast majority of customer migrations, but it can be raised by support up to 1000. However, be warned higher concurrency does not guarantee better migration throughput; there will be a "sweet spot" for each environment and it will require some experimentation. Remember also, this value has no effect by itself; it simply allows you to configure higher values on your migration endpoints. For instance, the default value of 300 would allow you to set MaxConcurrentMigrations to 100 on 3 migration endpoints (total 300), etc.
Q: What are the recommended settings for MaxConcurrentMigrations for my environment?
A: Each environment is different. You'll need to experiment to find the right value with the best throughput for your source environment.
Q: Won't increasing the tenant-level MaxConcurrentMigrations make my migrations faster?
A: No. The tenant-level MaxConcurrentMigrations setting has no direct impact on migrations. It only influences the values you are allowed to configure on the individual migration endpoints. The migration endpoint values are what control concurrency during migration.
Q: Why do I see all these moves with the StatusDetail of moves with StalledDueToSource_EndpointCapacityExceeded?
A: This isn't actually a problem. This is a status that you may see on moves when they are simply waiting for their turn to run (a migration endpoint concurrency slot). You can think of it as "queued".
Q: Is there a tenant-level MaxConcurrentIncrementalSyncs value too?
A: No, only MaxConcurrentMigrations. MaxConcurrentIncrementalSyncs is limited by the cmdlet to be less than or equal to the MaxConcurrentMigrations value on that same endpoint.
Q: When attempting to "complete" moves, they stick in the state StalledDueToSource_EndpointCapacityExceeded more than when doing normal migrations. Any idea why?
A: Your MaxConcurrentIncrementalSyncs value is probably set too low on the endpoint. Remember, completing (or finalizing) moves uses a slot from MaxConcurrentIncrementalSyncs (not from MaxConcurrentMigrations).
I hope this helps provide some understanding around migration concurrency as it seems to be a very confusing topic for many customers.