In my last blog post Snapshots with VirtualBox and btrfs we have seen some possibilities of btrfs snapshots.
In this blog post we will see how we can use btrfs snapshots in combination with an Oracle database to clone PDBs.
My colleague Marc Wagner has shown in his article the possibilities and the limitations of snapshot clones of a PDB. If we use btrfs snapshots, we can bypass these limitations.
Preparation of subvolumes
I will show, how to create a test-PDB “WEBT” from the production-PDB “WEBP”. The prerequisite is that all PDB files from WEBP are stored in their own btrfs sub-volume. If not yet done, do it now:
root$ btrfs subvolume list -t /u01/oradata/MNB1 |grep WEBP #if not found, continue
SQL> alter pluggable database WEBP close immediate;
root$ cd /u01/oradata/MNB
root$ mv WEBP WEBP.bck
root$ btrfs subvolume create WEBP
root$ mv WEBP.bck/* WEBP/
root$ rmdir WEBP.bck
SQL> alter pluggable database all open;
SQL> alter pluggable database WEBP open;
Create a subvolume snapshot and create a PDB with it
After this one-time preparation, we are ready to prepare our cloning. Unfortunately, the source-PDB (WEBP) must be read-only during the cloning.
ALTER SESSION SET container=WEBP;
shutdown immediate;
ALTER DATABASE OPEN READ ONLY;
-- create the manifest file
EXEC dbms_pdb.describe(pdb_descr_file=>'/u00/app/oracle/admin/MNB1/WEBP.xml');
As root (or as oracle with sudo-rights) we create the consistent subvolume snapshot of the btrfs filesystem:
cd /u01/oradata/MNB1
btrfs subvolume snapshot WEBP WEBT
Readonly for WEBP is no longer required, so we can re-open it read-write. Then, we can integrate our new WEBT test-database.
ALTER SESSION SET container=WEBP;
shutdown immediate;
ALTER DATABASE OPEN;
ALTER SESSION SET container=cdb$root;
CREATE pluggable DATABASE WEBT
AS clone USING '/u00/app/oracle/admin/MNB1/WEBP.xml'
source_file_name_convert=('WEBP','WEBT')
nocopy tempfile reuse standbys=NONE;
ALTER SESSION SET container=webt;
ALTER DATABASE OPEN;
Hint: If we have forgotten to do the snapshot in readonly mode, then we get
ORA-65368: unable to open the pluggable database due to errors during recovery
From Oracle’s point of view, we have now a full-clone of the pluggable database, not a sparse clone. And initially without additional space usage. The space usage only increases if we change blocks (in the original or the clone).
It is time to adapt your cloned WEBT. Change passwords, disable jobs, change dblinks, etc.
Recurring cloning
If you want to update WEBT, the workflow is similar. First, delete WEBT
SQL> alter pluggable database webt close abort;
SQL> drop pluggable database webt including datafiles;
root$ btrfs subvolume delete /u01/oradata/MNB1/WEBT
Then you can continue as above.
Cloning without downtime?
But can we really not do it without downtime of the production (put to readonly, put back to readwrite)? In the summary of Mike Dietrich’s blog about dbms_pdb.describe we can see that the package can now be called in read-write, but that is only intended for compatibility check, not for plugin.
If I create the subvolume snapshot before the manifest file: Is it not possible to recover the files up to the point-in-time of the manifest? Let’s try…
Because the source PDB is open, I want to be careful and I will enable the good old backup mode first before create the subvolume snapshot.
SQL> alter database begin backup;
cd /u01/oradata/MNB1
btrfs subvolume snapshot WEBP WEBT
SQL> alter database end backup;
I think it might work if the snapshot is older than the manifest file. Then hopefully we can recover the files to the time of the manifest.
ALTER SESSION SET container=WEBP;
-- now we will NOT shutdown and open read-write our production!
EXEC dbms_pdb.describe(pdb_descr_file=>'/u00/app/oracle/admin/MNB1/WEBP.xml');
ALTER SESSION SET container=cdb$root;
CREATE pluggable DATABASE WEBT
AS clone USING '/u00/app/oracle/admin/MNB1/WEBP.xml'
source_file_name_convert=('WEBP','WEBT')
nocopy tempfile reuse standbys=NONE;
alter pluggable database WEBT open;
But unfortunately, it does not work 🙁
CREATE pluggable DATABASE WEBT AS clone USING '/u00/app/oracle/admin/MNB1/WEBP.xml' source_file_name_convert=('WEBP','WEBT') nocopy tempfile reuse standbys=NONE
*
ERROR at line 1:
ORA-65368: unable to open the pluggable database due to errors during recovery
ORA-65089: pluggable database is not clean
ORA-01113: file 91 needs media recovery
ORA-01110: data file 91: '/u01/oradata/MNB1/WEBT/users01.dbf'
But wait. Let’s check what happens. In the alertlog:
ORA-65368 signalled during: CREATE pluggable DATABASE WEBT AS clone USING '/u00/app/oracle/admin/MNB1/WEBP.xml' source_file_name_convert=('WEBP','WEBT') nocopy tempfile reuse standbys=NONE...
2026-10-02T19:31:58.630109+02:00
Errors in file /u00/app/oracle/diag/rdbms/mnb1a/MNB1/trace/MNB1_mz00_1856209.trc:
ORA-01110: data file 3: '/u01/oradata/MNB1/sysaux01.dbf'
And I will check in sqlplus if it created something:
SQL> show pdbs
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
3 WEBP READ WRITE NO
4 WEBT MOUNTED
SQL> select name from v$datafile where name like '%WEBT%';
NAME
--------------------------------------------
/u01/oradata/MNB1/WEBT/system01.dbf
/u01/oradata/MNB1/WEBT/sysaux01.dbf
/u01/oradata/MNB1/WEBT/undotbs01.dbf
/u01/oradata/MNB1/WEBT/users01.dbf
SQL>
And we see, it created the pdb and the datafiles are visible. But it complained: “file 91 needs media recovery”. Recovery? let’s try with rman:
RMAN> recover database;
RMAN> recover database;
Starting recover at 02-OCT-26
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=263 device type=DISK
datafile 89 not processed because file is offline
datafile 90 not processed because file is offline
datafile 91 not processed because file is offline
...
Let’s try to recover the offline files
RMAN> recover datafile 89,90,91;
RMAN> recover datafile 89,90,91;
Starting recover at 02-OCT-26
using channel ORA_DISK_1
starting media recovery
media recovery complete, elapsed time: 00:00:00
Finished recover at 02-OCT-26
Now it looks better. I will now try to open the PDB.
SQL> alter pluggable database webt open;
SQL> alter pluggable database webt open;
alter pluggable database webt open
*
ERROR at line 1:
ORA-65368: unable to open the pluggable database due to errors during recovery
ORA-65089: pluggable database is not clean
ORA-01113: file 88 needs media recovery
ORA-01110: data file 88: '/u01/oradata/MNB1/WEBT/system01.dbf'
Why datafile 88? It complained 89-91… Nevermind, we will try to recover it and to open the PDB again.
SQL> recover datafile 88;
SQL> recover datafile 88;
Media recovery complete.
SQL> alter pluggable database webt open;
Pluggable database altered.
And yes! it worked 🙂
If not yet done, do not forget to disable the backup mode.
SQL> alter database end backup;
alter database end backup
*
ERROR at line 1:
ORA-01260: warning: END BACKUP succeeded but some files found not to be in backup mode
That is not a problem, It only affects our cloned files.
Scripting
To do the cloning automated, create some sudo-rules and create a cloning-script.
# visudo -f /etc/sudoers.d/oracle-btrfs
oracle ALL=(root) NOPASSWD: /sbin/btrfs subvolume delete /u01/oradata/MNB1/WEBT
oracle ALL=(root) NOPASSWD: /sbin/btrfs subvolume snapshot /u01/oradata/MNB1/WEBP /u01/oradata/MNB1/WEBT
# set the environment for the SID;
# I will use our dbi-services dmk environment script
. dmk.sh MNB1
# remove the old clone
echo "alter pluggable database webt close abort;
drop pluggable database webt including datafiles;
alter database begin backup;" \
|sqlplus -S -L / as sysdba
sudo btrfs subvolume delete /u01/oradata/MNB1/WEBT
# create the new clone
sudo btrfs subvolume snapshot /u01/oradata/MNB1/WEBP /u01/oradata/MNB1/WEBT
echo "alter database end backup;
ALTER SESSION SET container=WEBP;
EXEC dbms_pdb.describe(pdb_descr_file=>'/u00/app/oracle/admin/MNB1/WEBP.xml');
ALTER SESSION SET container=cdb\$root;
CREATE pluggable DATABASE WEBT AS clone
USING '/u00/app/oracle/admin/MNB1/WEBP.xml'
source_file_name_convert=('WEBP','WEBT')
nocopy tempfile reuse standbys=NONE;
" |sqlplus -S -L / as sysdba
echo "select 'recover datafile '||file#||';'
from v\$datafile where name like '%/WEBT/%';" \
| sqlplus -S -L / as sysdba | grep recover |rman target /
echo "alter pluggable database webt open;
show pdbs" |sqlplus -S -L / as sysdba
And here is what I want to see 🙂
CON_ID CON_NAME OPEN MODE RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED READ ONLY NO
3 WEBP READ WRITE NO
5 WEBT READ WRITE NO