Introduction
In an Oracle database, services have multiple purposes:
- identifying group of users prior authenticating them
- enabling/disabling access to a database for particular groups
- managing preferred nodes on a RAC configuration
- redirecting users to the primary after a switchover/failover when using Data Guard or Dbvisit Standby
- redirecting read only connections to Standby database with Active Guard option
A customer asked me if services could be used for switching between PDBs without modifying client’s connexion, for a very specific usage. Let’s try to find out.
Initializing the test environment
Let’s create 2 PDBs. The goal is to share a service that can alternatively run on the first PDB or on the second one:
create pluggable database JDU1 admin user JDU identified by JDU2021 ROLES=(DBA) DEFAULT TABLESPACE data DATAFILE SIZE 1G AUTOEXTEND ON MAXSIZE 8G TEMPFILE REUSE; create pluggable database JDU2 admin user JDU identified by JDU2021 ROLES=(DBA) DEFAULT TABLESPACE data DATAFILE SIZE 1G AUTOEXTEND ON MAXSIZE 8G TEMPFILE REUSE; alter pluggable database jdu1 open; alter pluggable database jdu2 open; |
Let’s check current running services on these 2 PDBs:
alter session set container=JDU1; select name from v$active_services; NAME ---------------------------------------------------------------- jdu1 alter session set container=JDU2; select name from v$active_services; NAME ---------------------------------------------------------------- jdu2 |
Create a “shared” service and test it
Let’s go to the first container and create the “shared” service:
alter session set container=JDU1; execute DBMS_SERVICE.CREATE_SERVICE (service_name => 'JDU_SVC' , network_name => 'JDU_SVC' , failover_method => 'BASIC' , failover_type => 'SELECT' , failover_retries => 1800 , failover_delay => 1 ); exec dbms_service.start_service (service_name => 'JDU_SVC' ); select name from v$active_services; NAME ---------------------------------------------------------------- jdu1 JDU_SVC |
Now, let’s also try to create the service inside the other PDB:
alter session set container=JDU2; execute DBMS_SERVICE.CREATE_SERVICE (service_name => 'JDU_SVC' , network_name => 'JDU_SVC' , failover_method => 'BASIC' , failover_type => 'SELECT' , failover_retries => 1800 , failover_delay => 1 ); * ERROR at line 1 : ORA- 44303 : service name exists ORA- 06512 : at "SYS.DBMS_SERVICE_ERR" , line 21 ORA- 06512 : at "SYS.DBMS_SERVICE" , line 316 ORA- 06512 : at line 1 |
It does not work because service seems to be global. Let’s try to start it:
exec dbms_service.start_service (service_name => 'JDU_SVC' ); * ERROR at line 1 : ORA- 44786 : Service operation cannot be completed. ORA- 06512 : at "SYS.DBMS_SERVICE" , line 76 ORA- 06512 : at "SYS.DBMS_SERVICE" , line 483 ORA- 06512 : at line 1 |
It does not work because it’s already started on the first PDB. Let’s stop it on the first PDB:
alter session set container=JDU1; exec dbms_service.stop_service (service_name => 'JDU_SVC' ); |
And start it on the second PDB:
alter session set container=JDU2; exec dbms_service.start_service (service_name => 'JDU_SVC' ); * ERROR at line 1 : ORA- 44786 : Service operation cannot be completed. ORA- 06512 : at "SYS.DBMS_SERVICE" , line 76 ORA- 06512 : at "SYS.DBMS_SERVICE" , line 483 ORA- 06512 : at line 1 |
No, it doesn’t work.
Let’s now remove the service from the first PDB and create and start it on the second PDB:
alter session set container=JDU1; exec dbms_service.delete_service (service_name => 'JDU_SVC' ); PL/SQL procedure successfully completed. alter session set container=JDU2; Session altered. execute DBMS_SERVICE.CREATE_SERVICE (service_name => 'JDU_SVC' , network_name => 'JDU_SVC' , failover_method => 'BASIC' , failover_type => 'SELECT' , failover_retries => 1800 , failover_delay => 1 ); exec dbms_service.start_service (service_name => 'JDU_SVC' ); select name from v$active_services; NAME ---------------------------------------------------------------- JDU_SVC jdu2 |
Findings
A service is global and cannot exist in more than one PDB. That’s quite obvious but I thought it was possible to declare it in multiple PDB and start only in one PDB.
Note that at the CDB level, querying the CDB_SERVICES will give you an overview of services and PDB associated:
col name for a30 col pdb for a30 select name, pdb from cdb_services; NAME PDB ------------------------------ ------------------------------ SYS$BACKGROUND CDB$ROOT SYS$USERS CDB$ROOT CDBSYR01.test.ch CDB$ROOT CDBSYR01XDB CDB$ROOT CDBSYR01_SITE1.test.ch CDB$ROOT CSYR01AXDB CDB$ROOT CSYR01A.test.ch CDB$ROOT CFRD01AXDB CDB$ROOT CFRD01A.test.ch CDB$ROOT JDU1 JDU1 JDU2 JDU2 JDU_SVC JDU2 |
Last question I’ve been asking: is it possible to stop the service from the CDB? No, you’ll have to switch to the correct container to do that.
conn / as sysdba exec dbms_service.stop_service (service_name => 'JDU_SVC' ); * ERROR at line 1 : ORA- 44786 : Service operation cannot be completed. ORA- 06512 : at "SYS.DBMS_SERVICE_ERR" , line 91 ORA- 06512 : at "SYS.DBMS_SERVICE" , line 519 ORA- 06512 : at line 1 |
Conclusion
Service is not manageable through CDB, although being a global mechanism. If you want to use services to switch from one PDB to another one, you’ll have to script that because it’s not a feature.
But would you really use this kind of script? Not sure this would be a good idea. If your script failed for some reason, your application will use a database that may not be the expected one, without being aware. Quite dangerous in my opinion.