Upgrade PDB en 12.1.0.2 : la méthode miracle ?

Les présentations des fonctionnalités multitenant de la 12C nous ont fait rêver d’une méthode de migration utlra simple pour passer d’une version de la base à une autre. Procédure que j’imagine en six étapes :

1) installation des nouveaux binaires
2) Création d’un container racine avec la nouvelle version
3) Arrêt de la base pluggable cible
4) Unplug de la base du container de l’ancienne version
5) Plug de la base dans le nouveau container
6)  Ouverture de la base
Et c’est fini ! Le temps d’arrêt, entre l’étape 3) et l’étape 6), est  réduit à la durée du déplacement des fichiers (qui n’est pas obligatoire)
Vu que le dictionnaire est dans le container racine et que c’est de lui dont dépend la version de la base, pourquoi ne pas y croire.
Voyons qu’elle est la réalité avec un cas d’école.


Environnement :

  • Système linux 64bits (Virtual Box) avec OEL6.1
  • Version 12.1.0.1 du logiciel base de données opérationnelle
  • Container cdbELE en version 12.1.2.0.1 (ORACLE_HOME=/u01/app/oracle/product/12.1.0.1)
  • Base PDBELE pluggée sur le container cdbELE  , base créée depuis le template et alimenté par un schéma SOE ,  schéma Order Entry de l’application de test Swingbench
  • Distribution database 12.1.2.0.2 installée (ORACLE_HOME=/u01/app/oracle/product/12.1.0.2)
  • Container cdb1212 créé vide.

Les étapes 1 et 2 sont donc déjà réalisée , déroulons la suite de la méthode:
Etape 3) Arrêt de la base pluggable cible
J’ajoute, par précaution avant de « déplugger  » la base,  le passage du script de vérification préalable à la mise à jour:
– Connexion sur le container source en version 12.1.0.1 : cdbELE

[oracle@ele1ole6 ~]$ . oraenv
ORACLE_SID = [oracle] ? cdbELE
The Oracle base remains unchanged with value /u01/app/oracle
[oracle@ele1ole6 ~]$ sqlplus / as sysdba
SQL*Plus: Release 12.1.0.1.0 Production on Thu Aug 14 09:28:30 2014
Copyright (c) 1982, 2013, Oracle.  All rights reserved.
Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
SQL> show pdbs     CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO        
         3 PDBELE                         READ WRITE NO
SQL> alter session set container=pdbELE ;
SQL> @/u01/app/oracle/product/12.1.0.2/rdbms/admin/preupgrd.sql

Résultat contenu dans le fichier journal:  tous les composants sont valides, pas d’erreur , pas de point d’attention, un seul message informatif apparait concernant la version d’APEX actuellement dans la base :

…/…
******************************************************************
                          [Pre-Upgrade Checks]
**********************************************************************
INFORMATION: --> Oracle Application Express (APEX) can be
     manually upgraded prior to database upgrade
     APEX is currently at version 4.2.0.00.27 and will need to be
     upgraded to APEX version 4.2.5 in the new release.
     Note 1: To reduce database upgrade time, APEX can be manually
             upgraded outside of and prior to database upgrade.
     Note 2: See MOS Note 1088970.1 for information on APEX
             installation upgrades.
…/…

Je me sens donc prêt et serein pour mon opération.
– Arrêt du pdb cible PDBELE  connecté sur le container d’origine :

SQL> alter session set container=CDB$ROOT ;
SQL> alter pluggable database pdbELE close ;
SQL> show pdbs
2 PDB$SEED                       READ ONLY  NO
3 PDBELE                         MOUNTED

Etape 4) Unplug de mon PDB  depuis le container d’origine
Une seule commande à passer, opération très rapide qui génère une description de la base dans le fichier xml donné :

SQL> alter pluggable database pdbELE unplug into '/home/oracle/pdbELE.xml' ;

A noter qu’a partir de ce moment, une fois la base “dépluggée” , la seule opération qui peut être réalisée dans le container d’origine pour cette base est une suppression via la clause « DROP », il sera impossible de la rouvrir dans le même container sans faire une opération de “plugging” avec les bonnes options.
Etape 5) Plug de la base PDBELE dans le nouveau container CDB1212
Avant cela,  il existe déjà un moyen de contrôle de la compatibilité pour la base que l’on veut traiter via le package DBMS_PDB.CHECK_PLUG_COMPATIBILTY, on peut l’utiliser de cette manière :
Connexion sur le container racine cible CDB1212

[oracle@ele1ole6 ~]$ . oraenv
ORACLE_SID = [oracle] ? cdb1212
The Oracle base remains unchanged with value /u01/app/oracle
[oracle@ele1ole6 ~]$ sqlplus / as sysdba SQL*Plus: Release 12.1.0.2.0 Production on Thu Aug 14 09:40:30 2014 Copyright (c) 1982, 2013, Oracle.  All rights reserved. Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
SQL> set serveroutput on
SQL> DECLARE
compatible CONSTANT VARCHAR2(3) := CASE DBMS_PDB.CHECK_PLUG_COMPATIBILITY(
pdb_descr_file => '/home/oracle/pdbELE.xml' ,
pdb_name => 'PDBELE' )
WHEN TRUE THEN 'YES' ELSE 'NO'
END;
BEGIN
DBMS_OUTPUT.PUT_LINE(compatible);
END;
/

Première déconvenue, le résultat est : NO ,  la vue PDB_PLUG_IN_VIOLATIONS nous donne plus d’informations :

SQL> select message, status from pdb_plug_in_violations where type like '%ERR%';
MESSAGE                                                                                           STATUS
---------------------------------------------------------------------------------------------------------
PDB's version does not match CDB's version: PDB's version 12.1.0.0.0. CDB's version 12.1.0.2.0.   PENDING
APEX mismatch: PDB installed version 4.2.0.00.27 CDB installed version 4.2.5.00.08                PENDING

Le message concernant APEX ne me perturbe pas,  l’opération de suppression et de réinstallation du module est possible et bien documenté via la note MOS 558340.1, par contre l’incohérence de version entre le PDB et le CDB, m’interroge sur la faisabilité de ma migration miracle !  Allons au bout de notre idée :
– Plugging de la base , par création d’une nouvelle base pluggable:
J’utilise l’option “using <xml file>” et la conversion de la localisation des fichiers via la clause “file_name_convert”  , par défaut les fichiers sont copiés et non pas déplacés:

SQL> create pluggable database pdbELE using '/home/oracle/pdbELE.xml'
file_name_convert=('/u01/oradata/cdbELE/pdbELE','/u03/oradata/cdb1212/pdbELE') ;

Résultat :  “Pluggable database created.“
Il me reste un faible espoir d’aboutir, l’instant de vérité est proche.
Etape 6)  Ouverture de la base en lecture/écriture

SQL> alter pluggable database PDBELE open ;
Warning: PDB altered with errors.

Aie ! ce n’est pas ce que j’espérais !
En fait la base est ouverte , mais dans le mode special “MIGRATE” et “RESTRICTED” :

SQL> show pdbs
CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
2 PDB$SEED                       READ ONLY  NO
3 PDBELE                         MIGRATE    YES

Cest l’équivalent de la commande :

SQL>  alter pluggable database pdbele open upgrade ;

On retrouve bien ces informations dans les traces du  fichier d’alerte :

ORA-65000 signalled during: alter pluggable database open ...
2014-08-14 10:30:58.166000 +02:00
alter pluggable database PDBELE open
Pluggable database PDBELE dictionary check beginning
Pluggable Database PDBELE Dictionary check complete
Database Characterset for PDBELE is WE8MSWIN1252
***************************************************************
WARNING: Pluggable Database PDBELE with pdb id - 3 is
altered with errors or warnings. Please look into
PDB_PLUG_IN_VIOLATIONS view for more details.
***************************************************************
Due to limited space in shared pool (need 6094848 bytes, have 3981120 bytes), limiting Resource Manager entities from 2048 to 32
2014-08-14 10:30:59.665000 +02:00
Opening pdb PDBELE (3) with Resource Manager plan: DEFAULT_PLAN
ALTER SYSTEM SET _system_trig_enabled=FALSE SCOPE=MEMORY;
ALTER SYSTEM SET enable_ddl_logging=FALSE SCOPE=MEMORY;
Resource Manager disabled during database migration: plan '' not set
ALTER SYSTEM SET resource_manager_plan= SCOPE=MEMORY;
ALTER SYSTEM SET recyclebin='OFF' DEFERRED SCOPE=MEMORY;
Pluggable database PDBELE opened read write
Completed: alter pluggable database PDBELE open

C’est confirmer par contenu de la vue PDB_PLUG_IN_VIOLATIONS qui insiste sur le fait que les deux versions du dictionnaire ne sont pas compatibles:

SQL> select message, status from pdb_plug_in_violations where type like '%ERR%';
MESSAGE                                                                                           STATUS
---------------------------------------------------------------------------------------------------------
PDB's version does not match CDB's version: PDB's version 12.1.0.1.0. CDB's version 12.1.0.2.0.   PENDING

Cela ne suffit donc pas,  adieu nos rêves de méthode de migration miracle, les liens entre le dictionnaire de la base pluggable (qui contient ses propres objets) et ceux des objets communs fournis par Oracle dans le dictionnaire racine doivent être refait. l’hypothèse de départ est fausse car il y a bien deux dictionnaires différent reliés par des pointeurs!
Pour s’en sortir pas d’autre solution que de passer par l’étape de mise à jour , soit:
Etape 6) qui devient : Mise à niveau du dictionnaire de la PDB
Le script à utiliser  est le perl catctl.pl de la version, le même que pour une base en architecture traditionnelle et que j’ai déja testé ici. Il se trouve sous $ORACLE_HOME/rdbms/admin de la version cible, et nécessite un répertoire de destination si on veut lui faire générer des fichiers journaux. Je l’utilise ainsi avec le nom du container concerné passé à l’argument « -c » :

[oracle@ele1ole6 ~]$ cd $ORACLE_HOME/rdbms/admin
[oracle@ele1ole6 admin]$ mkdir /home/oracle/upgradePDBELE
[oracle@ele1ole6 admin]$ /u01/app/oracle/product/12.1.0.2/perl/bin/perl catctl.pl -c "PDBELE" -l /home/oracle/upgradePDBELE catupgrd.sql

Voici la sortie générée :

Argument list for [catctl.pl]
SQL Process Count n = 0
SQL PDB Process Count N = 0
Input Directory d = 0
Phase Logging Table t = 0
Log Dir l = /home/oracle/upgradePDBELE
Script s = 0
Serial Run S = 0
Upgrade Mode active M = 0
Start Phase p = 0
End Phase P = 0
Log Id i = 0
Run in c = PDBELE
Do not run in C = 0
Echo OFF e = 1
No Post Upgrade x = 0
Reverse Order r = 0
Open Mode Normal o = 0
Debug catcon.pm z = 0
Debug catctl.pl Z = 0
Display Phases y = 0
Child Process I = 0
catctl.pl version: 12.1.0.2.0
Oracle Base = /u01/app/oracle
Analyzing file catupgrd.sql
Log files in /home/oracle/upgradePDBELE
catcon: ALL catcon-related output will be written to /home/oracle/upgradePDBELE/catupgrd_catcon_3693.lst
catcon: See /home/oracle/upgradePDBELE/catupgrd*.log files for output generated by scripts
catcon: See /home/oracle/upgradePDBELE/catupgrd_*.lst files for spool files, if any
Number of Cpus = 1
Parallel PDB Upgrades = 2
SQL PDB Process Count = 2
SQL Process Count = 0
New SQL Process Count = 1
[CONTAINER NAMES]
CDB$ROOT
PDB$SEED
PDBELE
PDB Inclusion:[PDBELE] Exclusion:[]
Starting
[/u02/app/oracle/product/12.1.0.2/perl/bin/perl catctl.pl -c 'PDBELE' -l /home/oracle/upgradePDBELE -I -i pdbele -n 2 catupgrd.sql]
Argument list for [catctl.pl]
SQL Process Count n = 2
SQL PDB Process Count N = 0
Input Directory d = 0
Phase Logging Table t = 0
Log Dir l = /home/oracle/upgradePDBELE
Script s = 0
Serial Run S = 0
Upgrade Mode active M = 0
Start Phase p = 0
End Phase P = 0
Log Id i = pdbele
Run in c = PDBELE
Do not run in C = 0
Echo OFF e = 1
No Post Upgrade x = 0
Reverse Order r = 0
Open Mode Normal o = 0
Debug catcon.pm z = 0
Debug catctl.pl Z = 0
Display Phases y = 0
Child Process I = 1
catctl.pl version: 12.1.0.2.0
Oracle Base = /u01/app/oracle
Analyzing file catupgrd.sql
Log files in /home/oracle/upgradePDBELE
catcon: ALL catcon-related output will be written to /home/oracle/upgradePDBELE/catupgrdpdbele_catcon_3775.lst
catcon: See /home/oracle/upgradePDBELE/catupgrdpdbele*.log files for output generated by scripts
catcon: See /home/oracle/upgradePDBELE/catupgrdpdbele_*.lst files for spool files, if any
Number of Cpus = 1
SQL PDB Process Count = 2
SQL Process Count = 2
[CONTAINER NAMES]
CDB$ROOT
PDB$SEED
PDBELE
PDB Inclusion:[PDBELE] Exclusion:[]
------------------------------------------------------
Phases [0-73]
Container Lists Inclusion:[PDBELE] Exclusion:[]
Serial Phase #: 0 Files: 1 Time: 10s PDBELE
Serial Phase #: 1 Files: 5 Time: 39s PDBELE
.../...
Serial Phase #:62 Files: 1 Time: 126s PDBELE
Restart Phase #:63 Files: 1 Time: 0s PDBELE
Serial Phase #:64 Files: 1 Time: 0s PDBELE
Serial Phase #:65 Files: 1 Calling sqlpatch with LD_LIBRARY_PATH=/u02/app/oracle/product/12.1.0.2/lib; export LD_LIBRARY_PATH;/u02/app/oracle/product/12.1.0.2/perl/bin/perl -I /u02/app/oracle/product/12.1.0.2/rdbms/admin -I /u02/app/oracle/product/12.1.0.2/rdbms/admin/../../sqlpatch /u02/app/oracle/product/12.1.0.2/rdbms/admin/../../sqlpatch/sqlpatch.pl -verbose -upgrade_mode_only -pdbs PDBELE > /home/oracle/upgradePDBELE/catupgrdpdbele_datapatch_upgrade.log 2> /home/oracle/upgradePDBELE/catupgrdpdbele_datapatch_upgrade.err
returned from sqlpatch
Time: 10s PDBELE
Serial Phase #:66 Files: 1 Time: 3s PDBELE
Serial Phase #:68 Files: 1 Time: 3s PDBELE
Serial Phase #:69 Files: 1 Calling sqlpatch with LD_LIBRARY_PATH=/u02/app/oracle/product/12.1.0.2/lib; export LD_LIBRARY_PATH;/u02/app/oracle/product/12.1.0.2/perl/bin/perl -I /u02/app/oracle/product/12.1.0.2/rdbms/admin -I /u02/app/oracle/product/12.1.0.2/rdbms/admin/../../sqlpatch /u02/app/oracle/product/12.1.0.2/rdbms/admin/../../sqlpatch/sqlpatch.pl -verbose -pdbs PDBELE > /home/oracle/upgradePDBELE/catupgrdpdbele_datapatch_normal.log 2> /home/oracle/upgradePDBELE/catupgrdpdbele_datapatch_normal.err
returned from sqlpatch
Time: 11s PDBELE
Serial Phase #:70 Files: 1 Time: 79s PDBELE
Serial Phase #:71 Files: 1 Time: 11s PDBELE
Serial Phase #:72 Files: 1 Time: 5s PDBELE
Serial Phase #:73 Files: 1 Time: 0s PDBELE
Grand Total Time: 1355s PDBELE
LOG FILES: (catupgrdpdbele*.log)
Upgrade Summary Report Located in:
/u01/app/oracle/product/12.1.0.2/cfgtoollogs/cdb1212/upgrade/upg_summary.log
Total Upgrade Time: [0d:0h:22m:35s]
Time: 1358s For PDB(s)
Grand Total Time: 1358s
LOG FILES: (catupgrd*.log)
Grand Total Upgrade Time: [0d:0h:22m:38s]

Cela prend pratiquement autant de temps que pour une base Oracle traditionnelle , et c’est dommage!
On pouvait penser qu’en manipulant des liens plutôt que des données dans les dictionnaires, le traitement soit plus rapide.
On vérifie qu’il n’y a pas de lignes d’erreur dans les fichiers journaux et on peut passer à la dernière étape : l’ouverture de la base ,
car en fin du perl celle-ci est arrêté, en voici la preuve dans le fichier d’alerte :

2014-08-14 11:40:12.656000 +02:00
SERVER COMPONENT id=POSTUP_END: timestamp=2014-08-14 11:40:12 Container=PDBELE Id=3
2014-08-14 11:40:28.333000 +02:00
ALTER PLUGGABLE DATABASE CLOSE IMMEDIATE
2014-08-14 11:40:31.680000 +02:00
ALTER SYSTEM: Flushing buffer cache inst=0 container=3 local
Pluggable database PDBELE closed
Completed: ALTER PLUGGABLE DATABASE CLOSE IMMEDIATE

Etape 7)   Ouverture de la base et dernière recompilation des objets invalides

SQL> alter pluggable database PDBELE open ;
Pluggable database altered.

Nécessité de faire une recompilation des objets via utlrp :

SQL> alter session set container=PDBELE
SQL> select count(*) from dba_objects where status not like 'VALID' ;
COUNT(*)
---------
728
SQL> @?/rdbms/admin/utlrp
TIMESTAMP
--------------------------------------------------------------------------------
COMP_TIMESTAMP UTLRP_BGN  2014-08-19 10:24:07
DOC>
DOC>#
PL/SQL procedure successfully completed.
TIMESTAMP
--------------------------------------------------------------------------------
COMP_TIMESTAMP UTLRP_END  2014-08-19 10:24:11
DOC> The following query reports the number of objects that have compiled
.../...
DOC>
OBJECTS WITH ERRORS
-------------------
                  0
DOC> The following query reports the number of errors caught during
DOC> recompilation. If this number is non-zero, please query the error
DOC> messages in the table UTL_RECOMP_ERRORS to see if any of these errors
DOC> are due to misconfiguration or resource constraints that must be
DOC> fixed before objects can compile successfully.
DOC>#
ERRORS DURING RECOMPILATION
---------------------------
                          0
Function created.
PL/SQL procedure successfully completed.
Function dropped.
...Database user "SYS", database schema "APEX_040200", user# "98" 10:24:34
...Compiled 0 out of 3014 objects considered, 0 failed compilation 10:24:34
...271 packages
...263 package bodies
...452 tables
...11 functions
...16 procedures
...3 sequences
...457 triggers
...1320 indexes
...211 views
...0 libraries
...6 types
...0 type bodies
...0 operators
...0 index types
...Begin key object existence check 10:24:34
...Completed key object existence check 10:24:35
...Setting DBMS Registry 10:24:35
...Setting DBMS Registry Complete 10:24:35
...Exiting validate 10:24:35
PL/SQL procedure successfully completed.

Noter que tous les composants sont dans le bon status :

SQL>select comp_name ,version , status from dba_registry ;
COMP_NAME VERSION STATUS
------------------------------ ------------------------------ -----------
Oracle Database Vault 12.1.0.2.0 VALID
Oracle Application Express 4.2.5.00.08 VALID
Oracle Label Security 12.1.0.2.0 VALID
Spatial 12.1.0.2.0 VALID
Oracle Multimedia 12.1.0.2.0 VALID
Oracle Text 12.1.0.2.0 VALID
Oracle Workspace Manager 12.1.0.2.0 VALID
Oracle XML Database 12.1.0.2.0 VALID
Oracle Database Catalog Views 12.1.0.2.0 VALID
Oracle Database Packages and T 12.1.0.2.0 VALID
JServer JAVA Virtual Machine 12.1.0.2.0 VALID
Oracle XDK 12.1.0.2.0 VALID
Oracle Database Java Packages 12.1.0.2.0 VALID
OLAP Analytic Workspace 12.1.0.2.0 VALID
Oracle OLAP API 12.1.0.2.0 VALID
Oracle Real Application Cluste 12.1.0.2.0 OPTION OFF

Finalisation
Il reste à mettre en place le listener de la nouvelle version, l’utilisation de « netca » est le plus simple pour cela. Attention si vous positionnez un port d’écoute différent de la valeur standard 1521 à ne pas oublier le paramétrage de « local_listener » pour cdb1212, exemple avec un port d’écoute sur la valeur 1522 :
a) Ajout entrée dans le fichier tnsnames.ora :

LISTENER_CDB1212 =
  (ADDRESS = (PROTOCOL = TCP)(HOST = ele1ole6)(PORT = 1522))

b) Positionner local_listener pour la base de données

[oracle@ele1ole6 ~]$ . oraenv
ORACLE_SID = [cdb1212] ? cdb1212
The Oracle base remains unchanged with value /u01/app/oracle
[oracle@ele1ole6 ~]$ sqlplus / as sysdba
SQL*Plus: Release 12.1.0.2.0 Production on Wed Aug 20 09:10:32 2014
Copyright (c) 1982, 2014, Oracle.  All rights reserved.
Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
SQL> alter system set local_listener="LISTENER_CDB1212" ;
System altered.
SQL> show parameter local_listener
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
local_listener                       string      LISTENER_CDB1212

Bien qu’étant un peu déçu de ne pas avoir une procédure plus simple et surtout plus rapide, il y a quand même quelques avantages avec cette architecture, par exemple je peux très bien avoir la même base (tant qu’il n’y a pas de nouvelles données ou des modifications) sur deux containers avec des versions différentes, ce qui laisse le temps pour valider votre applicatif par exemple. Ce n’est pas ce que dit Oracle qui précise que l’on ne peut avoir deux pluggable database de même nom sur deux containeurs racine sur le même serveur. Le secret : Un listener différent pour chaque PDB, car c’est en fait le nom de service qui est identique si on différencie l’accès via un numéro de port d’écoute différent cela fonctionne très bien.
Pas de miracle pour cette fois, mais de nouvelles possibilités, comme celle de réaliser le « plugin » d’une base dans un container au travers d’un dblink, une autre histoire à vous faire partager.