Why Teams Add a Third Standby
The typical Data Guard configuration is straightforward: one primary, one physical standby for DR, synchronous or near-synchronous redo transport, and a Data Guard Observer managing automatic failover. It works, it's well-documented, and the failure modes are well-understood.
Then comes the request for a reporting database. The analytics team needs a read-only Oracle database that doesn't impact production. The DBA solution: add an Active Data Guard standby, opened read-only, pointed at the production primary. This is exactly what Active Data Guard is designed for. But adding a third member to the configuration introduces complexity that doesn't exist in a two-member setup.
Sometimes the third standby is a second DR site — a distant standby in an alternate region or data center for geographic redundancy. Same technical result: a three-member Data Guard configuration with behaviors that differ from two-member in non-obvious ways.
Redo Transport to Multiple Standbys: The Sequencing Problem
In a two-member configuration, the primary ships redo to one standby. The transport mode (SYNC, ASYNC, FASTSYNC) determines how the primary waits for acknowledgment. The calculation is simple: one acknowledgment required, one destination to wait for.
With three members, the primary ships redo to two destinations simultaneously. The interaction between those destinations and the primary's commit protocol is where complexity emerges.
Consider a configuration where the primary ships SYNC to the local DR standby and ASYNC to a remote reporting standby:
-- Primary log_archive_dest configuration (in spfile or init.ora)
LOG_ARCHIVE_DEST_2='SERVICE=dr_standby SYNC AFFIRM
MAX_FAILURE=1 REOPEN=15
DB_UNIQUE_NAME=dr_standby
VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE)'
LOG_ARCHIVE_DEST_3='SERVICE=report_standby ASYNC NOAFFIRM
MAX_FAILURE=3 REOPEN=30
DB_UNIQUE_NAME=report_standby
VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE)'
-- Check current redo transport status
SELECT
DEST_ID,
DEST_NAME,
STATUS,
TARGET,
ARCHIVER,
SCHEDULE,
DESTINATION,
APPLIED_SCN,
ERROR
FROM V$ARCHIVE_DEST
WHERE STATUS != 'INACTIVE'
ORDER BY DEST_ID;
The problem emerges during the reporting standby's maintenance windows or outages. If report_standby becomes unreachable and MAX_FAILURE=3 is exhausted, the destination enters ERROR state. Redo transport to that destination stops. So far, manageable — ASYNC transport with NOAFFIRM shouldn't block the primary.
The non-obvious issue: if you later set ALTERNATE destinations or have complex dependency chains between destinations, an error on one ASYNC destination can trigger cascading destination state changes that affect the SYNC destination's behavior. Always validate destination states after any standby maintenance event.
FAL Server Configuration With Multiple Standbys
FAL (Fetch Archive Log) is the mechanism by which a standby requests missing archived redo logs from the primary or from another standby. Each standby has a FAL_SERVER parameter pointing to where it should request gap fills.
In a two-member config, the standby's FAL_SERVER points to the primary. Simple. In a three-member config, you have choices — and wrong choices create silent gaps.
-- On the DR standby (dr_standby)
-- FAL_SERVER should point to primary first, then report_standby as fallback
-- This is set in the standby's spfile
ALTER SYSTEM SET FAL_SERVER='primary_db,report_standby' SCOPE=BOTH;
ALTER SYSTEM SET FAL_CLIENT='dr_standby' SCOPE=BOTH;
-- On the report standby (report_standby)
-- FAL_SERVER points to primary first, dr_standby as fallback
ALTER SYSTEM SET FAL_SERVER='primary_db,dr_standby' SCOPE=BOTH;
ALTER SYSTEM SET FAL_CLIENT='report_standby' SCOPE=BOTH;
The common mistake: configuring FAL_SERVER on the report standby to point only to the DR standby (thinking it would "cascade" redo transport through the standby chain). This creates a dangerous dependency: if the DR standby is down or has its own gaps, the report standby can't fill its gaps even if the primary is available and healthy. Always list the primary first in FAL_SERVER, with other standbys as fallbacks.
More critically: if the report standby is being used as a FAL source by the DR standby, the report standby must be running and accessible whenever the DR standby needs gap fills. This creates an operational coupling between two standbys that is not obvious from looking at either configuration independently.
Data Guard Broker and the Three-Member Configuration
If you're using Data Guard Broker (strongly recommended for any production configuration), adding a third standby requires updating the broker configuration explicitly. The broker doesn't automatically discover new standbys added outside of broker management.
-- Add the new standby to the broker configuration
-- Run from DGMGRL on the primary or observer host
DGMGRL> CONNECT sys/password@primary_db
DGMGRL> SHOW CONFIGURATION;
-- Expected output shows current members
-- Configuration - my_dg_config
-- Protection Mode: MaxAvailability
-- Members:
-- primary_db - Primary database
-- dr_standby - Physical standby database
-- Add the report standby
DGMGRL> ADD DATABASE report_standby AS
CONNECT IDENTIFIER IS report_standby
MAINTAINED AS PHYSICAL;
-- Verify the standby was added
DGMGRL> SHOW DATABASE VERBOSE report_standby;
-- Enable the standby in the broker configuration
DGMGRL> ENABLE DATABASE report_standby;
-- Check overall configuration health
DGMGRL> SHOW CONFIGURATION;
The broker assigns each database a FastStartFailoverTarget property that determines which standbys are candidates for automatic failover (Fast Start Failover, FSFO). With three members, you need to specify which standby is the FSFO target — you typically don't want the reporting standby to be an automatic failover target if it's behind on redo application due to reporting workload.
-- Configure FSFO to only target the DR standby, not the report standby
DGMGRL> EDIT DATABASE primary_db SET PROPERTY
FastStartFailoverTarget='dr_standby';
-- Set the report standby to not be an automatic failover candidate
DGMGRL> EDIT DATABASE report_standby SET PROPERTY
FastStartFailoverTarget='';
-- Configure apply lag threshold for the report standby
-- This controls when broker considers the report standby "healthy"
DGMGRL> EDIT DATABASE report_standby SET PROPERTY
ApplyLagThreshold=300; -- 300 seconds apply lag before warning
Observer Placement and Quorum With Three Members
Data Guard Fast Start Failover uses an Observer process to monitor the primary and initiate automatic failover when the primary is unreachable. With a two-member configuration, the Observer's placement is straightforward: put it somewhere that can independently reach both the primary and standby without being co-located with either.
With three members, the Observer's quorum calculation becomes more complex. FSFO requires the Observer to establish that the primary is genuinely unavailable (not just a network partition between the Observer and primary). If the Observer can reach the DR standby but not the primary, and the DR standby also can't reach the primary, FSFO will trigger automatic failover.
The three-member configuration introduces a scenario where the Observer might be in a network segment that can reach the report standby but not the DR standby, while the primary can reach the DR standby but not the Observer. In this split-brain scenario, the quorum decision becomes ambiguous. The Observer's behavior depends on the configured FastStartFailoverThreshold and the specific connectivity state at failover time.
-- Check current Observer status and connectivity
DGMGRL> SHOW FAST_START FAILOVER;
-- Key properties to verify in a three-member config:
-- FastStartFailoverThreshold: seconds before FSFO triggers
-- FastStartFailoverAutoReinstate: whether old primary auto-rejoins
-- FastStartFailoverPmyShutdown: whether primary shuts itself down
-- when it loses observer connectivity (prevents split brain)
-- Enable FSFO with appropriate threshold
DGMGRL> ENABLE FAST_START FAILOVER CONDITION "Stuck Archiver";
DGMGRL> EDIT CONFIGURATION SET PROPERTY
FastStartFailoverThreshold=30;
DGMGRL> EDIT CONFIGURATION SET PROPERTY
FastStartFailoverPmyShutdown=TRUE; -- critical for split-brain prevention
FastStartFailoverPmyShutdown=TRUE is critical in any multi-member Data Guard configuration. If the primary loses connectivity to the Observer and a failover occurs, PmyShutdown=TRUE causes the original primary to shut itself down — preventing a situation where both the old primary and new primary accept writes simultaneously. This setting is OFF by default. It should be ON for any production Data Guard environment.
Redo Apply Lag on the Reporting Standby
The reporting standby (opened read-only with Active Data Guard) has a different operational profile than the DR standby. It's running user queries against the database while redo apply is simultaneously applying changes from the primary. Heavy reporting queries and redo apply compete for I/O bandwidth and buffer cache resources.
Under reporting load, the apply lag on the reporting standby will be higher than on the idle DR standby. This is expected behavior, but it has consequences:
- If you've configured the reporting standby as a FAL source for the DR standby, a lagging reporting standby may not have the archived logs the DR standby needs for gap fills
- The Data Guard Broker will generate warnings when apply lag exceeds
ApplyLagThreshold— tune this threshold to match your actual reporting workload rather than treating all lag as a problem - Long-running reporting queries on an Active Data Guard standby can block redo apply. Oracle will terminate queries that block apply beyond the
APPLY_DELAYthreshold
-- Monitor apply lag on all standbys from the primary
SELECT
DB_UNIQUE_NAME,
VALUE AS apply_lag,
UNIT,
TIME_COMPUTED
FROM V$DATAGUARD_STATS
WHERE NAME = 'apply lag'
ORDER BY DB_UNIQUE_NAME;
-- On each standby: check current apply rate
SELECT
DB_UNIQUE_NAME,
APPLY_RATE,
ESTIMATED_STARTUP_TIME
FROM V$RECOVERY_PROGRESS
WHERE TYPE = 'Active Apply Rate';
Failover Testing With Three Members
The most important thing you can do with a three-member Data Guard configuration is test failover — specifically, switchover and failback — before you need to execute it under pressure. The three-member configuration adds steps that don't exist in a two-member test:
- Switchover primary to DR standby (dr_standby becomes new primary)
- Verify report_standby automatically re-targets the new primary for redo transport
- Verify Observer recognizes the new primary
- Verify FAL_SERVER on report_standby is updated to point to new primary
- Switchback to original primary
- Verify all three members re-synchronize correctly
Step 2 and 4 are the steps that break in untested three-member configurations. After a switchover, the report standby must re-point its redo transport source to the new primary. If you're using Data Guard Broker, this happens automatically. If you're managing redo transport manually via log_archive_dest parameters, it requires manual intervention — which is why Broker management is strongly recommended for any production multi-member configuration.
Managing a complex Data Guard configuration?
We review Oracle HA architectures and identify failure scenarios before they become outages. Free assessment, no obligation.