Rolling upgrade with Transient Logical Standby is known as a MAA (Maximum Availability Architecture) technique, to minimize downtime during upgrade of Oracle database.
Approaches:
There are several methods which can be used:
- Manually from command line
- Manually using phsyru.sh script which is available since 11g (Doc ID 949322.1)
- Use DBMS_ROLLING package which is available since 12.1.0.1 (Doc ID 2086512.1)
Note: for both manual methods Data Guard Broker needs to be turned off. For DBMS_ROLLING approach you can leave it running and drive the rolling upgrade process semi-automatic.
In this post I will use DBMS_ROLLING approach with Data Broker setup.
This is the graphical representation of the whole process:

On a high level, these are the steps depicted from the above graph:
- Create the guaranteed restore point
- Build the logminer dictionary
- Convert the physical standby to a logical standby
- Upgrade the logical standby
- Start the apply again and let it recover
- Switchover the primary database to the Logical standby, at that point, you are upgraded already!
- The old primary is now a logical standby, so it needs to be flashed back
- Then converted to a physical standby.
- Due to that conversion, the redo (with the upgrade info in) is sent to the old primary, the new physical standby
- Finally, a switchover again to put the primary back in place
DBMS_ROLLING package will do the following actions:
- Prerequisite Phase
- init_plan
- build_plan
- start_plan
- upgrade (or do what you want with the Transient logical standby)
- switchover
- Restart the standby
- finish_plan
I have the following setup prepared for this demo:

The dataguard configuration is is MAXIMUM AVAILABILITY mode
[oracle@ol8-19-dg1 ~]$ dgmgrl / DGMGRL for Linux: Release 19.0.0.0.0 - Production on Mon Jun 21 07:26:57 2021 Version 19.3.0.0.0 Copyright (c) 1982, 2019, Oracle and/or its affiliates. All rights reserved. Welcome to DGMGRL, type "help" for information. Connected to "orcl_dga" Connected as SYSDG. DGMGRL> show configuration Configuration - my_dg_config Protection Mode: MaxAvailability Members: orcl_dga - Primary database orcl_dgb - Physical standby database orcl_dgc - Physical standby database Fast-Start Failover: Disabled Configuration Status: SUCCESS (status updated 52 seconds ago) DGMGRL>
This is an out of place upgrade which means I will do the upgrade on a different ORACLE_HOME (but on the same server). This is the plan for the upgrade for all 3 nodes:
NO DOWNTIME:
- On all 3 nodes: create the necessary folder for the the ORACLE_HOME which will be used for this upgrade
- On primary: clone the existing ORACLE_HOME using golden image way
- On primary: unzip the golden image into the new ORACLE_HOME
- On primary: install the golden image
- On primary: upgrade the new ORACLE_HOME
- On primary: clone the new ORACLE_HOME to be ready to ship to the standby nodes
- On primary: take a full backup of the database
- On primary: Ship from PRIMARY node to both standby nodes the new clone
- On standbys: unzip the new clone in the new ORACLE_HOME folder
- On standbys: install the golden image
- Start DBMS_ROLLING upgrade procedure:
- Prerequisite Phase
- init_plan
- build_plan
- start_plan
- upgrade (or do what you want with the Transient logical standby)
DOWNTIME
- switchover
NO DOWNTIME
- Restart the standby
- finish_plan
As you can see, there is very little downtime using this transient logical standby method.
There are 2 patches that I will deploy on the new ORACLE_HOME:
- p32545013_190000_Linux-x86-64.zip (19.11.0.0)
- p6880880_190000_Linux-x86-64.zip (latest 19.0.0.0 Opatch version)
Let’s start with the upgrade:
Step 1:
mkdir -p /u01/app/oracle/product/19.0.0/dbhome_2
Step 2, 3 ,4 :
All these steps are explained in detail in this post: How to clone ORACLE_HOME in 19c
Step 5:
cd $ORACLE_HOME mv OPatch OPatch_BCK unzip p32545013_190000_Linux-x86-64.zip -d $ORACLE_HOME cd /patch_location/ unzip p32545013_190000_Linux-x86-64.zip cd 32545013 $ORACLE_HOME/OPatch/opatch apply
Check the patch version now:
[oracle@ol8-19-dg1 scripts]$ $ORACLE_HOME/OPatch/opatch lsinventory Oracle Interim Patch Installer version 12.2.0.1.25 Copyright (c) 2021, Oracle Corporation. All rights reserved. Oracle Home : /u01/app/oracle/product/19.0.0/dbhome_2 Central Inventory : /u01/app/oraInventory from : /u01/app/oracle/product/19.0.0/dbhome_2/oraInst.loc OPatch version : 12.2.0.1.25 OUI version : 12.2.0.7.0 Log file location : /u01/app/oracle/product/19.0.0/dbhome_2/cfgtoollogs/opatch/opatch2021-06-21_08-21-48AM_1.log Lsinventory Output file location : /u01/app/oracle/product/19.0.0/dbhome_2/cfgtoollogs/opatch/lsinv/lsinventory2021-06-21_08-21-48AM.txt -------------------------------------------------------------------------------- Local Machine Information:: Hostname: ol8-19-dg1.local ARU platform id: 226 ARU platform description:: Linux x86-64 Installed Top-level Products (1): Oracle Database 19c 19.0.0.0.0 There are 1 products installed in this Oracle Home. Interim patches (2) : Patch 32545013 : applied on Thu Jun 17 09:12:39 UTC 2021 Unique Patch ID: 24175065 Patch description: "Database Release Update : 19.11.0.0.210420 (32545013)"
Step 6
All these steps are explained in detail in this post: How to clone ORACLE_HOME in 19c
Step 7
I’m using the following basic script:
#!/bin/bash INST=ORCL export ORACLE_SID=orcl export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1 LEVEL=0 TODAY=`date "+%Y%m%d"` RMAN_LOCATION=/u01/oracle_db_backups RMAN_LOCATION=$RMAN_LOCATION"/"$INST_$TODAY mkdir -p $RMAN_LOCATION mkdir -p $RMAN_LOCATION/LOG export PATH=$ORACLE_HOME/bin:$PATH fullBackup () { rman log=$RMAN_LOCATION/LOG/RMANFULLincr.log << EOF connect target / set echo on; run { ALLOCATE CHANNEL ch01 DEVICE TYPE DISK; ALLOCATE CHANNEL ch02 DEVICE TYPE DISK; configure controlfile autobackup format for device type DISK to '${RMAN_LOCATION}/${INST}_CONTROLFILE_%F'; CONFIGURE SNAPSHOT CONTROLFILE NAME TO '/u01/app/oracle/product/19.0.0/dbhome_1/dbs/snapcf_orcl.f'; CONFIGURE DEFAULT DEVICE TYPE TO DISK; CONFIGURE DEVICE TYPE DISK BACKUP TYPE TO COMPRESSED BACKUPSET; CONFIGURE CONTROLFILE AUTOBACKUP ON; #CONFIGURE ENCRYPTION FOR DATABASE OFF; backup incremental level 0 check logical database filesperset 1 tag 'orcl_lvl_0_${TODAY}' format '${RMAN_LOCATION}/${INST}_${TODAY}_df_%s_%p_%t'; release channel ch01; release channel ch02; } run { ALLOCATE CHANNEL ch01 DEVICE TYPE DISK; ALLOCATE CHANNEL ch02 DEVICE TYPE DISK; crosscheck archivelog all; backup check logical filesperset 1 tag 'ARCH_${INST}_${TODAY}' format '${RMAN_LOCATION}/${INST}_${TODAY}_al_%s_%p_%t' archivelog all not backed up 1 times; backup check logical filesperset 1 tag 'ARCH_${INST}_${TODAY}' format '${RMAN_LOCATION}/${INST}_${TODAY}_al_%s_%p_%t' archivelog all not backed up 1 times; release channel ch01; release channel ch02; } sql 'alter system archive log current'; exit EOF } # Main fullBackup
Step 8
scp /u01/19.3_goldimage/db_home_2021-06-17_10-01-12AM.zip ol8-19-dg2.local:/u01/19.3_goldimage/ scp /u01/19.3_goldimage/db_home_2021-06-17_10-01-12AM.zip ol8-19-dg3.local:/u01/19.3_goldimage/
Step 9
cd /u01/app/oracle/product/19.0.0/dbhome_2 unzip -oq /u01/19.3_goldimage/db_home_2021-06-17_10-01-12AM.zip
Step 10
${ORACLE_HOME}/runInstaller -ignorePrereq -waitforcompletion -silent \ -responseFile ${ORACLE_HOME}/install/response/db_install.rsp \ oracle.install.option=INSTALL_DB_SWONLY \ ORACLE_HOSTNAME=${ORACLE_HOSTNAME} \ UNIX_GROUP_NAME=oinstall \ INVENTORY_LOCATION=${ORA_INVENTORY} \ SELECTED_LANGUAGES=${ORA_LANGUAGES} \ ORACLE_HOME=${ORACLE_HOME} \ ORACLE_BASE=${ORACLE_BASE} \ oracle.install.db.InstallEdition=EE \ oracle.install.db.OSDBA_GROUP=dba \ oracle.install.db.OSBACKUPDBA_GROUP=dba \ oracle.install.db.OSDGDBA_GROUP=dba \ oracle.install.db.OSKMDBA_GROUP=dba \ oracle.install.db.OSRACDBA_GROUP=dba \ SECURITY_UPDATES_VIA_MYORACLESUPPORT=false \ DECLINE_SECURITY_UPDATES=true
Step 11 Start of the rolling upgrade procedure
Step 12
SQL> select * from DBA_ROLLING_UNSUPPORTED; no rows selected SQL>
Step 13
SQL> exec dbms_rolling.init_plan('orcl_dgb'); PL/SQL procedure successfully completed. SQL> set lin 400 col name format a50 col scope format a20 col curval format a10 select scope, name, curval from dba_rolling_parameters order by scope, name;
Step 14
SQL> exec dbms_rolling.build_plan; PL/SQL procedure successfully completed. SQL> SQL> set lin 300 col instid format 999 col target format a20 col phase format a30 col description format a80 SELECT instid, target, phase, description FROM DBA_ROLLING_PLAN;
Step 15
SQL> set timing on exec dbms_rolling.start_planSQL> PL/SQL procedure successfully completed. Elapsed: 00:05:58.75 SQL>
This is how the dgbroker is reporting the status:
DGMGRL> show configuration; Configuration - my_dg_config Protection Mode: MaxAvailability Members: orcl_dga - Primary database orcl_dgb - Transient logical standby database orcl_dgc - Physical standby database Fast-Start Failover: Disabled Configuration Status: ROLLING DATABASE MAINTENANCE IN PROGRESS DGMGRL>
Step 16
Let’s start with the upgrade. Since the upgrade of the binaries were done in the beginning of our process, we only have to proceed with datapatch.
First we stop the listener and the logical standby database:
[oracle@ol8-19-dg2 scripts]$ lsnrctl stop LSNRCTL for Linux: Version 19.0.0.0.0 - Production on 21-JUN-2021 11:58:39 Copyright (c) 1991, 2019, Oracle. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=ol8-19-dg2.local)(PORT=1521))) The command completed successfully [oracle@ol8-19-dg2 scripts]$ sqlplus / as sysdba SQL*Plus: Release 19.0.0.0.0 - Production on Mon Jun 21 11:58:44 2021 Version 19.3.0.0.0 Copyright (c) 1982, 2019, Oracle. All rights reserved. Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Version 19.3.0.0.0 SQL> shutdown immediate Database closed. Database dismounted. ORACLE instance shut down. SQL> exit
We source the profile for the new oracle home and start back the listener and oracle instance
[oracle@ol8-19-dg2 scripts]$ source setEnv_19-11.sh [oracle@ol8-19-dg2 scripts]$ [oracle@ol8-19-dg2 scripts]$ [oracle@ol8-19-dg2 scripts]$ lsnrctl start LSNRCTL for Linux: Version 19.0.0.0.0 - Production on 21-JUN-2021 12:02:00 Copyright (c) 1991, 2021, Oracle. All rights reserved. Starting /u01/app/oracle/product/19.0.0/dbhome_2/bin/tnslsnr: please wait... TNSLSNR for Linux: Version 19.0.0.0.0 - Production System parameter file is /u01/app/oracle/product/19.0.0/dbhome_2/network/admin/listener.ora Log messages written to /u01/app/oracle/diag/tnslsnr/ol8-19-dg2/listener/alert/log.xml Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=ol8-19-dg2.local)(PORT=1521))) Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521))) Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=ol8-19-dg2.local)(PORT=1521))) STATUS of the LISTENER ------------------------ Alias LISTENER Version TNSLSNR for Linux: Version 19.0.0.0.0 - Production Start Date 21-JUN-2021 12:02:00 Uptime 0 days 0 hr. 0 min. 0 sec Trace Level off Security ON: Local OS Authentication SNMP OFF Listener Parameter File /u01/app/oracle/product/19.0.0/dbhome_2/network/admin/listener.ora Listener Log File /u01/app/oracle/diag/tnslsnr/ol8-19-dg2/listener/alert/log.xml Listening Endpoints Summary... (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=ol8-19-dg2.local)(PORT=1521))) (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521))) Services Summary... Service "orcl_dgb" has 1 instance(s). Instance "orcl", status UNKNOWN, has 1 handler(s) for this service... Service "orcl_dgb.world" has 1 instance(s). Instance "orcl", status UNKNOWN, has 1 handler(s) for this service... Service "orcl_dgb_DGMGRL.world" has 1 instance(s). Instance "orcl", status UNKNOWN, has 1 handler(s) for this service... The command completed successfully [oracle@ol8-19-dg2 scripts]$ sqlplus / as sysdba SQL*Plus: Release 19.0.0.0.0 - Production on Mon Jun 21 12:02:05 2021 Version 19.11.0.0.0 Copyright (c) 1982, 2020, Oracle. All rights reserved. Connected to an idle instance. SQL> startup ORACLE instance started. Total System Global Area 1610609192 bytes Fixed Size 8897064 bytes Variable Size 385875968 bytes Database Buffers 1207959552 bytes Redo Buffers 7876608 bytes Database mounted. Database opened. SQL> select database_role,open_mode from v$database; DATABASE_ROLE OPEN_MODE ---------------- -------------------- LOGICAL STANDBY READ WRITE SQL>
We need to be sure that the logical standby is in SYNC with PRIMARY
SQL> alter database start logical standby apply immediate; Database altered. SQL>SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS'; Session altered. COL LATEST_TIME FOR A30 set lin 400 COL APPLIED_TIME FOR A30 COL MINING_TIME FOR A30 SELECT LATEST_TIME, APPLIED_TIME, MINING_TIME, (LATEST_TIME-APPLIED_TIME)*24*3600 GAP_IN_SECOND FROM V$LOGSTDBY_PROGRESS; LATEST_TIME APPLIED_TIME MINING_TIME GAP_IN_SECOND ------------------------------ ------------------------------ ------------------------------ ------------- 21-JUN-2021 12:10:01 21-JUN-2021 12:10:00 21-JUN-2021 12:10:00 1 SQL>
It’s in SYNC. Let’s continue with datapatch
[oracle@ol8-19-dg2 scripts]$ cd $ORACLE_HOME/OPatch [oracle@ol8-19-dg2 OPatch]$ ./datapatch -verbose
As the LOGICAL STANDBY has been patched, we need to make this LOGICAL STANDBY to become PRIMARY in order to upgrade this instance as well
SQL> exec dbms_rolling.switchover; PL/SQL procedure successfully completed. SQL> SQL>
Now my previous PRIMARY became LOGICAL STANDBY and it’s open READ/WRITE mode. We need to mount it and finish the plan.
[oracle@ol8-19-dg1 ~]$ sqlplus / as sysdba SQL*Plus: Release 19.0.0.0.0 - Production on Tue Jun 22 10:22:06 2021 Version 19.3.0.0.0 Copyright (c) 1982, 2019, Oracle. All rights reserved. Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Version 19.3.0.0.0 SQL> startup mount force; ORACLE instance started. Total System Global Area 1610609200 bytes Fixed Size 8897072 bytes Variable Size 385875968 bytes Database Buffers 1207959552 bytes Redo Buffers 7876608 bytes Database mounted. SQL>
Now execute finish plan from the NEW primary database:
exec dbms_rolling.finish_plan;
Now we need to stop both STANDBYs and change the ORACLE_HOME from the old to the new one and mount back the standbys and that’s pretty much it:
DGMGRL> show configuration; Configuration - my_dg_config Protection Mode: MaxAvailability Members: orcl_dgb - Primary database orcl_dga - Physical standby database orcl_dgc - Physical standby database Fast-Start Failover: Disabled Configuration Status: SUCCESS (status updated 57 seconds ago) DGMGRL>