Oracle problems

Oracle Data Guard Standby Lag and Gap: Transport Lag or Apply Lag, the Queries That Tell Them Apart, and the Fix

3 November 2026

A standby that falls behind is three different problems wearing one symptom: redo that does not arrive (transport lag), redo that arrives and is not applied (apply lag), and archived logs that are missing altogether (a gap). Each has a different cause and a different fix, and the standby itself says which one it is in two queries. The third query, on the primary, says what the primary thinks of each destination.

What the numbers mean

Oracle keeps the two lags in V$DATAGUARD_STATS on the standby, and returns no rows for it on a primary. transport lag is how far redo transport lags redo generation on the primary; apply lag is how far the data on the standby lags the primary. A healthy standby with real-time apply shows a few seconds of each. Transport lag with little apply lag of its own means the standby applies everything it gets, and the network or the primary's redo shipping is the limit. Apply lag far above transport lag means the redo is there and recovery cannot keep up, or is not running. A gap is different again: whole archived logs are missing on the standby, recovery stops at the first missing sequence, and both lags grow until it is filled.

Which one it is, on the standby

SELECT name, value, unit, datum_time, time_computed
FROM   v$dataguard_stats
WHERE  name IN ('transport lag', 'apply lag',
                'apply finish time');

The value is an interval, days then hours, minutes and seconds: +00 00:00:03 is three seconds. Oracle's own guidance: if time_computed is more than about 30 seconds after datum_time, the standby is not hearing from the primary, and the lag shown is stale. Then the gap view and the recovery processes:

SELECT thread#, low_sequence#, high_sequence#
FROM   v$archive_gap;

SELECT name, role, action, thread#, sequence#, block#
FROM   v$dataguard_process
WHERE  role IN ('managed recovery', 'recovery logmerger',
                'RFS async', 'RFS sync', 'RFS archive',
                'RFS gap')
ORDER  BY role;

A row in V$ARCHIVE_GAP is the gap: the sequences between low and high are missing on the standby and block recovery. In V$DATAGUARD_PROCESS (12.2 and later; Oracle recommends it over V$MANAGED_STANDBY), no recovery row at all means redo apply is not running; a recovery row with the action WAIT_FOR_GAP is apply waiting for a missing sequence, WAIT_FOR_LOG is apply caught up and idle, and APPLYING_LOG at a sequence far below the RFS rows is apply that cannot keep up. How fast it can go is in V$RECOVERY_PROGRESS:

SELECT item, units, sofar, timestamp
FROM   v$recovery_progress
WHERE  item IN ('Active Apply Rate',
                'Average Apply Rate',
                'Last Applied Redo',
                'Standby Apply Lag');

What the primary thinks, per destination

SELECT dest_id, db_unique_name, status, database_mode,
       recovery_mode, gap_status, synchronization_status,
       archived_seq#, applied_seq#, error
FROM   v$archive_dest_status
WHERE  status <> 'INACTIVE';

Read it left to right. status ERROR or DISABLED, with text in error, is transport failing: a password file that no longer matches, a listener that is down, a destination that was deferred. gap_status RESOLVABLE GAP is a gap the primary fills on its own, UNRESOLVABLE GAP needs the logs by hand, and LOG SWITCH GAP is the moment after a switch. recovery_mode IDLE is the apply-lag case: redo arrives and nothing applies it; MANAGED REAL TIME APPLY is what a healthy standby shows. archived_seq# minus applied_seq# is the backlog, in logs.

The fix, in order

  1. Apply not running: start it. On the standby, ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT; starts redo apply in the background; with the Broker, EDIT DATABASE stby SET STATE = 'APPLY-ON';. Apply stops more often than it fails: a patch window, a snapshot standby conversion, a restart that nobody followed with the command.
  2. Transport failing: fix the error text, not the lag. The error column names it. ORA-01017 or ORA-16191 is the password file: copy the primary's password file to the standby again after any SYS password change. A TNS error is the listener or the service name in LOG_ARCHIVE_DEST_n. A destination in DEFERRED is one LOG_ARCHIVE_DEST_STATE_n = ENABLE away.
  3. A gap: let it resolve, or fill it by hand. A redo gap occurs whenever redo transmission is interrupted; when it resumes, transport detects the gap and sends the missing redo, as long as those archived logs still exist on the primary, which is why the FRA and the archivelog backups matter here. If they are gone, restore them from backup on the primary, copy them over, and on the standby run ALTER DATABASE REGISTER LOGFILE '/arch/thread1_seq.arc'; for each, until V$ARCHIVE_GAP returns no rows.
  4. Apply too slow: standby redo logs and I/O. Real-time apply reads the standby redo logs as they are written; without them, apply waits for a complete archived log. The standby needs at least one more standby redo log group per thread than the primary has online groups, each at least as large as the primary's largest online log. After that, apply is bound by the standby's disks and CPU: a standby on slower storage than the primary lags at every batch, and the Active Apply Rate above says what it can do.
  5. Transport too slow: the link and the mode. ASYNC transport over a link smaller than the primary's redo rate at peak lags exactly at the peak and catches up afterwards; Redo Generated Per Sec on the primary against the link is the arithmetic. SYNC over a slow link slows the primary's commits instead, and shows up as log file sync.

See it coming

A standby does not fall an hour behind in a second. Radar Engine, the detection layer of Scops, watches both sides, the primary's destinations and the standby's lags, without an agent on either server:

DetectorClockWhen it fires
Standby transport lag, Standby apply lagOperations, against the usual lag of this standby3 minutes above the usual: Watch. 10 minutes: Warning. One hour: Critical.
Standby falling behindOperations, archived logs not on the standbyGrowing: Watch. 10 behind and steady: Warning. 20 and growing: Critical.
Apply stopped on standbyOperationsRedo apply idle for hours: Warning. With a growing gap, and not planned: Critical.
Data Guard destination failingOperations, from the primaryA destination in error: Warning. Four readings in a row, or a fatal error: Critical.
NOLOGGING changesOperations, 7 daysNOLOGGING changes on a primary that has a standby: Warning.

The finding names the standby, the destination and the number of logs behind, with the lag against its usual, so a stopped apply is seen in its first hour, not at the next failover test.

Questions

Why does V$DATAGUARD_STATS return nothing on my primary?
By design: Oracle returns no rows for it on a primary database. The lags are computed where the redo arrives, so query the standby, or the primary's view of each destination in V$ARCHIVE_DEST_STATUS.

Transport lag is zero but apply lag keeps growing. Why?
The redo arrives and is not applied: redo apply is stopped, waiting on a gap, or slower than the primary's redo rate. V$DATAGUARD_PROCESS shows which, and V$ARCHIVE_GAP shows the missing sequences if it is a gap.

Do I need standby redo logs?
Yes. Oracle's documentation requires them for synchronous and asynchronous redo transport, and real-time apply reads them as they are written. One more group per thread than the primary's online groups, each at least as large as its largest online log.

The gap is unresolvable. What now?
The archived logs the standby needs are no longer on the primary. Restore them from the primary's backups, copy them over, register each with ALTER DATABASE REGISTER LOGFILE, and keep the archivelog backups and the FRA large enough that the next interruption stays resolvable.

Related problems

Sources