Introduction

Since patch version 19.21 (current one is 19.25), Oracle Database Appliance (ODA) X7, X8 and X9 will require the use of Data Preserving Reprovisioning (DPR) to apply the patch coming from 19.20 and previous releases. Unlike traditional patching, DPR erases the system disks but keeps data on ASM/ACFS disks. Data means databases, ACFS volumes, DB homes, DB Systems and VMs metadata, vDisks for DB Systems and VMs. But it doesn’t keep your system settings: specific OS configurations, additional tools, monitoring, users and groups, aso.

The reason for not being able to use classic patching method is the mandatory OS upgrade from Linux 7 to Linux 8. It is done with a fresh setup only. Actually, it’s much easier to setup a brand new OS than applying a major patch onto an existing Linux system. Therefore, using DPR will require a longer downtime of your ODA, several hours at least, meaning that you will need to switchover your databases to another ODA with Data Guard or Dbvisit Standby.

Dbvisit Standby and Oracle Database Standard Edition

Data Guard is not available on Oracle Database Standard Edition. It means that you cannot configure a Disaster Recovery setup with the embedded binaries. But a database can still be a standby as it does not rely on the database edition. A standby database is mostly a primary that cannot be updated on its own with SQL statements. If you could compare each file between a primary and a standby, the only difference you would see is a flag in the controlfile. This flag defines the role of your database. And you can still convert a primary to a standby and vice-versa with a pair of SQL commands.

Dbvisit Standby relies on this fact and comes with the tooling for transporting archivelogs, managing the sync, switching the roles, doing the failover. Dbvisit Standby has always been the smartest solution for those needing a Disaster Recovery solution without the need for buying Enterprise Edition. Among the companies I work for, 95% of the owners of ODA plus Standard Edition are using Dbvisit Standby for at least one database. When comes the time for patching an ODA, usually once a year, primaries are switched to the standby server as it would be done with Data Guard. And until now, it brings the same flexibility and convenience as Data Guard.

Best practice for Dbvisit Standby on ODA

Classic patching does not require any additional operation when using Dbvisit Standby compared to Data Guard. You just need to change the DB home location in the Dbvisit configuration file after patching the DB home. Patching a DB home is actually moving the database to a new one.

Data Preserving Reprovisioning will have more impact on Dbvisit Standby. It will erase the system, meaning Dvisit binaries and configuration files if they were located on the OS disks.

Best practice to prevent this drawback is to setup Dbvisit Standby in a dedicated ACFS filesystem. I usually create 2 filesystems, one for the binaries, logfiles and configuration files. And another one for archivelogs. This one will be sized depending on the number of protected databases and on the activity of those databases:

su – oracle

mkdir /u01/app/dbvisit/standbymp
mkdir /u01/app/dbvisit/arc
exit

su - grid

sqlplus / as sysasm
ALTER DISKGROUP RECO ADD VOLUME dbvbin SIZE 50G;
ALTER DISKGROUP RECO ADD VOLUME dbvarc SIZE 200G;

col volume_device for a40
select volume_name, VOLUME_DEVICE from v$asm_volume where volume_name='DBVBIN' or volume_name='DBVARC';

VOLUME_NAME                    VOLUME_DEVICE
------------------------------ ----------------------------------------
DBVBIN                         /dev/asm/dbvbin-156
DBVARC                         /dev/asm/dbvarc-156

exit

su - root

/sbin/mkfs -t acfs /dev/asm/dbvbin-156
/sbin/mkfs -t acfs /dev/asm/dbvarc-156

/u01/app/19.24.0.0/grid/bin/srvctl add filesystem -d /dev/asm/dbvbin-156 -g RECO -v DBVBIN -m /u01/app/dbvisit/standbymp -u oracle
/u01/app/19.24.0.0/grid/bin/srvctl start filesystem -d /dev/asm/dbvbin-156

/u01/app/19.24.0.0/grid/bin/srvctl add filesystem -d /dev/asm/dbvarc-156 -g RECO -v DBVARC -m /u01/app/dbvisit/arc -u oracle
/u01/app/19.24.0.0/grid/bin/srvctl start filesystem -d /dev/asm/dbvarc-156

df -h /u01/app/dbvisit/standbymp /u01/app/dbvisit/arc
Filesystem          Size  Used Avail Use% Mounted on
/dev/asm/dbvbin-156  50G  1.1G   49G   1% /u01/app/dbvisit/standbymp
/dev/asm/dbvarc-156  200G  1.1G  199G   1% /u01/app/dbvisit/arc

Once done, you can do the setup and configuration onto these filesystems. They will be preserved when using DPR. The only task you will have to do again after a DPR is creating the Dbvisit service, that’s it:

/u01/app/dbvisit/standbymp/bin/dbvagentmanager service install --user oracle
/u01/app/dbvisit/standbymp/bin/dbvagentmanager service start

Problem you may encounter when using DPR with Dbvisit

I already had this problem 2 times on 2 different configurations: I’m unable to get a successful preupgradereport because of inconsistent metadata in my standby databases.

odacli describe-preupgradereport -i 917d6120-5837-4130-912a-9dc79faae324
...

__DB__
...
Validate Database Status       Success  Database 'PCPDBI' is running and is    None
                                        in 'CONFIGURED' state
Validate Database Version      Success  Version '19.20.0.0.230718' for         None
                                        database 'PCPDBI' is supported
Validate Database Datapatch    Success  Role of database 'PCPDBI' is not       None
Application Status                      'Primary'. This check is skipped.
Validate TDE wallet presence   Success  Database 'PCPDBI' is not TDE enabled.  None
                                        Skipping TDE wallet presence check.
Validate Database Home         Success  Database home location check passed    None
location                                for database PCPDBI_52
Validate Database Service      Failed   The following services [PCPDBI_RW]     These services should be stopped and
presence                                created on database 'PCPDBI' can       removed, then rerun
                                        result in a failure in 'detach-node'.  'create-preupgradereport'. After
                                                                               'restore-node -d' these services
                                                                               should be restored manually
Validate Database metadata     Failed   Internal error encountered             Please check dcs-agent logs
...

The first failure is easy to fix, just remove the service associated to database role. It’s easy to put it back after DPR:

PCPDBI
srvctl stop service -db PCPDBI_52 -service PCPDBI_RW
srvctl remove service -db PCPDBI_52 -service PCPDBI_RW

The second failure does need troubleshooting, here is an extract of the dcs-agent.log:

2024-11-14 17:43:37,414 DEBUG [Validate Database metadata : PCPDBI : JobId=a19cb6d3-0acf-4f82-95b1-0e459b7cd732] [] c.o.d.c.u.CommonsUtils: Output :
Database unique name: PCPDBI_52
Database name: PCPDBI
Oracle home: /u01/app/odaorahome/oracle/product/19.0.0.0/dbhome_30
Oracle user: oracle
Spfile: +DATA/PCPDBI_52/parameterfile/spfilePCPDBI.ora
Password ******** /u01/app/odaorahome/oracle/product/19.0.0.0/dbhome_30/dbs/orapwPCPDBI ********
Domain:
Start options: mount
Stop options: abort
Database role: PHYSICAL_STANDBY
Management policy: AUTOMATIC
Server pools:
Disk Groups: DATA,
RECO
Mount point paths: /u01/app/odaorahome,
/u01/app/odaorabase0
Services:
Type: SINGLE
OSDBA group: dba
OSOPER group: dbaoper
Database instance: PCPDBI
Configured nodes: rtssgtumitoda52
CSS critical: no
CPU count: 0
Memory target: 0
Maximum memory: 0
Default network number for database services:
Database is administrator managed
2024-11-14 17:43:37,414 ERROR [Validate Database metadata : PCPDBI : JobId=a19cb6d3-0acf-4f82-95b1-0e459b7cd732] [] c.o.d.a.d.DetachRpcHandler: Exception
java.lang.NullPointerException: null
	at com.oracle.dcs.agent.dpr.prechecks.DatabasePrechecks$DatabaseMetadataPrecheck.validateDatabaseDbRole(DatabasePrechecks.java:779)
	at com.oracle.dcs.agent.dpr.prechecks.DatabasePrechecks$DatabaseMetadataPrecheck.execute(DatabasePrechecks.java:701)
	at com.oracle.dcs.agent.dpr.DetachRpcHandler.executePreUpgradecheck(DetachRpcHandler.java:276)
	at sun.reflect.GeneratedMethodAccessor248.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.oracle.dcs.commons.jrpc.JsonRequestProcessor.invokeRequest(JsonRequestProcessor.java:246)
	at com.oracle.dcs.commons.jrpc.JsonRequestProcessor.process(JsonRequestProcessor.java:305)
	at com.oracle.dcs.agent.task.TaskDcsJsonRpcExt.callInternal(TaskDcsJsonRpcExt.java:106)
	at com.oracle.dcs.agent.task.TaskDcsJsonRpc.call(TaskDcsJsonRpc.java:303)
	at com.oracle.dcs.agent.task.TaskDcsJsonRpc.call(TaskDcsJsonRpc.java:79)
	at com.oracle.dcs.commons.task.TaskWrapper.call(TaskWrapper.java:118)
	at com.oracle.dcs.commons.task.TaskApi.call(TaskApi.java:63)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:65)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:36)
	at com.oracle.dcs.commons.task.TaskWrapper.call(TaskWrapper.java:118)
	at com.oracle.dcs.commons.task.TaskApi.call(TaskApi.java:63)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:65)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:36)
	at com.oracle.dcs.commons.task.TaskWrapper.call(TaskWrapper.java:118)
	at com.oracle.dcs.commons.task.TaskApi.call(TaskApi.java:63)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:65)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:36)
	at com.oracle.dcs.commons.task.TaskWrapper.call(TaskWrapper.java:118)
	at com.oracle.dcs.commons.task.TaskApi.call(TaskApi.java:63)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:65)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:36)
	at com.oracle.dcs.commons.task.TaskWrapper.call(TaskWrapper.java:118)
	at com.oracle.dcs.commons.task.TaskApi.call(TaskApi.java:63)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:65)
	at com.oracle.dcs.agent.task.TaskLockWrapper.call(TaskLockWrapper.java:136)
	at com.oracle.dcs.agent.task.TaskLockWrapper.call(TaskLockWrapper.java:59)
	at com.oracle.dcs.commons.task.TaskWrapper.call(TaskWrapper.java:118)
	at com.oracle.dcs.commons.task.TaskApi.call(TaskApi.java:63)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:65)
	at com.oracle.dcs.commons.task.TaskSequential.call(TaskSequential.java:36)
	at com.oracle.dcs.commons.task.TaskWrapper.call(TaskWrapper.java:118)
	at com.oracle.dcs.commons.task.TaskWrapper.call(TaskWrapper.java:60)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:750)

Metadata is not OK on this database, but which metadata?

As the standby is only mounted, it cannot be metadata inside the database. I had no problem when using DPR on databases protected by Data Guard, especially on standby databases. It should be the same with Dbvisit Standby.

Inconsistent metadata in OCR would make sense, but after checking on this database, and apart from database role and startup mode, it looks similar to the primary databases. I temporarily changed the database role from STANDBY to PRIMARY:

. oraenv <<< PCPDBI
srvctl modify database -db PCPDBI -role PRIMARY
srvctl stop database -db PCPDBI_52 ; sleep 10 ; srvctl start database -db PCPDBI_52 -o mount

But it doesn’t change anything.

It should be related to metadata in the ODA registry. Let’s have a look in the MySQL database where the registry is located. Stop the DCS agent before doing anything:

systemctl stop initdcsagent

cd /opt/oracle/dcs/mysql/bin/ 
./mysql -u root --socket=/opt/oracle/dcs/mysql/log/mysqldb.sock 

use dcsagentdb;
 
select name, dbrole from db where name='PCPDBI' or name='TSTDBI';
+----------+---------+
| name     | dbrole  |
+----------+---------+
| PCPDBI   | NULL    |
| TSTDBI   | PRIMARY |
+----------+---------+
2 rows in set (0.00 sec)

For some reason, there is no database role for my standby in the ODA registry, let’s solve this problem and restart the odacli stack:

Create table db_20241115 as select * from db;
Update db set dbrole='STANDBY' from db where name='PCPDBI';
Query OK, 1 rows affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Commit;
exit;

systemctl start initdcsagent

sleep 120 ; odacli create-preupgradereport -bm 
...

This solved my problem.

Conclusion

Using DPR on your ODA running Dbvisit Standby works fine, but don’t hesitate to check and fix the role of your database in the ODA registry if you’re stuck at the preupgradereport.