Why MSrepl_commands Keeps Growing in SQL Server, and How to Check for Replication Lag
Published 2026-08-13 · Updated 2026-08-13 · Last Verified 2026-08-13
Answer
There are two broad causes of `MSrepl_commands` continuously growing: (a) the distribution agent is failing to apply commands to the subscriber, or (b) the cleanup job is not running, or the retention period is too long. You can isolate which by checking the agent's last activity time in `MSdistribution_history` and pulling the pending command count with `sp_replmonitorsubscriptionpendingcmds`.
Applicability of This Document
| Product | SQL Server (transactional replication configuration) |
|---|---|
| Verified Versions | SQL Server 2008 and later (details depend on the distributor configuration — verify) |
| Environment | On-premises, EC2, Azure (Amazon RDS has restrictions on replication configuration — verify) |
| Required Permissions | Read permission on the distribution database. `sp_replmonitorsubscriptionpendingcmds` requires the replmonitor role or sysadmin. Changing the retention period requires sysadmin |
| Execution Impact | The read-only commands make no changes. Changing the retention period and reinitializing replication affect the configuration |
| Restart | Not required |
| Last Verified | 2026-08-13 |
Ready-to-Run Commands
- 対象
- SQL Server 2008 and later (distributor)
- 権限
- Read permission on the distribution database
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: 配布データベースへの参照権限
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
USE [distribution];
GO
SELECT
da.id AS agent_id,
da.name AS agent_name,
da.publisher_db,
da.publication,
da.subscriber_db,
MAX(dh.time) AS last_history_time,
MAX(dh.delivered_commands) AS last_delivered_commands,
MAX(dh.delivery_latency) AS last_delivery_latency_ms
FROM dbo.MSdistribution_agents AS da
LEFT JOIN dbo.MSdistribution_history AS dh
ON da.id = dh.agent_id
GROUP BY da.id, da.name, da.publisher_db, da.publication, da.subscriber_db
ORDER BY last_history_time ASC;An agent whose `last_history_time` is far from the current time is either not running, or running but failing to write history. The agent at the top (oldest) is your investigation target.
- 対象
- SQL Server 2008 and later (distributor)
- 権限
- Read permission on the distribution database
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: 配布データベースへの参照権限
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
USE [distribution];
GO
SELECT TOP (50)
dh.agent_id,
da.name AS agent_name,
dh.runstatus, -- 1:開始 2:成功 3:実行中 4:アイドル 5:再試行 6:失敗
dh.start_time,
dh.time,
dh.duration,
dh.delivered_transactions,
dh.delivered_commands,
dh.delivery_rate,
dh.delivery_latency,
dh.comments
FROM dbo.MSdistribution_history AS dh
INNER JOIN dbo.MSdistribution_agents AS da
ON dh.agent_id = da.id
ORDER BY dh.time DESC;If `runstatus = 6` (failed) or `runstatus = 5` (retrying) persists, `comments` will contain the error detail. If there is a command that cannot be applied, distribution stalls there and `MSrepl_commands` stops shrinking.
- 対象
- SQL Server 2008 and later (distributor)
- 権限
- replmonitor database role or sysadmin
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: replmonitor データベースロール または sysadmin
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
EXEC distribution.dbo.sp_replmonitorsubscriptionpendingcmds
@publisher = N'LEGACY-SQL01',
@publisher_db = N'SampleDB',
@publication = N'SamplePublication',
@subscriber = N'LEGACY-SQL01',
@subscriber_db = N'SampleDB',
@subscription_type = 0; -- 0: プッシュ / 1: プルThis returns the pending command count (pendingcmdcount) and an estimated processing time. If this value keeps growing, the cause is that application to the subscriber is falling behind (case (a)). If this value is small while `MSrepl_commands` is large, suspect the cleanup side instead (case (b)).
- 対象
- SQL Server 2008 and later (distributor)
- 権限
- Read permission on the distribution database
- 変更作業
- None (read-only, but involves a full scan)
- Production実行
- Safe to run, but can take a long time on a large distribution database
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: 配布データベースへの参照権限
-- 変更作業: なし(参照のみ。ただし全件スキャンを伴う)
-- Production 実行: 可能。ただし件数が多いと実行時間が長くなるため負荷の低い時間帯を選ぶこと
USE [distribution];
GO
-- トランザクション側(entry_time で保持範囲が分かる)
SELECT
t.publisher_database_id,
COUNT(*) AS transaction_count,
MIN(t.entry_time) AS oldest_entry_time,
MAX(t.entry_time) AS newest_entry_time
FROM dbo.MSrepl_transactions AS t
GROUP BY t.publisher_database_id;
-- コマンド側
SELECT
c.publisher_database_id,
COUNT(*) AS command_count
FROM dbo.MSrepl_commands AS c
GROUP BY c.publisher_database_id;`MSrepl_commands` can have an extremely large number of rows, so `COUNT(*)` becomes a full scan. It is read-only but consumes execution time and I/O, so pick a low-load time window. If `oldest_entry_time` is clearly older than the retention period (default 72 hours), cleanup is not running.
- 対象
- SQL Server 2008 and later (distributor)
- 権限
- Read permission on msdb
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: msdb への参照権限
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
SELECT TOP (30)
j.name AS job_name,
j.enabled,
h.run_date,
h.run_time,
h.run_duration,
h.run_status, -- 0:失敗 1:成功 2:再試行 3:取消 4:実行中
h.message
FROM msdb.dbo.sysjobs AS j
LEFT JOIN msdb.dbo.sysjobhistory AS h
ON j.job_id = h.job_id
AND h.step_id = 0
WHERE j.name LIKE 'Distribution clean up%'
ORDER BY h.run_date DESC, h.run_time DESC;The default job name is `Distribution clean up: distribution`. If `enabled = 0` or `run_status = 0` (failure) persists, distributed commands are not being deleted and `MSrepl_commands` keeps growing.
- 対象
- SQL Server 2008 and later (distributor)
- 権限
- sysadmin or db_owner on the distribution database
- 変更作業
- None (read-only)
- Production実行
- Safe to run
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: sysadmin または配布データベースの db_owner
-- 変更作業: なし(参照のみ)
-- Production 実行: 可能
EXEC sp_helpdistributiondb @database = N'distribution';This returns `min_distretention` (minimum retention), `max_distretention` (maximum retention, default 72 hours), and `history_retention` (history retention, default 48 hours). The longer the retention period is set, the longer distributed commands stick around.
- 対象
- SQL Server 2008 and later (distributor)
- 権限
- sysadmin or db_owner on the distribution database
- 変更作業
- None (read-only, but can run for a long time on a large distribution DB)
- Production実行
- Not recommended — narrow the range and limit it to a low-load window
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: sysadmin または配布データベースの db_owner
-- 変更作業: なし(参照のみ。ただし高コスト)
-- Production 実行: 推奨しない。xact_seqno の範囲を必ず絞ること
EXEC distribution.dbo.sp_browsereplcmds
@xact_seqno_start = '0x00000000000000000000',
@xact_seqno_end = '0x00000000000000000000',
@publisher_database_id = 1;`sp_browsereplcmds` decodes commands into a readable form, but running it without narrowing the range scans the entire distribution database. On environments with many rows, this runs for a long time and affects distributor-wide performance, so narrow it to just around the `xact_seqno` you identified from `MSdistribution_history`. The seqno values above are illustrative placeholders for the syntax.
- 対象
- SQL Server 2008 and later (distributor)
- 権限
- sysadmin
- 変更作業
- Yes (changes the distribution database's retention policy)
- Production実行
- Can be run, but shortening it risks deleting pending commands
-- 対象: SQL Server 2008 以降(ディストリビューター)
-- 権限: sysadmin
-- 変更作業: あり(配布データベースの保持ポリシー変更)
-- Production 実行: 可能。ただし短縮は未配布コマンド削除のリスクを伴う
-- 下の値は例示。現在の配布遅延を踏まえて決めること
EXEC sp_changedistributiondb
@database = N'distribution',
@property = N'max_distretention',
@value = 72;
EXEC sp_changedistributiondb
@database = N'distribution',
@property = N'history_retention',
@value = 48;Shortening the retention period increases the amount cleanup can remove, but if a command that a subscriber has not yet received gets deleted, that subscription will require reinitialization. Keep the value comfortably longer than the current maximum distribution latency. The numbers here are illustrative, not recommendations.
- 対象
- SQL Server 2008 and later (run on the publication database on the publisher side)
- 権限
- sysadmin, or db_owner on the publication database
- 変更作業
- Yes (discards pending commands and requests a resync from a snapshot)
- Production実行
- Not allowed. Requires an impact assessment equivalent to a business outage and a finalized execution plan first
-- 対象: SQL Server 2008 以降(パブリッシャー側のパブリケーションDBで実行)
-- 権限: sysadmin または パブリケーションDBの db_owner
-- 変更作業: あり(滞留コマンドを破棄し、スナップショットからの再同期を要求)
-- Production 実行: 不可。影響評価と実施計画を確定させ、承認を得てから実施すること
USE [SampleDB];
GO
EXEC sp_reinitsubscription
@publication = N'SamplePublication',
@subscriber = N'LEGACY-SQL01',
@destination_db = N'SampleDB';Requesting reinitialization leaves the target subscriber's data inconsistent until the next snapshot is applied. Generating and applying a snapshot takes time and I/O proportional to the data volume. First consider whether the underlying cause of the backlog (an application error, stalled cleanup) can be resolved instead, and treat this as the last resort if it cannot.
How to Read the Results
| Column | Meaning | What to Check |
|---|---|---|
| last_history_time | Time the distribution agent last wrote to history | If far from the current time, the agent is not running |
| runstatus | Agent run state (2: succeeded, 5: retrying, 6: failed, etc.) | If 5 or 6 persists, check the error detail in `comments` |
| delivery_latency | Distribution latency | A continuous increase means application on the subscriber side cannot keep up |
| comments | Agent's message | Contains the body of an application error — often the direct cause of a stall |
| pendingcmdcount | Pending command count (`sp_replmonitorsubscriptionpendingcmds`) | A large increase points to the distribution side; small while `MSrepl_commands` is large points to the cleanup side |
| oldest_entry_time | Oldest transaction time remaining in the distribution database | If it greatly exceeds the retention period, cleanup is not running |
| command_count | Row count of `MSrepl_commands` | Take this over time and watch for a continuing increase — a single reading cannot be judged |
| run_status (cleanup job) | Job execution result | If 0 (failure) persists, check `message` for the cause |
| max_distretention | Maximum retention time for the distribution database (default 72 hours) | Whether it is set too long — but don't set it shorter than the distribution latency |
When to Use This
- The distribution database keeps growing in size and is squeezing storage
- Data on the subscriber side has not been updated since a certain point in time
- The transaction log on the publisher side is not being released, and `log_reuse_wait_desc` stays at REPLICATION
- Distribution latency in Replication Monitor keeps increasing
- Snapshot application never finishes, or fails partway through
Possible Causes (Most Likely First)
01
The distribution agent cannot apply commands
This is the most common cause. If application fails due to a constraint violation, a missing target row, insufficient permissions, or a dropped connection on the subscriber side, distribution stalls there and subsequent commands pile up. The error is recorded in the `comments` column of `MSdistribution_history`.
02
The cleanup job is not running
If the `Distribution clean up: distribution` job is disabled, or keeps failing, already-distributed commands are not deleted and remain. The table keeps growing even if the agent itself is healthy.
03
The retention period is set too long
A large `max_distretention` keeps distributed commands around for a long time. Sometimes this is intentionally set long as a safety margin, so judge whether the setting is appropriate together with operational requirements.
04
The subscriber has been down for a long time
If the subscriber-side server is down or the network is disconnected, the corresponding commands back up in the distribution database. Since they are all delivered at once upon recovery, you need to account for the capacity needed during that window.
05
A large update happened all at once
A bulk delete or bulk update generates one command per row. Updating millions of rows in a single operation grows `MSrepl_commands` proportionally. Splitting into batches smooths this out.
06
The same data is being distributed through multiple publications
If the same table is included in multiple publications, commands are generated per publication. Check for configuration duplication (this depends on the configuration, so it is a hypothesis that needs verifying on the target environment).
Diagnostic Steps
- 1
Check the distribution agent's last activity time
Read-onlyJoin `MSdistribution_agents` with `MSdistribution_history` to identify agents with an old `last_history_time`.
- 2
Check recent run results and errors
Read-onlyUse `runstatus` and `comments` to see whether it is stalled on an application error.
- 3
Get the pending command count
Read-onlyUse `sp_replmonitorsubscriptionpendingcmds` to isolate whether the cause is on the distribution side or the cleanup side.
- 4
Check the cleanup job's history
Read-onlyCheck whether the job is enabled and has succeeded recently, in `msdb`.
- 5
Cross-check the retention period against the oldest data timestamp
Read-onlyCompare `max_distretention` from `sp_helpdistributiondb` against `oldest_entry_time` in `MSrepl_transactions`.
- 6
Get the count trend over time
LowA single `COUNT(*)` cannot show a growth trend. Sample it multiple times with a gap to confirm whether it keeps growing.
Solutions
Low-risk action that can be done immediately
Resolve the distribution agent's error
MediumResolve whatever error appears in `comments` (constraint violation, missing row, permissions, connection) and resume the agent. Once the backlog clears, the command count starts dropping.
Enable and re-run the cleanup job
MediumIf the job is disabled or failing, check the cause and enable it. If the backlog is large, the first cleanup pass can take a while.
Free up space in the distribution database
MediumIf writes are failing due to exhausted capacity, free up space first, then move on to addressing the root cause.
Change requiring advance review
Align the retention period with actual operations
MediumAdjust `max_distretention` and `history_retention` to a value comfortably longer than the actual maximum distribution latency, without being longer than necessary. Shortening it too much deletes pending commands.
Split bulk updates into batches
LowRunning large updates in batches reduces the peak in command generation.
Add distribution latency and backlog size to your monitoring
Read-onlyPeriodically pull `pendingcmdcount` and the distribution agent's `last_history_time`, and alert on a threshold crossing. This catches the problem before it manifests as exhausted capacity.
Revisit the distribution database's placement and sizing
MediumReview the distribution database's file placement, initial size, and autogrowth settings so a backlog does not immediately exhaust capacity.
Work requiring expert review
Reinitialize the subscription (reapply a snapshot)
Expert Review RequiredThis discards the backlog and resyncs from scratch. During reinitialization, the subscriber's data is temporarily inconsistent, and generating and applying the snapshot adds significant load. This assumes an impact assessment and a finalized execution plan.
Revisit the replication configuration itself
Expert Review RequiredSplitting publications, narrowing articles, or changing the replication method all affect the overall configuration. This assumes a design review.
!Warnings
- Shortening the retention period risks deleting commands a subscriber has not yet received. A subscription whose commands get deleted this way will require reinitialization. Keep the value comfortably longer than the current maximum distribution latency.
- `sp_browsereplcmds`, run without narrowing the range, scans the entire distribution database. On environments with many rows this affects distributor-wide performance, so always specify an `xact_seqno` range.
- `COUNT(*)` against `MSrepl_commands` is a full scan. It is read-only but consumes execution time and I/O.
- Reinitializing a subscription involves regenerating and reapplying a snapshot, during which the subscriber's data is inconsistent. This requires confirming business impact and obtaining approval first.
- `log_reuse_wait_desc = REPLICATION` can occur from either transactional replication or CDC. Do not conclude the cause from just one of them.
- Amazon RDS for SQL Server has restrictions on replication configuration. Check the target environment for whether a distributor can be placed and which features are available.
Differences by Version and Environment
If This Doesn't Resolve It
The Log Reader Agent on the publisher side
If commands are not arriving in the distribution database at all, the cause lies with the Log Reader, not the distribution side. Check its running status.
Blocking on the subscriber side
If application is slow, check whether lock waits are occurring on the subscriber.
Index fragmentation in the distribution database
After a prolonged backlog, the index state on the distribution database side also becomes worth checking.
Stability of the network path
If retries keep repeating, the cause may be the network rather than the database.
Basis and Limitations of This Document
Explanation based on official product documentation
A general procedure based on the public specifications of the SQL Server transactional replication distribution database schema (`MSrepl_commands`, `MSrepl_transactions`, `MSdistribution_agents`, `MSdistribution_history`) and `sp_replmonitorsubscriptionpendingcmds`, `sp_browsereplcmds`, `sp_helpdistributiondb`, and `sp_changedistributiondb`. Specific retention-period values depend on the environment and are illustrative only. Replication restrictions on Amazon RDS assume verification on the target environment.
FAQ
Why does MSrepl_commands keep growing?
There are two broad causes: the distribution agent failing to apply commands to the subscriber, or the distribution cleanup job not running so already-distributed commands are never deleted. If the pending command count from `sp_replmonitorsubscriptionpendingcmds` is large, it is the former; if it is small while the table is large, it is the latter.
Can this be run in production?
Checking the distribution agent's state, getting the pending command count, and reading job history are all read-only and can be run in production. `COUNT(*)` on `MSrepl_commands` and `sp_browsereplcmds` are read-only but expensive, so limit them to a time window and a narrow range. Changing the retention period and reinitializing are high-impact operations.
What permissions are required?
Reading the distribution database tables requires read permission on that database. `sp_replmonitorsubscriptionpendingcmds` requires the replmonitor database role or sysadmin; changing the retention period (`sp_changedistributiondb`) requires sysadmin.
Does shortening the retention period fix this?
It increases what cleanup can remove, but if pending commands also get deleted, that subscription will require reinitialization. First resolve the underlying cause of the stall, and only then set a retention period comfortably longer than the actual distribution latency.
How should I interpret the results?
A single count cannot be judged on its own. Combine three signals — whether `last_history_time` is updating, whether `pendingcmdcount` is increasing over time, and whether `oldest_entry_time` exceeds the retention period — to decide whether the problem is on the distribution side or the cleanup side.
Does this work on AWS RDS?
Amazon RDS for SQL Server has restrictions on replication configuration, and which role it can play also depends on the service specification. Verify on the target environment, including whether a distributor can be placed there.
Questions This Document Covers
- Checking Snapshot Replication lag
- I want to know why the distribution database keeps growing in size
- I want to isolate why data is not reaching a subscriber
What the Risk Labels Mean
- Read-onlyDoes not change data or settings.
- LowImpact is limited, but permissions and load should be checked.
- MediumMay affect performance, locking, or cost.
- HighMay cause an outage, data loss, or require recovery work.
- Expert Review RequiredRequires a separate review before applying to production.
GIIP's Scope of Support
A replication backlog stays hidden until the distribution database runs out of capacity, or until someone on the subscriber side notices the data looks stale. At GIIP, we periodically pull the distribution agent's last activity time and the pending command count, and notify the person in charge once the increase persists for a set duration. Operations tied directly to data consistency and business outages, like reinitialization, are excluded from automatic execution — detection, cause isolation, and scoping the impact are automated, but the decision to act remains human.
Author & Technical Review
GIIP Production Operations Team
About 30 years of experience in designing, migrating, and operating large-scale web services, SQL Server, Oracle, AWS, and Azure. Experience includes 12 sets of x12large-class AWS RDS for SQL Server environments, an Oracle environment with roughly 120,000 tables, and migrating roughly 3TB from TiDB to Aurora MySQL. AI agents and human experts currently continue to monitor and operate multiple cloud databases and about 30 web services.
How to Check Whether CDC Log Scanning Has Stopped in SQL Server
A procedure for checking whether CDC's log scan is progressing, using sys.dm_cdc_log_scan_sessions, cdc.lsn_time_mapping, and the state of the capture job.
sql-serverHow to Check Transaction Log Usage on RDS for SQL Server
A read-only procedure that checks log usage with DBCC SQLPERF(LOGSPACE) and sys.dm_db_log_space_usage, and isolates the reason it is not being released using log_reuse_wait_desc.
sql-serverSQL to Check for Long-Running Open Transactions in SQL Server
A procedure using the sys.dm_tran_active_transactions family of DMVs to identify abandoned transactions, including start time, session, and the last SQL statement executed.
monitoringWhat to Configure When Monitoring Servers and Databases 24/7
A checklist, organized by layer, of what to monitor, how to think about thresholds, the escalation structure, and why synthetic monitoring is necessary when designing 24/7 monitoring.
Related Services
Request help isolating replication lag
If you need to repeat the same checks across multiple environments, we can discuss your entire operations setup.
Request help isolating replication lag