Oracle PDBs in a Data Guard environment
Sven Illert -
Oracle introduced it’s container based database configuration with Oracle 12.1, which dates back to the year of 2013. So it’s ten years in the wild and the adoption process is still in progress as not all of my customers have migrated to the new architecture yet, even if is enforced with the upcoming 23c release. Although this architectural change has brought a variety of useful features that make the daily business for an DBA a lot easier, there are some problems in combination with Data Guard.
What works out of the box?
First let’s check what does work out of the box when using PDBs in the most easy fashion like in creating one from a PDB$SEED
. Dropping and modifying a PDB is not that of a problem because those are operations that can easily be handled by the automatic standby file management when the standby database is in mounted state. Let’s have a look at the following configuration.
SQL> -- PRIMARY
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
_________ ___________ ____________ _____________
2 PDB$SEED READ ONLY NO
SQL> show parameter standby_file_management
NAME TYPE VALUE
----------------------- ------ -----
standby_file_management string AUTO
SQL> -- STANDBY
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
_________ ___________ ____________ _____________
2 PDB$SEED READ ONLY NO
SQL> show parameter standby_file_management
NAME TYPE VALUE
----------------------- ------ -----
standby_file_management string AUTO
Now let’s create a pluggable database and add some services for it to be able to connect. Also with a service managed by Grid Infrastructure, there’s no need to save the state of a pluggable database, since it will be opened when the service starts.
SQL> create pluggable database PDB01SI01 admin user pdb_admin identified by oracle;
Pluggable database PDB01SI01 created.
[oracle@vm101 ~]$ srvctl add service -db $ORACLE_UNQNAME -pdb PDB01SI01 -service SVC_PDB01SI01 -role primary
[oracle@vm101 ~]$ srvctl start service -db $ORACLE_UNQNAME -service SVC_PDB01SI01
[oracle@vm105 ~]$ srvctl add service -db $ORACLE_UNQNAME -pdb PDB01SI01 -service SVC_PDB01SI01 -role primary
As you would expect the PDB is created on the primary and standby site.
SQL> -- PRIMARY
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
_________ ____________ _____________ _____________
2 PDB$SEED READ ONLY NO
3 PDB01SI01 READ WRITE NO
SQL> -- STANDBY
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
_________ ____________ ____________ _____________
2 PDB$SEED MOUNTED
3 PDB01SI01 MOUNTED
For the sake of completeness of the test we should perform a switchover operation to make sure that everything is working as expected.
DGMGRL> show configuration;
...
SUCCESS (status updated 42 seconds ago)
DGMGRL> switchover to si01v105;
...
Switchover succeeded, new primary is "si01v105"
SQL> -- NEW STANDBY
SQL> show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED
_________ ____________ ____________ _____________
2 PDB$SEED MOUNTED
3 PDB01SI01 MOUNTED
SQL> -- NEW PRIMARY
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
_________ ____________ _____________ _____________
2 PDB$SEED READ ONLY NO
3 PDB01SI01 READ WRITE NO
OK, so Data Guard and multi tenant is really easy, isn’t it? It seems we could end here with this post, right?
Unplug, plug and switchover with failure
Of course I wouldn’t write about this topic if there weren’t problems buried somewhere. The majority of problems can be avoided when you have Active Data Guard licensed, because some of the operations need an open database.
To demonstrate one of the possible problems with PDB management let’s try to unplug a database and plug it into another container.
SQL> alter pluggable database PDB01SI01 close immediate;
Pluggable database PDB01SI01 altered
SQL> alter pluggable database PDB01SI01 unplug into '/tmp/PDB01SI01.xml';
Pluggable database PDB01SI01 altered.
SQL> drop pluggable database PDB01SI01 keep datafiles;
Pluggable database PDB01SI01 dropped.
[oracle@vm105 ~]$ srvctl remove service -db $ORACLE_UNQNAME -service SVC_PDB01SI01
[oracle@vm101 ~]$ srvctl remove service -db $ORACLE_UNQNAME -service SVC_PDB01SI01
Plug it into another CDB with a data guard database. With copy we can recreate the PDB after a failure.
SQL> create pluggable database PDB01SI02 using '/tmp/PDB01SI01.xml' copy;
Pluggable database PDB01SI02 created.
[oracle@vm105 ~]$ srvctl add service -db $ORACLE_UNQNAME -pdb PDB01SI02 -service SVC_PDB01SI02 -role primary
[oracle@vm105 ~]$ srvctl start service -db $ORACLE_UNQNAME -service SVC_PDB01SI02
[oracle@vm101 ~]$ srvctl add service -db $ORACLE_UNQNAME -pdb PDB01SI02 -service SVC_PDB01SI02 -role primary
Check PDBs in both databases.
SQL> -- PRIMARY
SQL> show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED
_________ ____________ _____________ _____________
2 PDB$SEED READ ONLY NO
3 PDB01SI02 READ WRITE NO
SQL> -- STANDBY
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
_________ ____________ ____________ _____________
2 PDB$SEED MOUNTED
3 PDB01SI02 MOUNTED
Everything looks fine, so let’s switch, eh? Since this was a single instance configuration and it was running on the wrong node in a cluster, I relocated the DB first to the right node and wanted to check the Data Guard status. I discovered the following state.
DGMGRL> show configuration;
Configuration - si02
Protection Mode: MaxPerformance
Members:
si02v105 - Primary database
si02v100 - Physical standby database
Error: ORA-16810: multiple errors or warnings detected for the member
Fast-Start Failover: Disabled
Configuration Status:
ERROR (status updated 49 seconds ago)
The first action in this case was to check the alert log on the standby database.
2023-07-11 22:22:25.761000 +02:00
Started logmerger process
PR00 (PID:1152038): Managed Standby Recovery starting Real Time Apply
Errors in file /u01/app/oracle/diag/rdbms/si02v100/SI02/trace/SI02_dbw0_1151696.trc:
ORA-01157: cannot identify/lock data file 8 - see DBWR trace file
ORA-01110: data file 8: '+DATA'
ORA-17503: ksfdopn:2 Failed to open file +DATA
ORA-15045: ASM file name '+DATA' is not in reference form
OK, so what is datafile 8? Let’s check on the primary:
SQL> select file#, c.name, d.name PDB from v$datafile d, v$pdbs c where d.con_id=c.con_id and file#=8;
FILE# PDB NAME
________ ____________ _________________________________________________________________________________
8 PDB01SI02 +DATA/SI02V105/003BBAD8FC9C41AFE0636524100AA5EE/DATAFILE/system.340.1141942389
As we can see this file is part of the newly created PDB. At this point you should copy the datafiles from the source to the OMF destination for the standby database. We can use RMAN for that purpose on the standby location.
RMAN> connect target /
RMAN> recover standby database from service 'vm105/SI02V105';
This handles all the necessary copy, recover and rename stuff that’s needed to get the standby database running. Afterwards we have to reenable the database with the data guard broker.
DGMGRL> enable database si02v100;
Enabled.
DGMGRL> show configuration;
...
SUCCESS (status updated 19 seconds ago)
OK, now let’s verify switchover works and possibly switch back. We have seen that it might be not that easy creating a PDB from an existing location. The reason is that the standby container must execute all the steps for the creation process equivalently to the primary. That might not be possible if, for example, the creation process refers to files that don’t exist on the standby site like in our case (the XML file refers to other filenames as on the standby, because of the usage of OMF).
DGMGRL> switchover to si02v100;
DGMGRL> switchover to si02v105;
Unplug, Plug and switchover with success
The previous attempt went with room for improvement. Let’s drop the pdb and try another method.
SQL> alter pluggable database PDB01SI02 close immediate;
SQL> drop pluggable database PDB01SI02 including datafiles;
Verify this on both sides and check data guard after some time using these commands.
SQL> show pdbs
DGMGRL> show configuration;
DGMGRL> switchover to si02v100;
DGMGRL> switchover to si02v105;
What if there was a way to enable the standby database to use the source pdb files on it’s own? Let’s see what we can find in the documentation … Oh! There’s a parameter called standby_pdb_source_file_directory
. Maybe this will help us? Let’s try!
First we need to copy the data files of the source pdb to the standby machine.
$ for file in $(grep path /tmp/PDB01SI01.xml | cut -d'>' -f2 | cut -d'<' -f1); do asmcmd cp $file /u01/stage; done
copying +DATA/SI01V105/003BBAD8FC9C41AFE0636524100AA5EE/DATAFILE/system.336.1141938207 -> /u01/stage/system.336.1141938207
copying +DATA/SI01V105/003BBAD8FC9C41AFE0636524100AA5EE/DATAFILE/sysaux.337.1141938229 -> /u01/stage/sysaux.337.1141938229
copying +DATA/SI01V105/003BBAD8FC9C41AFE0636524100AA5EE/DATAFILE/undotbs1.338.1141938257 -> /u01/stage/undotbs1.338.1141938257
$ scp /u01/stage/* vm101:/u01/stage
Now let’s set the discovered parameter at the standby database and create the pluggable database again on the primary database.
SQL> -- STANDBY
SQL> alter system set standby_pdb_source_file_directory='/u01/stage';
SQL> -- PRIMARY
SQL> create pluggable database PDB01SI02 using '/tmp/PDB01SI01.xml' copy;
And now let’s have a look, what we see in the alert log of the standby database when we create the pluggable database the same way as in the previous example.
Recovery attempting to copy datafiles for pdb-PDB01SI02 from source dir-/u01/stage
2023-07-13 20:40:16.429000 +02:00
Recovery copied files for tablespace SYSTEM
Recovery successfully copied file +DATA/SI02V100/F27828A2C94754A9E0536624100AC310/DATAFILE/system.356.1142109589 from /u01/stage/system.336.1141938207
Successfully added datafile 11 to media recovery
Datafile #11: '+DATA/SI02V100/F27828A2C94754A9E0536624100AC310/DATAFILE/system.356.1142109589'
2023-07-13 20:40:43.943000 +02:00
Recovery copied files for tablespace SYSAUX
Recovery successfully copied file +DATA/SI02V100/003BBAD8FC9C41AFE0636524100AA5EE/DATAFILE/sysaux.349.1142109617 from /u01/stage/sysaux.337.1141938229
Successfully added datafile 12 to media recovery
Datafile #12: '+DATA/SI02V100/003BBAD8FC9C41AFE0636524100AA5EE/DATAFILE/sysaux.349.1142109617'
2023-07-13 20:40:57.173000 +02:00
Recovery copied files for tablespace UNDOTBS1
Recovery successfully copied file +DATA/SI02V100/003BBAD8FC9C41AFE0636524100AA5EE/DATAFILE/undotbs1.358.1142109645 from /u01/stage/undotbs1.338.1141938257
Successfully added datafile 13 to media recovery
Datafile #13: '+DATA/SI02V100/003BBAD8FC9C41AFE0636524100AA5EE/DATAFILE/undotbs1.358.1142109645'
This looks like we found a solution to the problem without recovery using RMAN. Hooray!
Unplug, Plug and switchover using PDB archive
But what about self-contained pdb archives? They become very handy if you want to copy everything in one single file.
SQL> alter pluggable database PDB01SI01 unplug into '/u01/stage/PDB01SI01.pdb';
SQL> drop pluggable database PDB01SI01 keep datafiles;
$ scp /u01/stage/PDB01SI01.pdb vm101:/u01/stage
SQL> create pluggable database PDB01SI02 using '/u01/stage/PDB01SI01.pdb' copy;
Let’s have a look into the standby alert log.
Recovery created pluggable database PDB01SI02
Recovery attempting to copy datafiles for pdb-PDB01SI02 from source dir-/u01/stage
Errors in file /u01/app/oracle/diag/rdbms/si02v100/SI02/trace/SI02_pr00_1439299.trc:
ORA-01565: error in identifying file '+DATA'
ORA-17503: ksfdopn:2 Failed to open file +DATA
ORA-15045: ASM file name '+DATA' is not in reference form
Recovery was unable to create the file as:
'+DATA'
Do we really have to unzip the files?
$ cd /u01/stage
$ unzip -q PDB01SI01.pdb
Let’s try again.
SQL> create pluggable database PDB01SI03 using '/u01/stage/PDB01SI01.pdb';
Let’s look into the alert log, if things got worse.
Recovery created pluggable database PDB01SI02
Recovery attempting to copy datafiles for pdb-PDB01SI02 from source dir-/u01/stage
Exception [type: SIGSEGV, Address not mapped to object] [ADDR:0x1C8] [PC:0x978D0AF, kpdbfOMFFindFile()+287] [flags: 0x0, count: 1]
Errors in file /u01/app/oracle/diag/rdbms/si02v100/SI02/trace/SI02_pr00_1460589.trc (incident=34045) (PDBNAME=CDB$ROOT):
ORA-07445: exception encountered: core dump [kpdbfOMFFindFile()+287] [SIGSEGV] [ADDR:0x1C8] [PC:0x978D0AF] [Address not mapped to object] []
Oops. So that does not seem to work. I have reproduced this with another database. What can we do? We could modify the XML description file to represent the extracted files. The archive remains extracted on the standby side in the same location, so that the datafiles are accessible.
$ cd /u01/stage
$ unzip -q PDB01SI01.pdb
$ mv u01/stage/PDB01SI01.xml .
$ sed "s#\+DATA/SI01V105/003BBAD8FC9C41AFE0636524100AA5EE/DATAFILE#/u01/stage#" -i PDB01SI01.xml
SQL> create pluggable database PDB01SI03 using '/u01/stage/PDB01SI01.xml' copy;
The outcome was quite disillusioning.
ORA-19505: failed to identify file "/u01/stage/system.339.1142110293"
ORA-27048: skgfifi: file header information is invalid
Additional information: 2
19505. 00000 - "failed to identify file \"%s\""
*Cause: call to identify the file returned an error
*Action: check additional messages, and check if the file exists.
So at the moment there seems to be no way to create a pluggable database from an archive. If you ever managed to create a standby PDB using an archive, let me know in the comments below.
Conclusion?
Above we have seen that there are several pitfalls when using PDBs in a Data Guard environment. One thing that is not described here but that obviously would also fail is creating a PDB using a network link. This would only succeed if you have Active Data Guard and the standby database can access the source pdb using the same host description specified in the db link as the primary database.
But is there a way to work around all that hassle that works in any way, independently of the process to create the PDB on the primary? Of course there is, but it need some work. The scheme would always be the same though.
The first step is to create the database with the standbys=none
clause. This prevents the standby CDB to set the newly created standby PDB into recovery mode.
SQL> create pluggable database PDB01SI03 using '/u01/stage/PDB01SI01.pdb' standbys=none;
SQL> alter session set container=PDB01SI03;
SQL> select 'alter database datafile ' || file# || ' online;' from v$datafile; -- Need that output later
Now on the standby database restore and recover the PDB, but disable Data Guard recovery before.
DGMGRL> edit database si03v105 set state = 'APPLY-OFF';
RMAN> run {
2> set newname for pluggable database PDB01SI03 to new;
6> restore pluggable database PDB01SI03 from service 'vm101/SI03V100';
7> switch datafile all;
8> switch tempfile all;
9> }
RMAN> recover database;
We need to enable recovery for the pdb, since it was disabled upon creation and online all datafiles that aren’t online now.
SQL> alter session set container=PDB01SI03;
SQL> alter pluggable database enable recovery;
SQL> alter database datafile 15 online;
SQL> alter database datafile 16 online;
SQL> alter database datafile 17 online;
Finally enable the recovery of the standby database again and that’s it!
DGMGRL> edit database si03v105 set state = 'APPLY-ON';