<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Archives des Oracle Multitenant - dbi Blog</title>
	<atom:link href="https://www.dbi-services.com/blog/tag/oracle-multitenant/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dbi-services.com/blog/tag/oracle-multitenant/</link>
	<description></description>
	<lastBuildDate>Tue, 20 Jun 2023 08:39:04 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/cropped-favicon_512x512px-min-32x32.png</url>
	<title>Archives des Oracle Multitenant - dbi Blog</title>
	<link>https://www.dbi-services.com/blog/tag/oracle-multitenant/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Unplug/Plug PDB in TDE Environment</title>
		<link>https://www.dbi-services.com/blog/unplug-plug-pdb-in-tde-environment/</link>
					<comments>https://www.dbi-services.com/blog/unplug-plug-pdb-in-tde-environment/#comments</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Tue, 14 Mar 2023 15:56:22 +0000</pubDate>
				<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<category><![CDATA[Oracle TDE]]></category>
		<category><![CDATA[PDB clone]]></category>
		<category><![CDATA[Transparent data encryption]]></category>
		<category><![CDATA[Unplug/Plug PDB]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=23300</guid>

					<description><![CDATA[<p>By Mouhamadou Diaw In an oracle environment with Transparent Data Encryption (TDE) the transfer of PDB may require additional steps. In this blog we will see some steps which may be required when doing Unplug and Plug &#160;of pluggable database We plan to unplug the pluggable database PDB1 from PROD01 and plug it into CLNE02. [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/unplug-plug-pdb-in-tde-environment/">Unplug/Plug PDB in TDE Environment</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><strong>By Mouhamadou Diaw</strong></p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">In an oracle environment with Transparent Data Encryption (TDE) the transfer of PDB may require additional steps.</p>



<p class="wp-block-paragraph">In this blog we will see some steps which may be required when doing Unplug and Plug &nbsp;of pluggable database</p>



<p class="wp-block-paragraph">We plan to unplug the pluggable database PDB1 from PROD01 and plug it into CLNE02. Both container have TDE configured</p>



<p class="wp-block-paragraph">Source : PROD01&nbsp; with TDE</p>



<pre class="wp-block-code"><code>10:37:38 SQL&gt; show PDB1s

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB1$SEED                       READ ONLY  NO
         3 PDB1                            READ WRITE NO
10:37:43 SQL&gt;
</code></pre>



<p class="wp-block-paragraph">Target : CLNE02&nbsp;with TDE</p>



<pre class="wp-block-code"><code>10:37:05 SQL&gt; show PDB1s

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB1$SEED                       READ ONLY  NO
	 3 POD2                           READ WRITE NO
10:37:09 SQL&gt;
</code></pre>



<p class="wp-block-paragraph"><strong>Export the encryption key on the source PDB1</strong></p>



<p class="wp-block-paragraph">On the source container PROD01, we have to export the encryption key while connecting to PDB1.</p>



<p class="wp-block-paragraph">The option force is needed if AUTO LOGIN is used</p>



<pre class="wp-block-code"><code>SQL&gt; alter session set container=PDB1;

Session altered.


11:24:23 SQL&gt; ADMINISTER KEY MANAGEMENT EXPORT ENCRYPTION KEYS WITH SECRET "********" TO '/home/oracle/tde/export.p12' FORCE keystore IDENTIFIED BY ********;

keystore altered.

11:24:25 SQL&gt;
</code></pre>



<p class="wp-block-paragraph">Then verify that the key was exported.</p>



<pre class="wp-block-code"><code>11:24:25 SQL&gt; host ls -l /home/oracle/tde/export.p12
-rw-r--r--. 1 oracle oinstall 2612 Feb 28 11:24 /home/oracle/tde/export.p12

11:25:36 SQL&gt;
</code></pre>



<p class="wp-block-paragraph"><strong>Unplug PDB1 from PROD01</strong></p>



<p class="wp-block-paragraph">After exporting the key, we can now unplug &nbsp;PDB1</p>



<pre class="wp-block-code"><code>11:28:08 SQL&gt; show con_name

CON_NAME
------------------------------
CDB$ROOT

11:28:17 SQL&gt; alter pluggable database PDB1 close;

Pluggable database altered.

11:28:30 SQL&gt; show PDB1s

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB1$SEED                       READ ONLY  NO
         3 PDB1                           MOUNTED
         5 PDB1CLONE                      MOUNTED
11:28:36 SQL&gt;


11:28:36 SQL&gt; ALTER PLUGGABLE DATABASE PDB1 UNPLUG INTO '/home/oracle/PDB1.xml';

Pluggable database altered.

11:30:54 SQL&gt;
</code></pre>



<p class="wp-block-paragraph"><strong>Plug the database on CLNE02&nbsp;</strong></p>



<p class="wp-block-paragraph">Now plug &nbsp;PDB1 into CLNE02&nbsp;</p>



<pre class="wp-block-code"><code>SQL&gt; show parameter db_name

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_name                              string      CLNE02
SQL&gt; show con_name

CON_NAME
------------------------------
CDB$ROOT

SQL&gt; CREATE PLUGGABLE DATABASE PDB1 USING '/home/oracle/PDB1.xml';

Pluggable database created.

SQL&gt;
</code></pre>



<p class="wp-block-paragraph">At this level we can open PDB1 in CLNE02 with errors, which is normal as we need to import the encryption key</p>



<pre class="wp-block-code"><code>11:37:16 SQL&gt; alter pluggable database PDB1 open;

Warning: PDB1 altered with errors.       
11:37:34 SQL&gt;
</code></pre>



<p class="wp-block-paragraph"><strong>Import the key into PDB1 on CLNE02</strong></p>



<p class="wp-block-paragraph">Now let&#8217;s import the exported key into PDB1 on CLNE02. </p>



<p class="wp-block-paragraph">-Open PDB1 in R/W mode if not</p>



<pre class="wp-block-code"><code>13:13:18 SQL&gt; alter pluggable database PDB1 open;

Warning: PDB1 altered with errors.

13:13:35 SQL&gt; show PDB1s

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB1$SEED                       READ ONLY  NO
         3 POD2                           READ WRITE NO
         4 PDB1                           READ WRITE YES
</code></pre>



<p class="wp-block-paragraph">-Open the keystore on PDB if not</p>



<pre class="wp-block-code"><code>13:13:46 SQL&gt; alter session set container=PDB1;


13:16:55 SQL&gt; ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY ********;

keystore altered.

13:16:59 SQL&gt;
</code></pre>



<p class="wp-block-paragraph">-And then import the key</p>



<pre class="wp-block-code"><code>SQL&gt; ADMINISTER KEY MANAGEMENT IMPORT ENCRYPTION KEYS WITH SECRET ******** FROM '/home/oracle/tde/export.p12' IDENTIFIED BY ******** WITH BACKUP;


keystore altered.
</code></pre>



<p class="wp-block-paragraph">-On CLNE02, restart the pluggable database PDB1</p>



<pre class="wp-block-code"><code>SQL&gt; show con_name

CON_NAME
------------------------------
PDB1

13:18:52 SQL&gt; shutdown
Pluggable Database closed.

13:19:00 SQL&gt; startup
Pluggable Database opened.
</code></pre>



<p class="wp-block-paragraph">PDB1 should no longer return any error now when starting in open mode</p>



<pre class="wp-block-code"><code>SQL&gt; conn / as sysdba
Connected.
13:21:56 SQL&gt; alter pluggable database PDB1 close;

Pluggable database altered.

13:22:07 SQL&gt; alter pluggable database PDB1 open;

Pluggable database altered.

13:22:14 SQL&gt; alter pluggable database all save state;

Pluggable database altered.

</code></pre>



<p class="wp-block-paragraph">We can then validate on PDB1 that encrypted objects are accessible.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">Hope this blog may help</p>
<p>L’article <a href="https://www.dbi-services.com/blog/unplug-plug-pdb-in-tde-environment/">Unplug/Plug PDB in TDE Environment</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/unplug-plug-pdb-in-tde-environment/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>How to create an Oracle GoldenGate EXTRACT in Multitenant</title>
		<link>https://www.dbi-services.com/blog/how-to-create-an-oracle-goldengate-extract-in-multitenant/</link>
					<comments>https://www.dbi-services.com/blog/how-to-create-an-oracle-goldengate-extract-in-multitenant/#respond</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Sat, 23 Apr 2022 10:42:35 +0000</pubDate>
				<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[golden gate]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/how-to-create-an-oracle-goldengate-extract-in-multitenant/</guid>

					<description><![CDATA[<p>Create an EXTRACT process into container database has some specificity : From the CDB$ROOT, create a common user and configure the database to be ready to extract data via GoldenGate: SQL&#62; create user c##gg_admin identified by "*****" default tablespace goldengate temporary tablespace temp; User created. SQL&#62; SQL&#62; alter user c##gg_admin quota unlimited on goldengate; User [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-create-an-oracle-goldengate-extract-in-multitenant/">How to create an Oracle GoldenGate EXTRACT in Multitenant</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Create an EXTRACT process into container database has some specificity :</p>
<p>From the CDB$ROOT, create a common user and configure the database to be ready to extract data via GoldenGate:</p>
<pre class="brush: bash; gutter: true; first-line: 1">SQL&gt; create user c##gg_admin identified by "*****" default tablespace goldengate temporary tablespace temp;

User created.

SQL&gt;

SQL&gt; alter user c##gg_admin quota unlimited on goldengate;

User altered.

SQL&gt;


SQL&gt; grant create session, connect,resource,alter system, select any dictionary, flashback any table to c##gg_admin container=all;

Grant succeeded.

SQL&gt;

SQL&gt; exec dbms_goldengate_auth.grant_admin_privilege(grantee =&gt; 'c##gg_admin',container=&gt;'all');

PL/SQL procedure successfully completed.

SQL&gt; alter user c##gg_admin set container_data=all container=current;

User altered.

SQL&gt;

SQL&gt; grant alter any table to c##gg_admin container=ALL;

Grant succeeded.

SQL&gt;

alter system set enable_goldengate_replication=true scope=both;


SQL&gt; alter database force logging;


SQL&gt; alter pluggable database add supplemental log data;

Pluggable database altered.

SQL&gt;

</pre>
<p>Add the schematrandata for the schema concerned:</p>
<pre class="brush: bash; gutter: true; first-line: 1">GGSCI (vmld-01726 as c##gg_admin@MYCDB) 3&gt; add schematrandata schema_source

2022-04-13 18:06:55  INFO    OGG-01788  SCHEMATRANDATA has been added on schema "schema_source".

2022-04-13 18:06:55  INFO    OGG-01976  SCHEMATRANDATA for scheduling columns has been added on schema "schema_source".

2022-04-13 18:06:55  INFO    OGG-10154  Schema level PREPARECSN set to mode NOWAIT on schema "schema_source".

2022-04-13 18:07:00  INFO    OGG-10471  ***** Oracle Goldengate support information on table schema_source.ZZ_DUMMY *****
Oracle Goldengate support native capture on table schema_source.ZZ_DUMMY.
Oracle Goldengate marked following column as key columns on table schema_source.ZZ_DUMMY: SCN, D, COMMENT_TXT
No unique key is defined for table schema_source.ZZ_DUMMY.

2022-04-13 18:07:00  INFO    OGG-10471  ***** Oracle Goldengate support information on table schema_source.ZZ_DUMMY2 *****
Oracle Goldengate support native capture on table schema_source.ZZ_DUMMY2.
Oracle Goldengate marked following column as key columns on table schema_source.ZZ_DUMMY2: SCN, D, COMMENT_TXT
No unique key is defined for table schema_source.ZZ_DUMMY2.

2022-04-13 18:07:00  INFO    OGG-10471  ***** Oracle Goldengate support information on table schema_source.ZZ_SURVEILLANCE *****
Oracle Goldengate support native capture on table schema_source.ZZ_SURVEILLANCE.
Oracle Goldengate marked following column as key columns on table schema_source.ZZ_SURVEILLANCE: I.

2022-04-13 18:07:00  INFO    OGG-10471  ***** Oracle Goldengate support information on table schema_source.ZZ_SURVEILLANCE_COPY *****
Oracle Goldengate support native capture on table schema_source.ZZ_SURVEILLANCE_COPY.
Oracle Goldengate marked following column as key columns on table schema_source.ZZ_SURVEILLANCE_COPY: I, SURV_DATE, ELLAPSED_1, ELLAPSED_2, CLIENT_HOST, CLIENT_TERMINAL, OS_USER, CLIENT_PROGRAM, INFO
No unique key is defined for table schema_source.ZZ_SURVEILLANCE_COPY.

GGSCI (vmld-01726 as c##gg_admin@MYCDB/CDB$ROOT) 3&gt; dblogin userid c##gg_admin@MYPDB password xxxx
Successfully logged into database.

GGSCI (vmld-01726 as c##gg_admin@MYCDB) 4&gt; info schematrandata schema_source

2022-04-13 18:32:43  INFO    OGG-06480  Schema level supplemental logging, excluding non-validated keys, is enabled on schema "schema_source".

2022-04-13 18:32:43  INFO    OGG-01980  Schema level supplemental logging is enabled on schema "schema_source" for all scheduling columns.

2022-04-13 18:32:43  INFO    OGG-10462  Schema "schema_source" have 4 prepared tables for instantiation.

GGSCI (vmld-01726 as c##gg_admin@MYCDB) 5&gt;
</pre>
<p>Create a new alias connection to the container database and register the extract, the extract must be registered into the root container (CDB$ROOT) even the data to capture are from the PDB:</p>
<pre class="brush: bash; gutter: true; first-line: 1">GGSCI (myserver) 10&gt; alter credentialstore add user c##gg_admin@MYCDB_X1 alias ggadmin_exacc
Password:

Credential store altered.

GGSCI (myserver) 11&gt; dblogin useridalias ggadmin
Successfully logged into database CDB$ROOT.

GGSCI (myserver as c##gg_admin@MYCDB/CDB$ROOT) 2&gt;

GGSCI (myserver as c##gg_admin@MYCDB/CDB$ROOT) 2&gt; register extract E3 database container (MYPDB)

2022-04-13 18:31:19  INFO    OGG-02003  Extract E3 successfully registered with database at SCN 3386436450080


GGSCI (myserver as c##gg_admin@MYCDB/CDB$ROOT) 3&gt;
</pre>
<p>Save the SCN &#8211;&gt; 3386436450080</p>
<p>Create the EXTRACT, connected on the CDB:</p>
<pre class="brush: bash; gutter: true; first-line: 1">[oracle@myserver:/u01/app/oracle/product/19.1.0.0.4/gg_1]$ mkdir -p /u01/gs_x/ogg/

GGSCI (myserver) 7&gt; add extract E3, integrated tranlog, begin now
EXTRACT (Integrated) added.


GGSCI (myserver) 8&gt; INFO ALL

Program     Status      Group       Lag at Chkpt  Time Since Chkpt

MANAGER     RUNNING
EXTRACT     STOPPED     E3		    00:00:00      00:00:06


GGSCI (myserver) 9&gt;


GGSCI (myserver) 9&gt; add exttrail /u01/gs_x/ogg/gz, extract E3
EXTTRAIL added.


GGSCI (myserver) 2&gt; edit param E3


GGSCI (myserver as c##gg_admin@MYCDB/CDB$ROOT) 13&gt; edit param E3
Extract E3
useridalias ggadmin
Exttrail /u01/gs_x/ogg/gz
LOGALLSUPCOLS
UPDATERECORDFORMAT COMPACT
DDL  &amp;
INCLUDE MAPPED OBJNAME MYPDB.SCHEMA.*
Sequence MYPDB.SCHEMA.*;
Table MYPDB.SCHEMA.* ;
</pre>
<p>The parameter Table must be prefixed by the Pdb Name</p>
<p>&nbsp;</p>
<p>Start the Extract always from the CDB$ROOT:</p>
<pre class="brush: bash; gutter: true; first-line: 1">GGSCI (myserver as c##gg_admin@MY_CDB/CDB$ROOT) 12&gt; START EXTRACT E3 atcsn 3386436450080

Sending START request to MANAGER ...
EXTRACT E3 starting


GGSCI (myserver as c##gg_admin@MYCDB/CDB$ROOT) 15&gt; info all

Program     Status      Group       Lag at Chkpt  Time Since Chkpt

MANAGER     RUNNING
EXTRACT     RUNNING     E3      00:00:05      00:00:03
</pre>
<p>Check the extract is running.<br />
Now you are ready to create the Pump process, do the Initial Load and create the replicat process on the target.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-create-an-oracle-goldengate-extract-in-multitenant/">How to create an Oracle GoldenGate EXTRACT in Multitenant</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/how-to-create-an-oracle-goldengate-extract-in-multitenant/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Magic Upgrade to 21c with Replay Upgrade Feature</title>
		<link>https://www.dbi-services.com/blog/magic-upgrade-to-21c-with-replay-upgrade-feature/</link>
					<comments>https://www.dbi-services.com/blog/magic-upgrade-to-21c-with-replay-upgrade-feature/#respond</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Mon, 20 Sep 2021 05:09:41 +0000</pubDate>
				<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[CDB]]></category>
		<category><![CDATA[Oracle 21C]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<category><![CDATA[Upgrade]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/magic-upgrade-to-21c-with-replay-upgrade-feature/</guid>

					<description><![CDATA[<p>Last week I did some upgrade tests from 19c to 21c. The plan was to copy a 19c PDB to a CDB 21c using Online Clone and then upgrade it to 21c. I have done this many times for 19c as well and just wanted to verify that it still worked with 21c. Clone PDB [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/magic-upgrade-to-21c-with-replay-upgrade-feature/">Magic Upgrade to 21c with Replay Upgrade Feature</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Last week I did some upgrade tests from 19c to 21c. The plan was to copy a 19c PDB to a CDB 21c using Online Clone and then upgrade it to 21c. I have done this many times for 19c as well and just wanted to verify that it still worked with 21c.</p>
<p><strong>Clone PDB to 21c CDB</strong><br />
Since it is a LAB, I gave the user SYSTEM in the CDB19c the appropriate duplicate permissions and then I created from the CDB 21c the appropriate DB link for the clone</p>
<pre class="brush: sql; gutter: true; first-line: 1">SQL*Plus: Release 19.0.0.0.0 - Production on Fri Sep 17 13:17:06 2021
Version 19.3.0.0.0
Copyright (c) 1982, 2019, Oracle.&nbsp; All rights reserved.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
SQL&gt; grant sysoper, create pluggable database to system container=all;
Grant succeeded.
-- create db link for clone
oracle@ora21:/home/oracle/ [cdb01] sqh
SQL*Plus: Release 21.0.0.0.0 - Production on Fri Sep 17 13:18:31 2021
Version 21.3.0.0.0
Copyright (c) 1982, 2021, Oracle.&nbsp; All rights reserved.
Connected to:
Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
SQL&gt; create database link cdb19c2 connect to system identified by manager using 'cdb19c2';
Database link created.</pre>
<p class="brush: sql; gutter: true; first-line: 1">Now I clone the 19c pdb into the 21c CDB over the dblink</p>
<pre class="brush: sql; gutter: true; first-line: 1">SQL&gt; create pluggable database pdb19c_copy from pdb19c@cdb19c2;
Pluggable database created.</pre>
<p><strong>Upgrade PDB to 21c</strong><br />
So the next step is to open the PDB and afterwards execute the upgrade script for upgrading the catalog of the pdb to 21c.</p>
<pre class="brush: sql; gutter: true; first-line: 1">SQL&gt; alter pluggable database pdb19c_copy open;</pre>
<p>And now with Oracle 21c the magic part happens, or i would say, that a autonoumous Feature found the way to the on prem word.<br />
After a few seconds the opening of the PDB was still not finished, but no error was displayed on the prompt, I looked into the alert log of the database to see what exactly the problem was. When I looked through the entries, I was quite surprised to see that my PDB was in the middle of an upgrade to 21c without Oracle giving me a choice.<br />
A look on the pdb state confirmed what oracle tells me in the alert.log</p>
<pre class="brush: sql; gutter: true; first-line: 1">SQL&gt; show pdbs</pre>
<pre class="brush: sql; gutter: true; first-line: 1">CON_ID     CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
2          PDB$SEED                       READ ONLY NO
3          PDB19c_COPY                    MIGRATE   YES</pre>
<p>Here you can see a very interesting snipped of the alert.log</p>
<pre class="brush: bash; gutter: false; first-line: 1">PDB19C_COPY(6):Starting Upgrade on PDB Open
2021-09-17T13:21:09.305411+02:00
PDB19C_COPY(6):alter pluggable database application APP$CDB$CATALOG begin install '19.0.0.0.0' on error capture
PDB19C_COPY(6):ORA-65229 signalled during: alter pluggable database application APP$CDB$CATALOG begin install '19.0.0.0.0' on error capture...
PDB19C_COPY(6):alter pluggable database application APP$CDB$CATALOG end install '19.0.0.0.0'
PDB19C_COPY(6):Completed: alter pluggable database application APP$CDB$CATALOG end install '19.0.0.0.0'
PDB19C_COPY(6):alter pluggable database application APP$CDB$CATALOG begin upgrade
'19.0.0.0.0' to '21.0.0.0.0.partial' on error capture
PDB19C_COPY(6):Completed: alter pluggable database application APP$CDB$CATALOG begin upgrade
'19.0.0.0.0' to '21.0.0.0.0.partial' on error capture</pre>
<p class="brush: bash; gutter: false; first-line: 1">On the first line you can see, that oracle has started to upgrade my pdb immediate after i issued the open command.<br />
The next lines are very interesting. If you look at the statements beginning with &#8220;alter pluggable database application &#8230; &#8221; you can see, that oracle uses Multitenant application container functionality to upgrade the pdb to the new version. It seams, that they manage different versions of the cdb catalog as applications.</p>
<p class="brush: bash; gutter: false; first-line: 1">With the next command oracle tries to upgrade the application <em>APP$CDB$CATALOG&nbsp;</em>to 19.0.0.0.0 what&#8217;s in fact the data dictionary version. This gives an error. I can&#8217;t confirm it, but in context of application containers the begin install command always install the newest application version in the pdb. In this context i would expect, that oracle tries to apply the newest 19c patches to my pdb but this failed for some reason.</p>
<pre class="brush: bash; gutter: false; first-line: 1">DB19C_COPY(6):alter pluggable database application APP$CDB$CATALOG begin install '19.0.0.0.0' on error capture 
PDB19C_COPY(6):ORA-65229 signalled during: alter pluggable database application APP$CDB$CATALOG begin install '19.0.0.0.0' on error capture...</pre>
<p>Then the next stage is upgrade of the catalog from version 19.0.0.0.0 to 21.0.0.0.0.partial. The syntax is exactly the same if you upgrade an application pdb to a new version.</p>
<pre class="brush: bash; gutter: false; first-line: 1">PDB19C_COPY(6):alter pluggable database application APP$CDB$CATALOG begin upgrade 
'19.0.0.0.0' to '21.0.0.0.0.partial' on error capture 
PDB19C_COPY(6):Completed: alter pluggable database application APP$CDB$CATALOG begin upgrade '19.0.0.0.0' to '21.0.0.0.0.partial' on error capture</pre>
<p>And in the last step oracle performs the upgrade from the version 21.0.0.0.0.partial to 21.0.0.0.0. I have no idea why this additional step to 21.0.0.0.0.partial is necessary.</p>
<pre class="brush: bash; gutter: false; first-line: 1">PDB19C_COPY(6):alter pluggable database application APP$CDB$CATALOG begin upgrade '21.0.0.0.0.partial' to '21.0.0.0.0' on error capture
PDB19C_COPY(6):Completed: alter pluggable database application APP$CDB$CATALOG begin upgrade '21.0.0.0.0.partial' to '21.0.0.0.0' on error capture</pre>
<p>Finally the Upgrade of the Application APP$CDB$CATALOG is done and oracle open the pdb with the new version 21c</p>
<pre class="brush: bash; gutter: false; first-line: 1">PDB19C_COPY(6):alter pluggable database application APP$CDB$CATALOG end upgrade
Pluggable database PDB19C_COPY opened read write</pre>
<p class="brush: bash; gutter: false; first-line: 1"><strong>Reverse Engineering</strong><br />
To see if my assumptions are correct, we can query the most important app container views in the CDB and the PDB and see what is in there.<br />
Let&#8217;s check the applications that are registered in the cdb$root</p>
<pre class="brush: sql; gutter: true; first-line: 1">SQL&gt; select app_name, app_id, app_version, app_capture_module from dba_applications;

APP_NAME                           APP_ID APP_VERSION                    APP_CAPTURE_MODULE
------------------------------ ---------- ------------------------------ ----------------------------------------------------------------
APP$CDB$SYSTEM                          2 1.0                            catcon(pid=26657)
APP$CDB$PDBONLY$NCDBTOPDB              21 1.0                            catcon(pid=26657)
APP$CDB$CATALOG                4294967293 21.0.0.0.0                     catcon(pid=24698)
APP$CDB$CATALOG                4294967292 21.0.0.0.0                     catcon(pid=8832)
APP$CDB$CATALOG                4294967291 21.0.0.0.0                     catcon(pid=1120)</pre>
<p class="brush: sql; gutter: true; first-line: 1">We can see that we have an application <em>APP$CDB$CATALOG </em>that holds the upgrade scripts to upgrade a pdb to 21c and also we have a application<em> APP$CDB$PDBONLY$NCDBTOPDB&nbsp;</em>which is responsible auto upgrading a non cdb to 21c and additionally including the scripts for the conversion from noncdb to pdb<br />
And if we have a look at the app versions, we can see, that oracle stores the catalog upgrade scripts from 12.2 onwards. This allows to auto upgrade the pdb to 21c for all versions beginning with 12.2</p>
<pre class="brush: sql; gutter: false; first-line: 1">SQL&gt; select app_name, app_version from dba_app_versions;

APP_NAME                                 APP_VERSION
---------------------------------------- ------------------------------
APP$CDB$SYSTEM                           1.0
APP$CDB$PDBONLY$NCDBTOPDB                1.0.upgmode
APP$CDB$PDBONLY$NCDBTOPDB                1.0
APP$CDB$CATALOG                          12.2.0.1.0
APP$CDB$CATALOG                          21.0.0.0.0.partial
APP$CDB$CATALOG                          21.0.0.0.0
APP$CDB$CATALOG                          18.0.0.0.0
APP$CDB$CATALOG                          21.0.0.0.0.partial
APP$CDB$CATALOG                          21.0.0.0.0
APP$CDB$CATALOG                          19.0.0.0.0
APP$CDB$CATALOG                          21.0.0.0.0.partial
APP$CDB$CATALOG                          21.0.0.0.0</pre>
<p><strong>Replay Upgrade Feature<br />
</strong>To learn a bit more about what exactly Oracle is doing here, I talked to Mike Dietrich. He confirmed my experience and told me that the feature implemented with 21c is called &#8220;Replay Upgrade&#8221;. The process to upgrade a PDB is stored in the CDB from 21c on and as soon as a PDB with a lower version is opened, this recorded process is automatically replayed to upgrade the PDB. This new feature is even rudimentarily documented <a href="https://docs.oracle.com/en/database/oracle/oracle-database/21/upgrd/adopting-non-cdb-pbs-using-capture-upgrade.html#GUID-77BA4A1D-447F-46FB-B3EE-C58B8546FEB0">here.</a><br />
A big disadvantage with this automatic upgrade process is that no single upgrade log is created for it. In my lab, which consists of an empty PDB, it worked without problems, but for real databases, this is not recommended, since no analysis can be done in case of an error. Also, the replay upgrade does not update the Timezone file to the latest version. This must be done manually afterwards. This was also confirmed by Mike Dietrich and therefore for an upgrade you should always work with the &#8220;autoupgrade tool&#8221;, which is officially recommended by Oracle.<br />
Based on the documentation, the replay feature in the CDB can also be disabled. I would recommend this, because you have back the full control over upgrades afterwards.</p>
<pre class="brush: sql; gutter: true; first-line: 1">ALTER DATABASE UPGRADE SYNC OFF</pre>
<p>To be complete, the feature can be reactivated with the following command.</p>
<pre class="brush: sql; gutter: true; first-line: 1">ALTER DATABASE UPGRADE SYNC ON</pre>
<p><strong>Container Compatibility<br />
</strong>The Replay Upgrade feature only applies if exactly the same components are installed in the Source CDB and the Target CDB. If this is not the case, the PDB clone cannot be created at all and terminates with an error.<strong><br />
</strong></p>
<pre class="brush: sql; gutter: false; first-line: 1">SQL&gt; create pluggable database pdb19_copy from pdb19@cdb19c;
create pluggable database pdb19_copy from pdb19@cdb19c
*
ERROR at line 1:
ORA-65346: The PDB version is lower and components (APS, CONTEXT, DV, OLS,
ORDIM, SDO, XOQ) are missing in CDB.</pre>
<p>L’article <a href="https://www.dbi-services.com/blog/magic-upgrade-to-21c-with-replay-upgrade-feature/">Magic Upgrade to 21c with Replay Upgrade Feature</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/magic-upgrade-to-21c-with-replay-upgrade-feature/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Oracle disables your multitenant option when you run on EC2</title>
		<link>https://www.dbi-services.com/blog/oracle-disables-your-multitenant-option-when-you-run-on-ec2/</link>
					<comments>https://www.dbi-services.com/blog/oracle-disables-your-multitenant-option-when-you-run-on-ec2/#respond</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Thu, 26 Mar 2020 21:56:41 +0000</pubDate>
				<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CDB]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[max_pdbs]]></category>
		<category><![CDATA[multitenant]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<category><![CDATA[PDB]]></category>
		<category><![CDATA[Pluggable Databases]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/oracle-disables-your-multitenant-option-when-you-run-on-ec2/</guid>

					<description><![CDATA[<p>I have installed Oracle 19.6 on an EC2 for our Multitenant Workshop training. And of course, during the workshop we create a lot of PDBs. If you don&#8217;t have paid for the Enterprise Edition plus the Multitenant Option you can create at most 3 pluggable database. But with this option you can create up to [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/oracle-disables-your-multitenant-option-when-you-run-on-ec2/">Oracle disables your multitenant option when you run on EC2</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I have installed Oracle 19.6 on an EC2 for our <a href="https://www.dbi-services.com/trainings/oracle-multitenant-database-administration/" rel="noopener noreferrer" target="_blank">Multitenant Workshop</a> training. And of course, during the workshop we create a lot of PDBs. If you don&#8217;t have paid for the Enterprise Edition plus the Multitenant Option you can create at most 3 pluggable database. But with this option you can create up to 252 pluggable databases. Does it worth the price, which according to the <a href="https://www.oracle.com/assets/technology-price-list-070617.pdf" rel="noopener noreferrer" target="_blank">public price list</a> is USD 47,500 + 17,500 per processor, which means per-core because Oracle doesn&#8217;t count the core factor when your Intel processors are in AWS Cloud (according to the <a href="https://www.oracle.com/assets/cloud-licensing-070579.pdf" rel="noopener noreferrer" target="_blank">Authorized Cloud Environments</a> paper)? Probably not because Oracle detects where you run and bridles some features depending whether you are on the Dark or the Light Side of the public cloud (according to their criteria of course).<br />
<span id="more-13815"></span><br />
At one point I have 3 pluggable databases in my CDB:</p>
<pre><code>
SQL&gt; show pdbs
   CON_ID     CON_NAME    OPEN MODE    RESTRICTED
_________ ____________ ____________ _____________
        2 PDB$SEED     READ ONLY    NO
        3 CDB1PDB01    MOUNTED
        4 CDB1PDB03    MOUNTED
        5 CDB1PDB02    MOUNTED
</code></pre>
<p>I want to create a 4th one:</p>
<pre><code>
SQL&gt; create pluggable database CDB1PDB04 from CDB1PDB03;

create pluggable database CDB1PDB04 from CDB1PDB03
                          *
ERROR at line 1:
ORA-65010: maximum number of pluggable databases created
</code></pre>
<p>It fails. The maximum number of pluggable databases is defined by MAX_PDBS, but I defined nothing in my SPFILE:</p>
<pre><code>
SQL&gt; show spparameter max_pdbs
SID NAME     TYPE    VALUE
--- -------- ------- -----
*   max_pdbs integer
</code></pre>
<p>I thought that the default was 4098 (which is incorrect anyway as you cannot create more than 4096) but it is actually 5 here:</p>
<pre><code>
SQL&gt; show parameter max_pdbs
NAME     TYPE    VALUE
-------- ------- -----
max_pdbs integer 5
</code></pre>
<p>Ok&#8230; this parameter is supposed to count the number of user pluggable databases (the ones with CON_ID&gt;2) and I have 3 of them here. The limit is 5 and I have an error mentioning that I&#8217;ve reached the limit. That&#8217;s not the first time I see <a href="https://www.dbi-services.com/blog/oracle-12cr2-max_pdbs/" rel="noopener noreferrer" target="_blank">wrong maths</a> with this parameter. But there&#8217;s worse as I cannot change it:</p>
<pre><code>
SQL&gt; alter system set max_pdbs=6;

alter system set max_pdbs=6
 *
ERROR at line 1:
ORA-02097: parameter cannot be modified because specified value is invalid
ORA-65334: invalid number of PDBs specified
</code></pre>
<p>I can change it in the SPFILE but it doesn&#8217;t help me to create more pluggable databases:</p>
<pre><code>
SQL&gt; alter system set max_pdbs=200 scope=spfile;

System altered.

SQL&gt; startup force;

Total System Global Area   2147482744 bytes
Fixed Size                    9137272 bytes
Variable Size               587202560 bytes
Database Buffers           1543503872 bytes
Redo Buffers                  7639040 bytes
Database mounted.
Database opened.

SQL&gt; show parameter max_pdbs
NAME     TYPE    VALUE
-------- ------- -----
max_pdbs integer 200

SQL&gt; create pluggable database CDB1PDB04 from CDB1PDB03;

create pluggable database CDB1PDB04 from CDB1PDB03
                          *
ERROR at line 1:
ORA-65010: maximum number of pluggable databases created
</code></pre>
<p>Something bridles me. There&#8217;s a MOS Note <a href="https://support.oracle.com/epmos/faces/DocContentDisplay?id=2328600.1" rel="noopener noreferrer" target="_blank">ORA-65010 When Oracle Database Hosted on AWS Cloud (Doc ID 2328600.1)</a> about the same problem but that&#8217;s in 12.1.0.2 (before MAX_PDBS was introduced) which is supposed to be fixed in AUG 2017 PSU. But here I am 3 years later in 19.6 (the January 2020 Release Update for the latest version available on-premises).</p>
<p>So, Oracle limits the number of pluggable databases when we are on a public cloud provider which is not the Oracle Public Cloud. This limitation is not documented in the <a href="https://docs.oracle.com/en/database/oracle/oracle-database/19/dblic/Licensing-Information.html#GUID-0F9EB85D-4610-4EDF-89C2-4916A0E7AC87" rel="noopener noreferrer" target="_blank">licensing documentation</a> which mentions 252 as the Enterprise Edition limit, and I see nothing about &#8220;Authorized Cloud Environments&#8221; limitations for this item. This, and the fact that it can come and go with Release Updates put customers at risk when running on AWS EC2: financial risk and availability risk. I think there are only two choices, on long term, when you want to run your database on a cloud: go to Oracle Cloud or leave for another Database.</p>
<p>How does the Oracle instance know on which public cloud you run? All cloud platforms provide some metadata through HTTP api. I have straced all sendto() and recvfrom() system calls when starting the instance:</p>
<pre><code>
strace -k -e trace=recvfrom,sendto -yy -s 1000 -f -o trace.trc sqlplus / as sysdba &lt;&lt;&lt;&#039;startup force&#039;
</code></pre>
<p>And I searched for Amazon and AWS here:</p>
<p><a href="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/Annotation-2020-03-26-222846.jpg"><img fetchpriority="high" decoding="async" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/Annotation-2020-03-26-222846.jpg" alt="" width="1024" height="850" class="aligncenter size-large wp-image-38535" /></a></p>
<p>This is clear: the instance has a function to detect the cloud provider (kgcs_clouddb_provider_detect) when initializing the SGA in a multitenant architecture (kpdbInitSga) with the purpose of detecting non-oracle clouds (kscs_is_non_oracle_cloud). This queries the AWS metadata (documented on <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html" rel="noopener noreferrer" target="_blank">Retrieving Instance Metadata</a>):</p>
<pre><code>
[oracle@ora-cdb-1 ~]$ curl http://169.254.169.254/latest/meta-data/services/domain
amazonaws.com/
</code></pre>
<p>When Oracle software sees the name of the enemy in the domain name amazonaws.com, it sets an internal limit for the number of pluggable databases that overrides the MAX_PDBS setting. Ok, I don&#8217;t need this metadata and I&#8217;m root on EC2 so my simple workaround is to block this metadata API:</p>
<pre><code>
[root@ora-cdb-1 ~]# iptables -A OUTPUT -d 169.254.169.254  -j REJECT
[root@ora-cdb-1 ~]# iptables -L
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
REJECT     udp  --  anywhere             10.0.0.2             udp dpt:domain reject-with icmp-port-unreachable
REJECT     all  --  anywhere             10.0.0.2             reject-with icmp-port-unreachable
</code></pre>
<p>Then restart the instance and it works: I can set or reset MAX_PDBS and create more pluggable databases.</p>
<p>I can remove the rule</p>
<pre><code>
[root@ora-cdb-1 ~]# iptables -D OUTPUT -d 169.254.169.254  -j REJECT
</code></pre>
<p>If, for watever reason I want to revert back.</p>
<p>Finally, because they had <a href="https://support.oracle.com/epmos/faces/DocContentDisplay?id=2538172.1" rel="noopener noreferrer" target="_blank">many bugs</a> with the MAX_PDBS soft limit, there&#8217;s a parameter to disable it and this disables also the hard limit:</p>
<pre><code>
SQL&gt; alter system set "_cdb_disable_pdb_limit"=true scope=spfile;
System altered.
</code></pre>
<p>Thanks to Mauricio Melnik for the heads-up on that:</p>
<blockquote class="twitter-tweet" data-width="500" data-dnt="true">
<p lang="en" dir="ltr">MOS 2538172.1  _cdb_disable_pdb_limit=true &#8230;. maybe?</p>
<p>&mdash; MM (@maurimelnik) <a href="https://twitter.com/maurimelnik/status/1243298784764932097?ref_src=twsrc%5Etfw">March 26, 2020</a></p></blockquote>
<p><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p>
<p>However, with this parameter you cannot control anymore the maximum number of PDBs so don&#8217;t forget to monitor your AUX_COUNT in DBA_FEATURE_USAGE_STATISTICS. </p>
<p>Here was my discovery when preparing the multitenant workshop lab environment. Note that given the current situation where everybody works from home when possible, we are ready to give this training full of hands-on exercises though Microsoft Teams and AWS EC2 virtual machines. Two days to be comfortable when moving to CDB architecture, which is what should be done this year when you plan to stay with Oracle Database for the future versions.</p>
<h3>Update 27-MAR-2020</h3>
<p>In order not to sound too negative here, this limit on AWS platforms has been removed in the past and this may be a bug re-introduced with the change from 1 to 3 PDBs in Standard Edition.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/oracle-disables-your-multitenant-option-when-you-run-on-ec2/">Oracle disables your multitenant option when you run on EC2</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/oracle-disables-your-multitenant-option-when-you-run-on-ec2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Multitenant, PDB, &#8216;save state&#8217;, services and standby databases</title>
		<link>https://www.dbi-services.com/blog/multitenant-pdb-save-state-services-and-standby-databases/</link>
					<comments>https://www.dbi-services.com/blog/multitenant-pdb-save-state-services-and-standby-databases/#comments</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Mon, 05 Feb 2018 20:12:44 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CDB]]></category>
		<category><![CDATA[Data Guard]]></category>
		<category><![CDATA[multitenant]]></category>
		<category><![CDATA[Oracle 12c]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<category><![CDATA[PDB]]></category>
		<category><![CDATA[Pluggable Databases]]></category>
		<category><![CDATA[services]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/multitenant-pdb-save-state-services-and-standby-databases/</guid>

					<description><![CDATA[<p>By Franck Pachot . Creating &#8211; and using &#8211; your own services has always been the recommendation. You can connect to a database without a service name, though the instance SID, but this is not what you should do. Each database registers its db_unique_name as a service, and you can use it to connect, but [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/multitenant-pdb-save-state-services-and-standby-databases/">Multitenant, PDB, &#8216;save state&#8217;, services and standby databases</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>By Franck Pachot</h2>
<p>.<br />
Creating &#8211; and using &#8211; your own services has always been the recommendation. You can connect to a database without a service name, though the instance SID, but this is not what you should do. Each database registers its db_unique_name as a service, and you can use it to connect, but it is always better to create your own application service(s). In multitenant, each PDB registers its name as a service, but the recommendation is still there: create your own services, and connect with your services.<br />
I&#8217;ll show in this blog post what happens if you use the PDB name as a service and the standby database registers to the same listener as the primary database. Of course, you can workaround the non-unique service names by registering to different listeners. But this just hides the problem. The main reason to use services is to be independent from physical attributes, so being forced to assign a specific TCP/IP port is not better than using an instance SID.<br />
<span id="more-10807"></span><br />
I have the primary (CDB1) and standby (CDB2) databases registered to the default local listener:</p>
<pre><code>
LSNRCTL for Linux: Version 12.2.0.1.0 - Production on 03-FEB-2018 23:11:23
&nbsp;
Copyright (c) 1991, 2016, Oracle.  All rights reserved.
&nbsp;
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias                     LISTENER
Version                   TNSLSNR for Linux: Version 12.2.0.1.0 - Production
Start Date                02-FEB-2018 09:32:30
Uptime                    1 days 13 hr. 38 min. 52 sec
Trace Level               off
Security                  ON: Local OS Authentication
SNMP                      OFF
Listener Parameter File   /u01/app/oracle/product/12.2.0/dbhome_1/network/admin/listener.ora
Listener Log File         /u01/app/oracle/diag/tnslsnr/VM122/listener/alert/log.xml
Listening Endpoints Summary...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=VM122)(PORT=5501))(Presentation=HTTP)(Session=RAW))
Services Summary...
Service "627f7512a0452fd4e0537a38a8c055c0" has 2 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "CDB1" has 1 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1XDB" has 1 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1_CFG" has 2 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "CDB1_DGB" has 1 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
Service "CDB1_DGMGRL" has 1 instance(s).
  Instance "CDB1", status UNKNOWN, has 1 handler(s) for this service...
Service "CDB2" has 1 instance(s).
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "CDB2XDB" has 1 instance(s).
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "CDB2_DGB" has 1 instance(s).
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "CDB2_DGMGRL" has 1 instance(s).
  Instance "CDB2", status UNKNOWN, has 1 handler(s) for this service...
Service "pdb1" has 2 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
  Instance "CDB2", status READY, has 1 handler(s) for this service...
The command completed successfully
</code></pre>
<p>Look at service &#8216;pdb1&#8217;, which is the name for my PDB. Connecting to //localhost:1521/PDB1 can connect you randomly to CDB1 (the primary database) or CDB2 (the standby database). </p>
<p>Here is an example, connecting several times to the PDB1 service:</p>
<pre><code>
[oracle@VM122 ~]$ for i in {1..5} ; do sqlplus -L -s sys/oracle@//localhost/pdb1 as sysdba &lt;&lt;&lt; &#039;select name,open_mode,instance_name from v$instance , v$database;&#039;; done
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ WRITE           CDB1
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ ONLY WITH APPLY CDB2
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ WRITE           CDB1
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ ONLY WITH APPLY CDB2
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ WRITE           CDB1
</code></pre>
<p>I was connected at random to CDB1 or CDB2.</p>
<p>As an administrator, you know the instance names and you can connect to the one you want with: //localhost:1521/PDB1/CDB1 or //localhost:1521/PDB1/CDB2:</p>
<pre><code>
[oracle@VM122 ~]$ for i in {1..3} ; do sqlplus -L -s sys/oracle@//localhost/pdb1/CDB1 as sysdba &lt;&lt;&lt; &#039;select name,open_mode,instance_name from v$instance , v$database;&#039;; done
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ WRITE           CDB1
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ WRITE           CDB1
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ WRITE           CDB1
&nbsp;
[oracle@VM122 ~]$ for i in {1..3} ; do sqlplus -L -s sys/oracle@//localhost/pdb1/CDB2 as sysdba &lt;&lt;&lt; &#039;select name,open_mode,instance_name from v$instance , v$database;&#039;; done
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ ONLY WITH APPLY CDB2
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ ONLY WITH APPLY CDB2
&nbsp;
NAME      OPEN_MODE            INSTANCE_NAME
--------- -------------------- ----------------
CDB1      READ ONLY WITH APPLY CDB2
</code></pre>
<p>Of course this is not what you want. And we must not start or stop the default services. For the application, the best you can do is to create your service. And if you want to be able to connect to the Active Data Guard standby, which is opened in read-only, then you can create a &#8216;read-write&#8217; service and a &#8216;read-only&#8217; service that you start depending on the role.</p>
<h3>Create and Start a read-write service on the primary</h3>
<p>This example supposes that you have only Oracle Database software installed. If you are in RAC, with the resources managed by Grid Infrastructure, or simply with Oracle Restart, creating a service is easy with srvctl, and you add it to a PDB with &#8216;-pdb&#8217; and also with a role to start it automatically in the primary or in the standby. But without it, you use dbms_service:</p>
<pre><code>
SQL&gt; connect /@CDB1 as sysdba
Connected.
&nbsp;
SQL&gt; alter session set container=pdb1;
Session altered.
&nbsp;
SQL&gt; exec dbms_service.create_service(service_name=&gt;'pdb1_RW',network_name=&gt;'pdb1_RW');
PL/SQL procedure successfully completed.
&nbsp;
SQL&gt; exec dbms_service.start_service(service_name=&gt;'pdb1_RW');
PL/SQL procedure successfully completed.
&nbsp;
SQL&gt; alter session set container=cdb$root;
Session altered.
</code></pre>
<p>The service is created, stored in SERVICE$ visible with DBA_SERVICES:</p>
<pre><code>
SQL&gt; select name,name_hash,network_name,creation_date,pdb from cdb_services order by con_id,service_id;
NAME         NAME_HASH NETWORK_NAME   CREATION_DATE   PDB
----         --------- ------------   -------------   ---
pdb1_RW     3128030313 pdb1_RW        03-FEB-18       PDB1
pdb1        1888881990 pdb1           11-JAN-18       PDB1
</code></pre>
<h3>Save state</h3>
<p>I have created and started the PDB1_RW service. However, if I restart the database, the service will not start automatically. How do you ensure that the PDB1 pluggable database starts automatically when you open the CDB? You &#8216;save state&#8217; when it is opened. It is the same for the services you create. You need to &#8216;save state&#8217; when they are opened.</p>
<pre><code>
SQL&gt; alter pluggable database all save state;
Pluggable database ALL altered.
</code></pre>
<p>The information is stored in PDB_SVC_STATE$, and I&#8217;m not aware of a dictionary view on it:</p>
<pre><code>
SQL&gt; select name,name_hash,network_name,creation_date,con_id from v$active_services order by con_id,service_id;
&nbsp;
NAME         NAME_HASH NETWORK_NAME   CREATION_DATE     CON_ID
----         --------- ------------   -------------     ------
pdb1_RW     3128030313 pdb1_RW        03-FEB-18              4
pdb1        1888881990 pdb1           11-JAN-18              4
&nbsp;
SQL&gt; select * from containers(pdb_svc_state$);
&nbsp;
  INST_ID INST_NAME   PDB_GUID                                PDB_UID     SVC_HASH   SPARE1   SPARE2   SPARE3   SPARE4 SPARE5   SPARE6     CON_ID 
  ------- ---------   --------                                -------     --------   ------   ------   ------   ------ ------   ------     ------
        1 CDB1        627F7512A0452FD4E0537A38A8C055C0     2872139986   3128030313                                                              1 
</code></pre>
<p>The name is not in this table, you have to join with v$services using(name_hash):</p>
<pre><code>
SQL&gt; select name,name_hash,network_name,creation_date,con_id from v$active_services order by con_id,service_id;
&nbsp;
NAME                NAME_HASH NETWORK_NAME   CREATION_DATE     CON_ID 
----                --------- ------------   -------------     ------
SYS$BACKGROUND      165959219                26-JAN-17              1 
SYS$USERS          3427055676                26-JAN-17              1 
CDB1_CFG           1053205690 CDB1_CFG       24-JAN-18              1 
CDB1_DGB            184049617 CDB1_DGB       24-JAN-18              1 
CDB1XDB            1202503288 CDB1XDB        11-JAN-18              1 
CDB1               1837598021 CDB1           11-JAN-18              1 
pdb1               1888881990 pdb1           11-JAN-18              4 
pdb1_RW            3128030313 pdb1_RW        03-FEB-18              4 
</code></pre>
<p>So, in addition to storing the PDB state in PDBSTATE$, visible with dba_pdb_saved_states, the service state is also stored. Note that they are at different level. PDBSTATE$ is a data link: stored on CDB$ROOT only (because the data must be read before opening the PDB) but PDB_SVC_STATE$ is a local table in the PDB as the services can be started only when the PDB is opened.</p>
<p>This new service is immediately registered on CDB1:</p>
<pre><code>
Service "pdb1" has 2 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "pdb1_RW" has 1 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
The command completed successfully
</code></pre>
<h3>Create and Start a read-only service for the standby</h3>
<p>If you try to do the same on the standby for a PDB1_RO service, you cannot because service information has to be stored in the dictionary:</p>
<pre><code>
SQL&gt; exec dbms_service.create_service(service_name=&gt;'pdb1_RO',network_name=&gt;'pdb1_RO');
&nbsp;
Error starting at line : 56 File @ /media/sf_share/122/blogs/pdb_svc_standby.sql
In command -
BEGIN dbms_service.create_service(service_name=&gt;'pdb1_RO',network_name=&gt;'pdb1_RO'); END;
Error report -
ORA-16000: database or pluggable database open for read-only access
</code></pre>
<p>So, the read-only service has to be created on the primary:</p>
<pre><code>
SQL&gt; connect /@CDB1 as sysdba
Connected.
SQL&gt; alter session set container=pdb1;
Session altered.
&nbsp;
SQL&gt; exec dbms_service.create_service(service_name=&gt;'pdb1_RO',network_name=&gt;'pdb1_RO');
&nbsp;
SQL&gt; select name,name_hash,network_name,creation_date,pdb from cdb_services order by con_id,service_id;
NAME         NAME_HASH NETWORK_NAME   CREATION_DATE   PDB
----         --------- ------------   -------------   ---
pdb1_RW     3128030313 pdb1_RW        03-FEB-18       PDB1
pdb1_RO     1562179816 pdb1_RO        03-FEB-18       PDB1
pdb1        1888881990 pdb1           11-JAN-18       PDB1
</code></pre>
<p>The SERVICE$ dictionary table is replicated to the standby, so I can I can start it on the standby:</p>
<pre><code>
SQL&gt; connect /@CDB2 as sysdba
Connected.
SQL&gt; alter session set container=pdb1;
Session altered.
&nbsp;
SQL&gt; exec dbms_service.start_service(service_name=&gt;'pdb1_RO');
PL/SQL procedure successfully completed.
</code></pre>
<p>Here is what is registered to the listener:</p>
<pre><code>
Service "pdb1" has 2 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "pdb1_RO" has 1 instance(s).
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "pdb1_RW" has 1 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
The command completed successfully
</code></pre>
<p>Now, the PDB_RO connects to the standby and PDB_RW to the primary. Perfect.</p>
<h3>No &#8216;save state&#8217; on the standby</h3>
<p>At this point, you would like to have the PDB_RO started when PDB1 is opened on the standby, but &#8216;save state&#8217; is impossible on a read-only database:</p>
<pre><code>
SQL&gt; alter session set container=cdb$root;
Session altered.
&nbsp;
SQL&gt; alter pluggable database all save state;
&nbsp;
Error starting at line : 84 File @ /media/sf_share/122/blogs/pdb_svc_standby.sql
In command -
alter pluggable database all save state
Error report -
ORA-16000: database or pluggable database open for read-only access
</code></pre>
<p>You can&#8217;t manage the state (open the PDB, start the services) in the standby database.</p>
<h3>The primary &#8216;save state&#8217; is replicated in standby</h3>
<p>For the moment, everything is ok with my services:</p>
<pre><code>
Service "pdb1_RO" has 1 instance(s).
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "pdb1_RW" has 1 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
</code></pre>
<p>If I restart the primary CDB1, everything is ok again because I saved the state of the PDB and the service. But what happens when the standby CDB2 restarts?</p>
<pre><code>
SQL&gt; connect /@CDB2 as sysdba
Connected.
SQL&gt; startup force;
...
SQL&gt; show pdbs
&nbsp;
    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         4 PDB1                           MOUNTED
</code></pre>
<p>The PDB is not opened: the &#8216;saved state&#8217; for PDB is not read in the standby.<br />
However, when I open the PDB, it seems that the &#8216;saved state&#8217; for service is applied, and this one is replicated from the primary:</p>
<pre><code>
SQL&gt; alter pluggable database PDB1 open;
Pluggable database altered.
SQL&gt; host lsnrctl status
...
Service "pdb1" has 2 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
  Instance "CDB2", status READY, has 1 handler(s) for this service...
Service "pdb1_RW" has 2 instance(s).
  Instance "CDB1", status READY, has 1 handler(s) for this service...
  Instance "CDB2", status READY, has 1 handler(s) for this service...
The command completed successfully
</code></pre>
<p>My PDB1_RW is registered for both, connections will connect at random to the primary or the standby, and then the transactions will fail half of the times. It will be the same in case of switchover. This is not correct. </p>
<h3>Save state instances=()</h3>
<p>What I would like is the possibility to save state for a specific DB_UNIQUE_NAME, like with pluggable &#8216;spfile&#8217; parameters. But this is not possible. What is possible is to mention an instance but you can use it only for the primary instance where you save the state (or you get ORA-65110: Invalid instance name specified) and anyway, this will not be correct after a switchover.</p>
<h3>So what?</h3>
<p>Be careful, with services and ensure that the services used by the application are registered only for the correct instance. Be sure that this persists when the instances are restarted. For this you must link a service name to a database role. This cannot be done correctly with &#8216;save state&#8217;. You can use startup triggers, or better, Grid Infrastructure service resources.</p>
<p>Do not connect to the default service with the PDB name, you cannot remove it and cannot stop it, so you may have the same name for different instances in a Data Guard configuration. You can register the standby instances to different local listeners, to avoid the confusion, but you may still register to the same SCAN listener. </p>
<p>Create your own services, start them depending on the database role, and do not use &#8216;save state&#8217; in a physical standby configuration.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/multitenant-pdb-save-state-services-and-standby-databases/">Multitenant, PDB, &#8216;save state&#8217;, services and standby databases</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/multitenant-pdb-save-state-services-and-standby-databases/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>12c Multitenant internals: PDB_PLUG_IN_VIOLATIONS</title>
		<link>https://www.dbi-services.com/blog/12c-multitenant-internals-pdb_plug_in_violations/</link>
					<comments>https://www.dbi-services.com/blog/12c-multitenant-internals-pdb_plug_in_violations/#respond</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Sat, 30 Dec 2017 18:54:51 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CDB]]></category>
		<category><![CDATA[internals]]></category>
		<category><![CDATA[multitenant]]></category>
		<category><![CDATA[Oracle 12c]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<category><![CDATA[PDB]]></category>
		<category><![CDATA[Pluggable Databases]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/12c-multitenant-internals-pdb_plug_in_violations/</guid>

					<description><![CDATA[<p>By Franck Pachot . In the previous post https://www.dbi-services.com/blog/12c-multitenant-internals-pdb-replay-ddl-for-common-users I&#8217;ve done some DDL on a common user to show how this is replayed later for PDBs that were not opened at that time. But what happens when one of the DDL fails on one PDB? PDB$LASTREPLAY In the last post, the C##USER1 common user was [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/12c-multitenant-internals-pdb_plug_in_violations/">12c Multitenant internals: PDB_PLUG_IN_VIOLATIONS</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>By Franck Pachot</h2>
<p>.<br />
In the previous post <a href="https://www.dbi-services.com/blog/12c-multitenant-internals-pdb-replay-ddl-for-common-users" target="_blank" rel="noopener noreferrer">https://www.dbi-services.com/blog/12c-multitenant-internals-pdb-replay-ddl-for-common-users</a> I&#8217;ve done some DDL on a common user to show how this is replayed later for PDBs that were not opened at that time. But what happens when one of the DDL fails on one PDB?<br />
<span id="more-10716"></span></p>
<h3>PDB$LASTREPLAY</h3>
<p>In the last post, the C##USER1 common user was created and all pluggable databases (PDB1 with con_id=3 and PDB2 with con_id=4) were opened and synchronized:</p>
<pre><code>
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where opcode=-1 and bitand(flags,8)!=8 order by con_id,replay#;
&nbsp;
  CON_ID   SCNWRP   SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------   ------ -----                ----             --------   --------     ------   ------- -------
       1        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        10
       3        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        10
       4        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        10 
</code></pre>
<p>When REPLAY# in the PDB is equal to the CDB$ROOT one, this means that there are no additional statements to replicate on the PDB.</p>
<p>I have PDB1 opened read write and PDB2 in read only:</p>
<pre><code>
SQL&gt; show pdbs
&nbsp;
  CON_ID CON_NAME   OPEN MODE    RESTRICTED
  ------ --------   ---- ----    ----------
       2 PDB$SEED   READ ONLY    NO
       3 PDB1       READ WRITE   NO
       4 PDB2       READ ONLY    NO
</code></pre>
<p>For the demo my user&#8217;s default tablespace is SYSTEM:</p>
<pre><code>
SQL&gt; select con_id,username,common,default_tablespace from cdb_users where username='C##USER1' order by 1;
&nbsp;
  CON_ID USERNAME   COMMON   DEFAULT_TABLESPACE
  ------ --------   ------   ------------------
       1 C##USER1   YES      SYSTEM
       3 C##USER1   YES      SYSTEM
       4 C##USER1   YES      SYSTEM
</code></pre>
<h3>Failure in opened containers</h3>
<p>I want to change the default tablespace for C##USER1 and I have a USERS tablespace in CDB$ROOT (but not in the PDBs):</p>
<pre><code>
SQL&gt; alter user C##USER1 default tablespace USERS;
&nbsp;
Error starting at line : 50 File @ common-users-pdb-sync.sql
In command -
alter user C##USER1 default tablespace USERS
Error report -
ORA-65048: error encountered when processing the current DDL statement in pluggable database PDB1
ORA-00959: tablespace 'USERS' does not exist
</code></pre>
<p>As we have seen in the last post, the DDL is executed on all containers that are opened read write. Here it is fine on CDB$ROOT but fails on PDB1.</p>
<p>Then I create the USERS tablespace in PDB1:</p>
<pre><code>
SQL&gt; alter session set container=PDB1;
Session altered.
SQL&gt; create tablespace USERS datafile '/u01/oradata/CDB2/PDB1/users.dbf' size 5M;
Tablespace USERS created.
SQL&gt; alter session set container=CDB$ROOT;
Session altered.
</code></pre>
<p>And now, the statement is successful in CDB$ROOT, replicated on PDB1:</p>
<pre><code>
SQL&gt; alter user C##USER1 default tablespace USERS;
User C##USER1 altered.
</code></pre>
<p>This is nice: the statement is successful in all containers or fails. When it is successful, statements are recorded in PDB_SYNC$:</p>
<pre><code>
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id=1 and bitand(flags,8)!=8 order by con_id,replay#;
&nbsp;
  CON_ID   SCNWRP    SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------    ------ -----                ----             --------   --------     ------   ------- -------
       1        0         0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        11
       1        0    852610 26-jan-17 02:57:26   CTXSYS                      SYS               5         1 alter user CTXSYS account unlock identified by *^@
       1        0    853177 26-jan-17 02:57:34   CTXSYS                      SYS               5         2 alter user CTXSYS password expire account lock^@
       1        0   1405359 26-jan-17 03:31:31   SYSTEM                      SYS               5         3 alter user system account lock password expire^@
       1        0   1408693 23-dec-17 11:34:43   SYS                         SYS               5         4 alter user sys account unlock identified by *^@
       1        0   1408703 23-dec-17 11:34:43   SYSTEM                      SYS               5         5 alter user system account unlock identified by *^@
       1        0   1466615 29-dec-17 09:26:56   C##PROFILE1                 SYS               7         6 create profile C##PROFILE1 limit inactive_account_time 15 container=all^@
       1        0   1466641 29-dec-17 09:26:57   C##ROLE1                    SYS               3         7 create role C##ROLE1 container=all^@
       1        0   1466748 29-dec-17 09:26:58   C##USER1                    SYS               1         8 create user C##USER1 identified by * container=all^@
       1        0   1466812 29-dec-17 09:26:59   C##USER1                    SYS               5         9 alter user C##USER1 profile C##PROFILE1^@
       1        0   1466853 29-dec-17 09:26:59   C##USER1         C##ROLE1   SYS              10        10 grant C##ROLE1 to C##USER1 container=all^@
       1        0   1467010 29-dec-17 09:27:01   C##USER1                    SYS               5        11 alter user C##USER1 default tablespace USERS^@
</code></pre>
<h3>Failure in replay at open for closed containers</h3>
<p>But PDB2 is not synchronized because it was not opened read write:</p>
<pre><code>
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id&gt;1 and bitand(flags,8)!=8 order by con_id,replay#;
&nbsp;
  CON_ID   SCNWRP   SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------   ------ -----                ----             --------   --------     ------   ------- -------
       3        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        11
       4        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        10
</code></pre>
<p>But I don&#8217;t have a USERS tablespace in PDB2, so the replay will fail:</p>
<pre><code>
SQL&gt; alter pluggable database PDB2 close;
Pluggable database PDB2 altered.
&nbsp;
SQL&gt; alter pluggable database PDB2 open;
ORA-24344: success with compilation error
Pluggable database PDB2 altered.
</code></pre>
<p>This is a warning only. The SQlcl feedback is a bit misleading, mentioning a compilation error because this is where we used to have warnings, but the SQl*Plus message is more clear:</p>
<pre><code>
SQL&gt; alter pluggable database PDB2 open;
Warning: PDB altered with errors.
</code></pre>
<p>The PDB2 cannot be left closed, because you need to create a tablespace here. But it cannot be opened to everyone, because it is not in sync with CDB$ROOT. So what happens is that the PDB is opened in restricted mode:</p>
<pre><code>
SQL&gt; show pdbs
&nbsp;
  CON_ID CON_NAME   OPEN MODE    RESTRICTED
  ------ --------   ---- ----    ----------
       2 PDB$SEED   READ ONLY    NO
       3 PDB1       READ WRITE   NO
       4 PDB2       READ WRITE   YES
</code></pre>
<p>Note that if you look at PDB_SYNC$ in the PDB at that time, it looks like REPLAY#=11 has increased but you also see rows for the statement that has to be run. You have to connect to the PDB because containers() do not run in restricted session containers:</p>
<pre><code>
SQL&gt; alter session set container=PDB2;
Session altered.
&nbsp;
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id&gt;1 and bitand(flags,8)!=8 order by con_id,replay#;
&nbsp;
  CON_ID   SCNWRP    SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------    ------ -----                ----             --------   --------     ------   ------- -------
       4        0         0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        11
       4        0   1469022 29-dec-17 09:27:02   C##USER1                    SYS               5        11 alter user C##USER1 default tablespace USERS^@
&nbsp;
SQL&gt; alter session set container=CDB$ROOT;
Session altered.
</code></pre>
<p>Actually, the attempt to sync has inserted the statements and pushed the last replay indicator. Now, the PDB has all information to do a sync without the need to go to CDB$ROOT. The DDL was not replayed, but has been stored locally. When the sync will be successful, statements will be removed from the local PDB_SYNC$ leaving only the LASTREPLAY indicator.</p>
<h3>PDB_PLUG_IN_VIOLATIONS</h3>
<p>More info about the warning is stored in PDB_ALERT$ which you query from PDB_PLUG_IN_VIOLATIONS (the strange name reminds the TRANSPORT_SET_VIOLATIONS view used by DBMS_TTS):</p>
<pre><code>
SQL&gt; select name, cause, type, status,action,message,time from pdb_plug_in_violations;
&nbsp;
NAME   CAUSE          TYPE    STATUS    ACTION   MESSAGE                                                                               TIME
----   -----          ----    ------    ------   -------                                                                               -------------------------------
PDB2   Sync Failure   ERROR   PENDING            Sync PDB failed with ORA-959 during 'alter user C##USER1 default tablespace USERS'    29-DEC-17 09.27.03.266780000 PM
</code></pre>
<p>Here you have the statement that failed and the error number, but no recommended ACTION. However, ORA-959 is &#8220;tablespace &#8216;%s&#8217; does not exist&#8221; which gives a clue about the problem encountered.</p>
<p>As the PDB is opened a DBA (with RESTRICTED SESSION privilege) can add the tablespace:</p>
<pre><code>
SQL&gt; alter session set container=PDB2;
Session altered.
&nbsp;
SQL&gt; create tablespace USERS datafile '/u01/oradata/CDB2/PDB2/users.dbf' size 5M;
Tablespace USERS created.
</code></pre>
<p>But you cannot simply disable restricted session:</p>
<pre><code>
SQL&gt; alter system disable restricted session;
SQL&gt; alter system disable restricted session;
&nbsp;
Error starting at line : 74 File @ common-users-pdb-sync.sql
In command -
alter system disable restricted session
Error report -
ORA-65144: ALTER SYSTEM DISABLE RESTRICTED SESSION is not permitted
65144. 00000 -  "ALTER SYSTEM DISABLE RESTRICTED SESSION is not permitted"
*Cause:    An attempt was made to disable a restricted session while an unresolved error existed in PDB_PLUG_IN_VIOLATIONS.
*Action:   Resolve all of the errors before trying to disable a restricted session.
</code></pre>
<p>One solution is to close and open the PDB to get the DDL replay:</p>
<pre><code>SQL&gt; alter session set container=CDB$ROOT;
Session altered.
SQL&gt; alter pluggable database PDB2 close;
Pluggable database PDB2 altered.
SQL&gt; alter pluggable database PDB2 open;
Pluggable database PDB2 altered.
</code></pre>
<p>The other solution is to call DBMS_PDB.SYNC_PDB and disable restricted mode:</p>
<pre><code>
SQL&gt; exec dbms_pdb.sync_pdb; commit;
PL/SQL procedure successfully completed.
&nbsp;
SQL&gt; alter system disable restricted session;
System DISABLE altered.
</code></pre>
<p>In both case, no warning here, and no restricted mode:</p>
<pre><code>
SQL&gt; show pdbs
&nbsp;
  CON_ID CON_NAME   OPEN MODE    RESTRICTED
  ------ --------   ---- ----    ----------
       2 PDB$SEED   READ ONLY    NO
       3 PDB1       READ WRITE   NO
       4 PDB2       READ WRITE   NO
</code></pre>
<p>and the PDB_PLUG_IN_VIOLATIONS is updated to flag the issue as resolved:</p>
<pre><code>
SQL&gt; select name, cause, type, status,action,message,time from pdb_plug_in_violations;
&nbsp;
NAME   CAUSE          TYPE    STATUS     ACTION   MESSAGE                                                                               TIME
----   -----          ----    ------     ------   -------                                                                               -------------------------------
PDB2   Sync Failure   ERROR   RESOLVED            Sync PDB failed with ORA-959 during 'alter user C##USER1 default tablespace USERS'    29-DEC-17 09.27.04.093659000 PM
</code></pre>
<p>At that time, the local PDB_SYNC$ table in PDB2 contains only the PDB$LASTREPLAY row, with the same value as in the CDB$ROOT table. The rows with the statements have been deleted once the DDL has been successfully replayed:</p>
<pre><code>
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id&gt;1 and bitand(flags,8)!=8 order by con_id,replay#;
&nbsp;
  CON_ID   SCNWRP    SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------    ------ -----                ----             --------   --------     ------   ------- -------
       4        0         0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        11
</code></pre>
<h3>So what?</h3>
<p>The mechanism is simple: record what is done in CDB$ROOT, replicate it in PDBs when possible (opened read-write) and try to replay it, mark the last replay step. For containers that were not writeable, at open, the DDL is replicated on the PDBs that lag being CDB$ROOT and replay step is updated. Then the DDL is replayed. When sucessful, the statement is removed from the replicated DDL. When it fails, you get a warning, and a message in PDB_PLUG_IN_VIOLATIONS, and the PDB is opened in restricted session mode to let you solve the problem.<br />
If you can fix the issue so that the DDL to be replayed is successful, then you can just sync and disable restricted session, or simply close and re-open the PDB.<br />
If you can&#8217;t fix it I suppose you need to hack the statements in the local PDB_SYNC$, with Oracle Support agreement of course, and make sure that you arrive to a state which is consistent with the other containers, especially CDB$ROOT.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/12c-multitenant-internals-pdb_plug_in_violations/">12c Multitenant internals: PDB_PLUG_IN_VIOLATIONS</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/12c-multitenant-internals-pdb_plug_in_violations/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>12c Multitenant internals: PDB replay DDL for common users</title>
		<link>https://www.dbi-services.com/blog/12c-multitenant-internals-pdb-replay-ddl-for-common-users/</link>
					<comments>https://www.dbi-services.com/blog/12c-multitenant-internals-pdb-replay-ddl-for-common-users/#respond</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Fri, 29 Dec 2017 21:05:47 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CDB]]></category>
		<category><![CDATA[internals]]></category>
		<category><![CDATA[multitenant]]></category>
		<category><![CDATA[Oracle 12c]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<category><![CDATA[PDB]]></category>
		<category><![CDATA[Pluggable Databases]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/12c-multitenant-internals-pdb-replay-ddl-for-common-users/</guid>

					<description><![CDATA[<p>By Franck Pachot . In multitenant, you can create common Users, Roles, and Profiles. You create them in CDB$ROOT, with the CONTAINER=ALL clause (which is optional because it is the only possible value when connected to CDB$ROOT) but they are visible to all containers. As the goal of multitenant is to avoid to duplicate common [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/12c-multitenant-internals-pdb-replay-ddl-for-common-users/">12c Multitenant internals: PDB replay DDL for common users</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>By Franck Pachot</h2>
<p>.<br />
In multitenant, you can create common Users, Roles, and Profiles. You create them in CDB$ROOT, with the CONTAINER=ALL clause (which is optional because it is the only possible value when connected to CDB$ROOT) but they are visible to all containers. As the goal of multitenant is to avoid to duplicate common metadata to all containers, You may think that they are visible through those magic metadata links. But that&#8217;s actually wrong: they are simply replicated with a very simple mechanism: the DDL for common objects is replayed into each user PDB.<br />
<span id="more-10715"></span><br />
I&#8217;m connected to CDB2&#8217;s CDB$ROOT and I have two pluggable databases:</p>
<pre><code>
SQL&gt; show pdbs
&nbsp;
  CON_ID CON_NAME   OPEN MODE    RESTRICTED
  ------ --------   ---- ----    ----------
       2 PDB$SEED   READ ONLY    NO
       3 PDB1       READ WRITE   NO
       4 PDB2       MOUNTED
</code></pre>
<p>PDB1 is opened and PDB2 is closed.</p>
<h3>PDB_SYNC$</h3>
<p>In this example, I&#8217;ll query PDB_SYNC$ which is the table where Oracle stores all DDL for common users, roles, or profiles in order to be able to replay it later:</p>
<pre><code>
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id=1 and bitand(flags,8)!=8 order by con_id,replay#;
&nbsp;
  CON_ID   SCNWRP    SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------    ------ -----                ----             --------   --------     ------   ------- -------
       1        0    852610 26-jan-17 02:57:26   CTXSYS                      SYS               5         1 alter user CTXSYS account unlock identified by *^@
       1        0    853177 26-jan-17 02:57:34   CTXSYS                      SYS               5         2 alter user CTXSYS password expire account lock^@
       1        0   1405359 26-jan-17 03:31:31   SYSTEM                      SYS               5         3 alter user system account lock password expire^@
       1        0   1408693 23-dec-17 11:34:43   SYS                         SYS               5         4 alter user sys account unlock identified by *^@
       1        0   1408703 23-dec-17 11:34:43   SYSTEM                      SYS               5         5 alter user system account unlock identified by *^@
       1        0         0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1         5
</code></pre>
<p>I excluded the bitand(flags,8)=8 because it concerns application containers. I query with the container() to show the con_id but this is for con_id=1 which is the CDB$ROOT.</p>
<p>You can see some DDL for CTXSYS recorded on January 26th which is the day where this release (12.2.0.1) was built. I used a template with datafiles to create the CDB with DBCA. And you see some DDL to unlock SYS and SYSTEM on December 23rd when I created the database. You can also see that they are ordeded in sequence with REPLAY#.</p>
<p>More interesting is the OPCODE=-1 which is PDB$LASTREPLAY and looks like the last value of REPLAY#. This means that on this container, CDB$ROOT, all statements where REPLAY#&lt;=5 was run.</p>
<p>With a similar query, I query the opened PDBs:</p>
<pre><code>
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id&gt;1 and bitand(flags,8)!=8 order by con_id,replay#;
&nbsp;
  CON_ID   SCNWRP   SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------   ------ -----                ----             --------   --------     ------   ------- -------
       3        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1         5
</code></pre>
<p>There only one row here in CON_ID=3, which is PDB1: the PDB$LASTREPLAY mentioning that all statements up to REPLAY=5 have been run also in this container.<br />
I don&#8217;t see PDB2 (CON_ID=4) here because the container() clause cannot query closed containers.</p>
<h3>CONTAINER=ALL DDL</h3>
<p>I&#8217;ll run some common DLL to create a profile, a role and a user:</p>
<pre><code>
SQL&gt; create profile C##PROFILE1 limit inactive_account_time 15 container=all;
Profile C##PROFILE1 created.
&nbsp;
SQL&gt; create role C##ROLE1 container=all;
Role C##ROLE1 created.
&nbsp;
SQL&gt; create user C##USER1 identified by oracle container=all;
User C##USER1 created.
&nbsp;
SQL&gt; alter user C##USER1 profile C##PROFILE1;
User C##USER1 altered.
&nbsp;
SQL&gt; grant C##ROLE1 to C##USER1 container=all;
Grant succeeded.
</code></pre>
<p>The C## prefix is mandatory to isolate the common user namespace. You can change it with the common_prefix parameter. You can even set it to the empty string, but then you have a risk of namespace collision when you plug a PDB between CDB having different common profiles or roles.<br />
The CONTAINER=ALL is the default and the only possibility when connected to CDB$ROOT so it is optional. I recommend to mention it explicitly in order to avoid problems when running the same DDL in CDB$ROOT and in PDBs.</p>
<p>All those DDL have been recorded into PDB_SYNC$ and the REPLAY# has been increased:</p>
<pre><code>
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id=1 and bitand(flags,8)!=8 order by con_id,replay#;
&nbsp;
  CON_ID   SCNWRP    SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------    ------ -----                ----             --------   --------     ------   ------- -------
       1        0    852610 26-jan-17 02:57:26   CTXSYS                      SYS               5         1 alter user CTXSYS account unlock identified by *^@
       1        0    853177 26-jan-17 02:57:34   CTXSYS                      SYS               5         2 alter user CTXSYS password expire account lock^@
       1        0   1405359 26-jan-17 03:31:31   SYSTEM                      SYS               5         3 alter user system account lock password expire^@
       1        0   1408693 23-dec-17 11:34:43   SYS                         SYS               5         4 alter user sys account unlock identified by *^@
       1        0   1408703 23-dec-17 11:34:43   SYSTEM                      SYS               5         5 alter user system account unlock identified by *^@
       1        0   1466615 29-dec-17 09:26:56   C##PROFILE1                 SYS               7         6 create profile C##PROFILE1 limit inactive_account_time 15 container=all^@
       1        0   1466641 29-dec-17 09:26:57   C##ROLE1                    SYS               3         7 create role C##ROLE1 container=all^@
       1        0   1466748 29-dec-17 09:26:58   C##USER1                    SYS               1         8 create user C##USER1 identified by * container=all^@
       1        0   1466812 29-dec-17 09:26:59   C##USER1                    SYS               5         9 alter user C##USER1 profile C##PROFILE1^@
       1        0   1466853 29-dec-17 09:26:59   C##USER1         C##ROLE1   SYS              10        10 grant C##ROLE1 to C##USER1 container=all^@
       1        0         0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        10
</code></pre>
<p>PDB1 (CON_ID=3) was opened read write, and then has been synchronized (the DDL has been run in the container to create the same profile, role and user) and the PDB$LASTREPLAY has been updated in this container to show that all has been done:</p>
<pre><code>
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id&gt;1 and bitand(flags,8)!=8 order by con_id,opcode,replay#;
%nbsp;
  CON_ID   SCNWRP   SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------   ------ -----                ----             --------   --------     ------   ------- -------
       3        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        10
</code></pre>
<h3>MOUNTED or READ ONLY</h3>
<p>I open the PDB2 read only because I want to see what is in PDB_SYNC$ there. But READ ONLY means that the DDL cannot be run because no write is allowed in the local dictionary.</p>
<pre><code>
SQL&gt; alter pluggable database PDB2 open read only;
Pluggable database PDB2 altered.
</code></pre>
<p>Running the same query as above, I can see that PDB2 (CON_ID=4) is synchronized only up to the statements with REPLAY#=5 because my DDL was not replicated there.</p>
<pre><code>SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id&gt;1 and bitand(flags,8)!=8 order by con_id,opcode,replay#;
&nbsp;
  CON_ID   SCNWRP   SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------   ------ -----                ----             --------   --------     ------   ------- -------
       3        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        10
       4        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1         5
</code></pre>
<h3>Sync at OPEN</h3>
<p>When I open PDB2 in read write mode, the DDL can be synchronized:</p>
<pre><code>
SQL&gt; alter pluggable database PDB2 open read write force;
Pluggable database PDB2 altered.
</code></pre>
<p>At open, the DDL from REPLAY#&gt;5 has been replayed and once opened the PDB is in sync with CDB$ROOT:</p>
<pre><code>
SQL&gt; select con_id,scnwrp,scnbas,ctime,name,auxname1,auxname2,opcode,replay#,sqlstmt from containers(pdb_sync$) where con_id&gt;1 and bitand(flags,8)!=8 order by con_id,opcode,replay#;
&nbsp;
  CON_ID   SCNWRP   SCNBAS CTIME                NAME             AUXNAME1   AUXNAME2     OPCODE   REPLAY# SQLSTMT
  ------   ------   ------ -----                ----             --------   --------     ------   ------- -------
       3        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        10
       4        0        0 26-jan-17 01:53:02   PDB$LASTREPLAY                               -1        10
</code></pre>
<h3>So what?</h3>
<p>The common users, roles and profiles are not stored only in CDB$ROOT to be shared, but rather replicated to all PDBs. The DDL is replicated synchronously to all opened pluggable databases in read write, and stored into the CDB$ROOT PDB_SYNC$ table to be replayed later when non-synced PDBs are opened. I&#8217;ll show in the <a href="https://www.dbi-services.com/blog/12c-multitenant-internals-pdb_plug_in_violations/" target="_blank" rel="noopener noreferrer">next post</a> what happens when the DDL is in error.</p>
<p>Note that even when all pluggable databases are opened read write, the DDL is stored and they are purged later (when replayed on all PDBs) because they are needed when you create a new PDB and open it. The PDB$SEED has REPLAY#=0 for PDB$LASTREPLAY which means that all statements will be replayed.</p>
<p>This is 12.2.0.1 where this mechanism is only for common users, roles and profiles having DDL in CDB$ROOT. With application containers, more than that is recorded: all DML and DDL run between the &#8216;begin install/upgrade/patch&#8217; and &#8216;end install/upgrade/patch&#8217; in the application root. Then, the statements can be replayed into the application PDB with a simple SYNC command. In the future release (18<i>c</i>) we expect to have that application root behavior ported to CDB$ROOT so that we don&#8217;t have to run catupgrd.sql in all containers. Then the PDB will probably be patched or upgraded when opened.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/12c-multitenant-internals-pdb-replay-ddl-for-common-users/">12c Multitenant internals: PDB replay DDL for common users</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/12c-multitenant-internals-pdb-replay-ddl-for-common-users/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>12c Multitenant Internals: compiling system package from PDB</title>
		<link>https://www.dbi-services.com/blog/12c-multitenant-internals-compiling-system-package-from-pdb/</link>
					<comments>https://www.dbi-services.com/blog/12c-multitenant-internals-compiling-system-package-from-pdb/#respond</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Wed, 22 Nov 2017 12:38:23 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CDB]]></category>
		<category><![CDATA[multitenant]]></category>
		<category><![CDATA[Oracle 12c]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<category><![CDATA[PDB]]></category>
		<category><![CDATA[Pluggable Databases]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/12c-multitenant-internals-compiling-system-package-from-pdb/</guid>

					<description><![CDATA[<p>By Franck Pachot . When I explain the multitenant internals, I show that all metadata about system procedures and packages are stored only in CDB$ROOT and are accessed from the PDBs through metadata links. I take an example with DBMS_SYSTEM that has nothing in SOURCE$ of the PDB. But I show that we can compile [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/12c-multitenant-internals-compiling-system-package-from-pdb/">12c Multitenant Internals: compiling system package from PDB</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>By Franck Pachot</h2>
<p>.<br />
<a href="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/DPKi1vxX0AAADLm.jpg"><img decoding="async" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/DPKi1vxX0AAADLm.jpg" alt="DPKi1vxX0AAADLm" width="300" height="225" class="alignleft size-medium wp-image-19698" /></a>When I explain the multitenant internals, I show that all metadata about system procedures and packages are stored only in CDB$ROOT and are accessed from the PDBs through metadata links. I take an example with DBMS_SYSTEM that has nothing in SOURCE$ of the PDB. But I show that we can compile it from the PDB. This is my way to prove that the session can access the system objects, internally switching the session to the root container when it needs to read SOURCE$. At DOAG Conference I had a very interesting question about what happens exactly in CDB$ROOT: Is the session really executing all the DML on the internal tables storing the compiled code of the procedure?<br />
<span id="more-10647"></span><br />
My first answer was something like &#8216;why not&#8217; because the session in a PDB can switch and do modifications into CDB$ROOT internally. For example, even a local PDB DBA can change some &#8216;spfile&#8217; parameters which are actually stored in the CDB$ROOT. But then I realized that the question goes further: is the PDB session really compiling the DBMS_SYSTEM package in the CDB$ROOT? Actually, there are some DDL that are transformed to &#8216;no-operation&#8217; when executed on the PDB.</p>
<p>To see which ones are concerned, the best is to trace:</p>
<pre><code>
SQL&gt; alter session set events='10046 trace name context forever, level 4';
Session altered.
SQL&gt; alter session set container=PDB1;                                                                              
Session altered.              
SQL&gt; alter package dbms_system compile;                                                                               
Package altered.           
SQL&gt; alter session set events='10046 trace name context off';
Session altered.
</code></pre>
<p>I&#8217;ll not show the whole trace here. For sure I can see that the session switches to CDB$ROOT to read the source code of the package:</p>
<pre><code>
*** 2017-11-22T08:36:01.963680+01:00 (CDB$ROOT(1))
=====================
PARSING IN CURSOR #140650193204552 len=54 dep=1 uid=0 oct=3 lid=0 tim=5178881528 hv=696375357 ad='7bafeab8' sqlid='9gq78x8ns3q1x'
select source from source$ where obj#=:1 order by line
END OF STMT
PARSE #140650193204552:c=0,e=290,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=0,tim=5178881527
EXEC #140650295606992:c=1000,e=287,p=0,cr=0,cu=0,mis=0,r=0,dep=2,og=4,plh=813480514,tim=5178881999
FETCH #140650295606992:c=0,e=35,p=0,cr=4,cu=0,mis=0,r=1,dep=2,og=4,plh=813480514,tim=5178882057
CLOSE #140650295606992:c=0,e=12,dep=2,type=3,tim=5178882104
</code></pre>
<p>That was my point about metadata links. But now about modifications.</p>
<p>As I need to see only the statements, I can use TKPROF to get them aggregated, but then the container switch &#8211; like (CDB$ROOT(1)) here &#8211; is ignored. </p>
<p>Here is a small AWK script I use to add the Container ID to the SQL ID so that it is visible and detailed into TKPROF output:</p>
<pre><code>
awk '/^[*]{3}/{con=$3}/^PARSING IN/{sub(/sqlid=./,"&amp;"con" ")}{print &gt; "con_"FILENAME }'
</code></pre>
<p>Then I run TKPROF on the resulting file, with &#8216;sort=(execu)&#8217; so that I have the modifications (insert/delete/update) first. The result starts with something like this:</p>
<pre><code>
SQL ID: (PDB1(3)) 1gfaj4z5hn1kf Plan Hash: 1110520934
&nbsp;
delete from dependency$
where
 d_obj#=:1
</code></pre>
<p>I know that dependencies are replicated into all containers (because table metadata is replicated into all containers) so I see following tables modified in the PDB:  DEPENDENCY$, ACCESS$, DIANA_VERSION$, and of course OBJ$.</p>
<p>But to answer the initial question, there are no modifications done in the CDB$ROOT. Only SELECT statements there, on SOURCE$, SETTINGS$, CODEAUTH$, WARNING_SETTINGS$</p>
<p>So, probably, the updates have been transformed to no-op operations once the session is aware that the source is the same (same signature) and it just reads the compilation status.</p>
<p>Just as a comparison, tracing the same compilation when done on the CDB$ROOT will show inserts/delete/update on ARGUMENT$, PROCEDUREINFO$, SETTINGS$, PROCEDUREPLSQL$, IDL_UB1$, IDL_SB4$, IDL_UB2$, IDL_CHAR$, &#8230; all those tables sorting the compiled code.</p>
<p>So basically, when running DDL on metadata links in a PDB, not all the work is done in the CDB, especially not writing again what is already there (because you always upgrade the CDB$ROOT first). However, up to 12.2 we don&#8217;t see a big difference in time. This should change in 18<em>c</em> where the set of DDL to be run on the PDB will be pre-processed to avoid unnecessary operations.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/12c-multitenant-internals-compiling-system-package-from-pdb/">12c Multitenant Internals: compiling system package from PDB</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/12c-multitenant-internals-compiling-system-package-from-pdb/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Multitenant internals: INT$ and INT$INT$ views</title>
		<link>https://www.dbi-services.com/blog/multitenant-internals-int-and-intint-views/</link>
					<comments>https://www.dbi-services.com/blog/multitenant-internals-int-and-intint-views/#respond</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Sun, 05 Nov 2017 18:24:53 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CDB]]></category>
		<category><![CDATA[internals]]></category>
		<category><![CDATA[multitenant]]></category>
		<category><![CDATA[Oracle 12c]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<category><![CDATA[PDB]]></category>
		<category><![CDATA[Pluggable Databases]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/multitenant-internals-int-and-intint-views/</guid>

					<description><![CDATA[<p>By Franck Pachot . This month, I&#8217;ll talk &#8211; with lot of demos &#8211; about multitenant internals at DOAG conference. The multitenant dictionary architecture starts with a simple idea: system metadata and data are in CDB$ROOT and user metadata and data are in PDB. And it could have been that simple. When a session connected [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/multitenant-internals-int-and-intint-views/">Multitenant internals: INT$ and INT$INT$ views</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>By Franck Pachot</h2>
<p>.<br />
This month, I&#8217;ll talk &#8211; with lot of demos &#8211; about multitenant internals at <a href="https://2017.doag.org/en/home/" target="_blank" rel="noopener noreferrer">DOAG conference</a>. <a href="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/CaptureMultitenantInternals.png"><img decoding="async" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/CaptureMultitenantInternals.png" alt="CaptureMultitenantInternals" width="300" height="184" class="alignleft size-medium wp-image-19398" /></a><br />
The multitenant dictionary architecture starts with a simple idea: system metadata and data are in CDB$ROOT and user metadata and data are in PDB. And it could have been that simple. When a session connected to a PDB needs to read some system information, the session context is switched to the CDB$ROOT container and reads from CDB$ROOT SYS tablespace and objects, and then switches back to the PDB container. This is implemented by metadata and data links: the PDB lists the objects for which the session has to switch to CDB$ROOT to get metadata or data. </p>
<p><a href="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/CaptureMultitenantInternals1.png"><img loading="lazy" decoding="async" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/CaptureMultitenantInternals1.png" alt="CaptureMultitenantInternals1" width="300" height="187" class="alignright size-medium wp-image-19409" /></a>But, for compatibility reason, and ease of administration, the dictionary views must display information from both containers, transparently, and then things become a little more complex with common views and extended data views.</p>
<p>At Oracle Open World, the multitenant architects, in the #PDBExpert session, answered questions about the multitenant architecture posted on Twitter. My first question (because I was investigating a <a href="https://www.dbi-services.com/blog/wrong-result-with-multitenant-dba_contraints-and-current_schema/" target="_blank" rel="noopener noreferrer">bug</a> at that time) was about some views, such as INT$INT$DBA_CONSTRAINTS, introduced to implement the complexity of showing the same information in dictionary views as the ones we had on non-CDB. Of course, the architects didn&#8217;t want to go too far on this and had a very accurate answer: INT$ is for internal, and here you have two &#8216;INT$&#8217; so you shouldn&#8217;t look at that.</p>
<p>But I like to understand how things work and here is the explanation of these INT$INT$ views. And I&#8217;m not even sure that INT is for &#8216;internal&#8217; but maybe &#8216;intermediate&#8217;. But for sure, the $ at the end is used by Oracle internal dictionary objects.</p>
<p><span id="more-10610"></span></p>
<h3>INT$ Extended Data views</h3>
<p>We are used to seeing all objects, system ones and user ones, listed by the dictionary views. For example, DBA_PROCEDURES shows all procedures, system and user ones, and then have to read from both containers (current PDB and CDB$ROOT) through extended data links. ALL_PROCEDURES shows all procedures accessible by the user, and they also have to switch to CDB$ROOT if the user has been granted to read system objects. USER_PROCEDURES shows only the objects owned by the current user, and then can read from the current container only.</p>
<p>For the ease of the definition, in 12<i>c</i> all the joins on the underlying tables(such as procedureinfo$, user$, obj$) is done by an intermediate view such as INT$DBA_PROCEDURES which is defined as EXTENDED DATA link to read from CDB$ROOT in addition to the local table. Then DBA_PROCEDURES, ALL_PROCEDURES and USER_PROCEDURES are defined on top of it with the required where clause to filter out owner and privilege accessibility.</p>
<h3>INT$INT$ Extended Data views</h3>
<p>In this post, I&#8217;ll detail the special case of DBA_CONSTRAINTS because things are more complex to get the multitenant architecture behaving the same as the non-CDB.</p>
<p>There are several types of constraints which are identified with the CONSTRAINT_TYPE column of DBA_CONSTRAINTS, or the TYPE# of the underlying table CDEF#</p>
<p>Here, I query the underlying table with the CONTAINER() function to see what is stored in each container:</p>
<pre><code>
SQL&gt; select decode(type#,1,'C',2,'P',3,'U',4,'R',5,'V',6,'O',7,'C',8,'H',9,'F',10,'F',11,'F',13,'F','?') constraint_type,
  2  type#,con_id,count(*) from containers(cdef$)
  3  group by type#,con_id order by type#,con_id;
&nbsp;
CONSTRAINT_TYPE     TYPE#   CON_ID   COUNT(*)
---------------     -----   ------   --------
C                       1        1         74
C                       1        3         74
P                       2        1        843
P                       2        3        844
U                       3        1        238
U                       3        3        238
R                       4        1        324
R                       4        3        324
V                       5        1         11
O                       6        1        172
O                       6        3         26
C                       7        1       5337
C                       7        3       5337
F                      11        1         11
F                      11        3         11
?                      12        1          3
?                      12        3          3
</code></pre>
<p>I have very few user objects in this database. CON_ID=1 is CDB$ROOT and CON_ID=3 is my PDB. What we can see here is that we have nearly the same number of rows in both containers for the following constraint types: C (check constraint on a table), P (primary key), U (unique key), R (referential integrity), and other types related to tables. And some types have most of their rows in CDB$ROOT only: V (check option on views), R (read only on views)</p>
<p>That&#8217;s an implementation specificity of the multitenant architecture which makes things more complex for the dictionary views. For some objects (such as procedures and views) the metadata is stored in only one container: system objects have all their information in CDB$ROOT and the PDB has only a link which is a dummy row in OBJ$ which mentions the sharing (such as metadata link), owner and name (to match to the object in CDB$ROOT), and a signature (to verify that the DDL creating the object is the same). But other objects (such as tables) have their information duplicated in all containers for system objects (CDB$ROOT, PDB$SEED and all user PDBs). This is the reason why we see rows in both containers for constraint definition when they are related to a table.</p>
<h3>Example on view constraint</h3>
<p>I&#8217;ll take a constraint on system view as an example: constraint SYS_C003357 on table SYS.DBA_XS_SESSIONS</p>
<pre><code>
SQL&gt; select owner,object_name,object_type,sharing from dba_objects where owner='SYS' and object_name='DBA_XS_SESSIONS';
&nbsp;
OWNER   OBJECT_NAME       OBJECT_TYPE   SHARING
-----   -----------       -----------   -------
SYS     DBA_XS_SESSIONS   VIEW          METADATA LINK
&nbsp;
SQL&gt; select owner,table_name,constraint_type,constraint_name from containers(dba_constraints) where owner='SYS' and table_name='DBA_XS_SESSIONS'  and rownum=1;
&nbsp;
OWNER   TABLE_NAME        CONSTRAINT_TYPE   CONSTRAINT_NAME
-----   ----------        ---------------   ---------------
SYS     DBA_XS_SESSIONS   O                 SYS_C003357
</code></pre>
<p>I&#8217;m looking at the dependencies for the DBA_CONSTRAINTS view:</p>
<pre><code>
SQL&gt; select owner,name,referenced_owner,referenced_name from dba_dependencies where owner='SYS' and name='DBA_CONSTRAINTS' and type='VIEW';
&nbsp;
OWNER   NAME              REFERENCED_OWNER   REFERENCED_NAME
-----   ----              ----------------   ---------------
SYS     DBA_CONSTRAINTS   SYS                GETLONG
SYS     DBA_CONSTRAINTS   SYS                INT$DBA_CONSTRAINTS
</code></pre>
<p>So the DBA_CONSTRAINT is a view on INT$DBA_CONSTRAINTS as we have seen above. However, this view is not directly reading the tables but another view:</p>
<pre><code>
SQL&gt; select owner,name,referenced_owner,referenced_name from dba_dependencies where owner='SYS' and name='INT$DBA_CONSTRAINTS' and type='VIEW';
&nbsp;
OWNER   NAME                  REFERENCED_OWNER   REFERENCED_NAME
-----   ----                  ----------------   ---------------
SYS     INT$DBA_CONSTRAINTS   SYS                GETLONG
SYS     INT$DBA_CONSTRAINTS   SYS                INT$INT$DBA_CONSTRAINTS
</code></pre>
<p>Here is our additional INT$INT$ view which is reading the tables:</p>
<pre><code>
SQL&gt; select owner,name,referenced_owner,referenced_name from dba_dependencies where owner='SYS' and name='INT$INT$DBA_CONSTRAINTS' and type='VIEW';
&nbsp;
OWNER   NAME                      REFERENCED_OWNER   REFERENCED_NAME
-----   ----                      ----------------   ---------------
SYS     INT$INT$DBA_CONSTRAINTS   SYS                USER$
SYS     INT$INT$DBA_CONSTRAINTS   SYS                CDEF$
SYS     INT$INT$DBA_CONSTRAINTS   SYS                OBJ$
SYS     INT$INT$DBA_CONSTRAINTS   SYS                CON$
SYS     INT$INT$DBA_CONSTRAINTS   SYS                _CURRENT_EDITION_OBJ
SYS     INT$INT$DBA_CONSTRAINTS   SYS                _BASE_USER
SYS     INT$INT$DBA_CONSTRAINTS   SYS                GETLONG
</code></pre>
<p>In summary, the EXTENDED DATA view which reads the tables on each container (CDB$ROOT and PDB) is here the INT$INT$DBA_CONSTRAINTS and the INT$DBA_CONSTRAINTS is another intermediate one before the DBA_CONSTRAINTS view.</p>
<pre><code>
SQL&gt; select owner,object_name,object_type,sharing from dba_objects where object_name in ('DBA_CONSTRAINTS','INT$DBA_CONSTRAINTS','INT$INT$DBA_CONSTRAINTS') order by object_id desc;
&nbsp;
OWNER    OBJECT_NAME               OBJECT_TYPE   SHARING
-----    -----------               -----------   -------
PUBLIC   DBA_CONSTRAINTS           SYNONYM       METADATA LINK
SYS      DBA_CONSTRAINTS           VIEW          METADATA LINK
SYS      INT$DBA_CONSTRAINTS       VIEW          METADATA LINK
SYS      INT$INT$DBA_CONSTRAINTS   VIEW          EXTENDED DATA LINK
</code></pre>
<p>In this example, we don&#8217;t understand the reason for the additional intermediate view because the return all the same number of rows in each container:</p>
<pre><code>
SQL&gt; select con_id,constraint_type,constraint_name from containers(INT$INT$DBA_CONSTRAINTS) where OWNER='SYS' and constraint_name='SYS_C003357'
&nbsp;
  CON_ID CONSTRAINT_TYPE   CONSTRAINT_NAME
  ------ ---------------   ---------------
       1 O                 SYS_C003357
       3 O                 SYS_C003357
&nbsp;
SQL&gt; select con_id,constraint_type,constraint_name from containers(INT$DBA_CONSTRAINTS) where OWNER='SYS' and constraint_name='SYS_C003357'
&nbsp;
  CON_ID CONSTRAINT_TYPE   CONSTRAINT_NAME
  ------ ---------------   ---------------
       1 O                 SYS_C003357
       3 O                 SYS_C003357
&nbsp;
SQL&gt; select con_id,constraint_type,constraint_name from containers(DBA_CONSTRAINTS) where OWNER='SYS' and constraint_name='SYS_C003357'
&nbsp;
  CON_ID CONSTRAINT_TYPE   CONSTRAINT_NAME
  ------ ---------------   ---------------
       1 O                 SYS_C003357
       3 O                 SYS_C003357
</code></pre>
<p>The difference is only a few additional columns from the object definition (OWNERID,OBJECT_ID,OBJECT_TYPE#,SHARING) in the INT$ and INT$INT$ which are not selected in the final view:</p>
<pre><code>
SQL&gt; select * from containers(INT$INT$DBA_CONSTRAINTS) where OWNER='SYS' and constraint_name='SYS_C003357'
&nbsp;
OWNER     OWNERID CONSTRAINT_NAME   CONSTRAINT_TYPE   TABLE_NAME          OBJECT_ID   OBJECT_TYPE# SEARCH_CONDITION_VC   R_OWNER   R_CONSTRAINT_NAME   DELETE_RULE   STATUS    DEFERRABLE       DEFERRED    VALIDATED       GENERATED        BAD   RELY   LAST_CHANGE   INDEX_OWNER   INDEX_NAME   INVALID   VIEW_RELATED     SHARING   ORIGIN_CON_ID   CON_ID
-----     ------- ---------------   ---------------   ----------          ---------   ------------ -------------------   -------   -----------------   -----------   ------    ----------       --------    ---------       ---------        ---   ----   -----------   -----------   ----------   -------   ------------     -------   -------------   ------
SYS             0 SYS_C003357       O                 DBA_XS_SESSIONS         10316              4                                                                   ENABLED   NOT DEFERRABLE   IMMEDIATE   NOT VALIDATED   GENERATED NAME                26-JAN-17                                                                 1               1        1
SYS             0 SYS_C003357       O                 DBA_XS_SESSIONS         10316              4                                                                   ENABLED   NOT DEFERRABLE   IMMEDIATE   NOT VALIDATED   GENERATED NAME                26-JAN-17                                                                 1               1        3
&nbsp;
SQL&gt; select * from containers(INT$DBA_CONSTRAINTS) where OWNER='SYS' and constraint_name='SYS_C003357'
&nbsp;
OWNER     OWNERID CONSTRAINT_NAME   CONSTRAINT_TYPE   TABLE_NAME          OBJECT_ID   OBJECT_TYPE# SEARCH_CONDITION_VC   R_OWNER   R_CONSTRAINT_NAME   DELETE_RULE   STATUS    DEFERRABLE       DEFERRED    VALIDATED       GENERATED        BAD   RELY   LAST_CHANGE   INDEX_OWNER   INDEX_NAME   INVALID   VIEW_RELATED     SHARING   ORIGIN_CON_ID   CON_ID
-----     ------- ---------------   ---------------   ----------          ---------   ------------ -------------------   -------   -----------------   -----------   ------    ----------       --------    ---------       ---------        ---   ----   -----------   -----------   ----------   -------   ------------     -------   -------------   ------
SYS             0 SYS_C003357       O                 DBA_XS_SESSIONS         10316              4                                                                   ENABLED   NOT DEFERRABLE   IMMEDIATE   NOT VALIDATED   GENERATED NAME                26-JAN-17                                                                 1               1        1
SYS             0 SYS_C003357       O                 DBA_XS_SESSIONS         10316              4                                                                   ENABLED   NOT DEFERRABLE   IMMEDIATE   NOT VALIDATED   GENERATED NAME                26-JAN-17                                                                 1               1        3
&nbsp;
SQL&gt; select * from containers(DBA_CONSTRAINTS) where OWNER='SYS' and constraint_name='SYS_C003357'
&nbsp;
OWNER   CONSTRAINT_NAME   CONSTRAINT_TYPE   TABLE_NAME        SEARCH_CONDITION_VC   R_OWNER   R_CONSTRAINT_NAME   DELETE_RULE   STATUS    DEFERRABLE       DEFERRED    VALIDATED       GENERATED        BAD   RELY   LAST_CHANGE   INDEX_OWNER   INDEX_NAME   INVALID   VIEW_RELATED     ORIGIN_CON_ID   CON_ID
-----   ---------------   ---------------   ----------        -------------------   -------   -----------------   -----------   ------    ----------       --------    ---------       ---------        ---   ----   -----------   -----------   ----------   -------   ------------     -------------   ------
SYS     SYS_C003357       O                 DBA_XS_SESSIONS                                                                     ENABLED   NOT DEFERRABLE   IMMEDIATE   NOT VALIDATED   GENERATED NAME                26-JAN-17                                                                       1        1
SYS     SYS_C003357       O                 DBA_XS_SESSIONS                                                                     ENABLED   NOT DEFERRABLE   IMMEDIATE   NOT VALIDATED   GENERATED NAME                26-JAN-17   
</code></pre>
<p>If we look at the INT$DBA_CONSTRAINTS definition we see some filters on those object definition:</p>
<pre><code>
SQL&gt; ddl INT$DBA_CONSTRAINTS
&nbsp;
  CREATE OR REPLACE FORCE NONEDITIONABLE VIEW "SYS"."INT$DBA_CONSTRAINTS" ("OWNER", "OWNERID", "CONSTRAINT_NAME", "CONSTRAINT_TYPE", "TABLE_NAME", "OBJECT_ID", "OBJECT_TYPE#", "SEARCH_CONDITION", "SEARCH_CONDITION_VC", "R_OWNER", "R_CONSTRAINT_NAME", "DELETE_RULE", "STATUS", "DEFERRABLE", "DEFERRED", "VALIDATED", "GENERATED", "BAD", "RELY", "LAST_CHANGE", "INDEX_OWNER", "INDEX_NAME", "INVALID", "VIEW_RELATED", "SHARING", "ORIGIN_CON_ID") AS
  select OWNER, OWNERID, CONSTRAINT_NAME, CONSTRAINT_TYPE,
       TABLE_NAME, OBJECT_ID, OBJECT_TYPE#, SEARCH_CONDITION,
       SEARCH_CONDITION_VC, R_OWNER, R_CONSTRAINT_NAME, DELETE_RULE, STATUS,
       DEFERRABLE, DEFERRED, VALIDATED, GENERATED,
       BAD, RELY, LAST_CHANGE, INDEX_OWNER, INDEX_NAME,
       INVALID, VIEW_RELATED, SHARING, ORIGIN_CON_ID
from   INT$INT$DBA_CONSTRAINTS INT$INT$DBA_CONSTRAINTS
where  INT$INT$DBA_CONSTRAINTS.OBJECT_TYPE# = 4         /* views */
       OR (INT$INT$DBA_CONSTRAINTS.OBJECT_TYPE# = 2     /* tables */
           AND (INT$INT$DBA_CONSTRAINTS.ORIGIN_CON_ID
                = TO_NUMBER(SYS_CONTEXT('USERENV', 'CON_ID'))));
</code></pre>
<p>For views (OBJECT_TYPE#=4) there is no filter, which explains why we see the same number of rows in the previous example. But for tables (OBJECT_TYPE#=2) there&#8217;s an additional filter to keep the row from the current container only.</p>
<h3>Example on table constraint</h3>
<p>Then, I&#8217;ll take another example with a constraint definition for a table:</p>
<pre><code>
SQL&gt; select owner,object_name,object_type,sharing from dba_objects where owner='SYS' and object_name='RXS$SESSIONS';
&nbsp;
OWNER   OBJECT_NAME    OBJECT_TYPE   SHARING
-----   -----------    -----------   -------
SYS     RXS$SESSIONS   TABLE         METADATA LINK
&nbsp;
SQL&gt; select owner,table_name,constraint_type,constraint_name from dba_constraints where owner='SYS' and table_name='RXS$SESSIONS' and rownum=1;
&nbsp;
OWNER   TABLE_NAME     CONSTRAINT_TYPE   CONSTRAINT_NAME
-----   ----------     ---------------   ---------------
SYS     RXS$SESSIONS   C                 SYS_C003339
</code></pre>
<p>From the INT$INT$ view, we have a duplicate when we query on a PDB because for tables the PDB not only holds a dummy row in OBJ$ but full information about the table is duplicated in other tables such as TAB$ and CDEF$:</p>
<pre><code>
SQL&gt; select con_id,constraint_type,constraint_name from containers(INT$INT$DBA_CONSTRAINTS) where OWNER='SYS' and constraint_name='SYS_C003339'
&nbsp;
  CON_ID CONSTRAINT_TYPE   CONSTRAINT_NAME
  ------ ---------------   ---------------
       1 C                 SYS_C003339
       3 C                 SYS_C003339
       3 C                 SYS_C003339
</code></pre>
<p>This is the reason for the additional intermediate view: filtering out those duplicate by removing the rows from CDB$ROOT when queried from a PDB.</p>
<pre><code>
SQL&gt; select con_id,constraint_type,constraint_name from containers(INT$DBA_CONSTRAINTS) where OWNER='SYS' and constraint_name='SYS_C003339'
&nbsp;
  CON_ID CONSTRAINT_TYPE   CONSTRAINT_NAME
  ------ ---------------   ---------------
       1 C                 SYS_C003339
       3 C                 SYS_C003339
</code></pre>
<p>Thanks to that, the duplicates are not visible to the end-user views DBA_CONSTRAINTS and PDB_CONSTRAINTS.</p>
<p>You may wonder why only DBA_CONSTRAINTS needs this views and not DBA_TABLES, DBA_INDEXES or DBA_TAB_COLUMNS? That&#8217;s because all information about system tables and indexes are replicated in all PDBs and then there is no need for EXTENDED DATA and context switches. DBA_CONSTRAINT has the particularity of showing information about tables and views, which implement the metadata links in a different way.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/multitenant-internals-int-and-intint-views/">Multitenant internals: INT$ and INT$INT$ views</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/multitenant-internals-int-and-intint-views/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Multitenant dictionary: what is consolidated and what is not</title>
		<link>https://www.dbi-services.com/blog/multitenant-dictionary-what-is-consolidated-and-what-is-not/</link>
					<comments>https://www.dbi-services.com/blog/multitenant-dictionary-what-is-consolidated-and-what-is-not/#respond</comments>
		
		<dc:creator><![CDATA[Oracle Team]]></dc:creator>
		<pubDate>Sun, 05 Nov 2017 16:38:31 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[CDB]]></category>
		<category><![CDATA[internals]]></category>
		<category><![CDATA[multitenant]]></category>
		<category><![CDATA[Oracle 12]]></category>
		<category><![CDATA[Oracle Multitenant]]></category>
		<category><![CDATA[PDB]]></category>
		<category><![CDATA[Pluggable Databases]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/multitenant-dictionary-what-is-consolidated-and-what-is-not/</guid>

					<description><![CDATA[<p>By Franck Pachot . The documentation says that for Reduction of duplication and Ease of database upgrade the Oracle-supplied objects such as data dictionary table definitions and PL/SQL packages are represented only in the root. Unfortunately, this is only partly true. System PL/SQL packages are only in root but system table definition are replicated into [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/multitenant-dictionary-what-is-consolidated-and-what-is-not/">Multitenant dictionary: what is consolidated and what is not</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>By Franck Pachot</h2>
<p>.<br />
The <a href="https://docs.oracle.com/database/121/CNCPT/cdblogic.htm#CNCPT89255" target="_blank" rel="noopener noreferrer">documentation</a> says that for <i>Reduction of duplication</i> and <i>Ease of database upgrade</i> the <i>Oracle-supplied objects such as data dictionary table definitions and PL/SQL packages are represented only in the root</i>. </p>
<p>Unfortunately, this is only partly true. System PL/SQL packages are only in root but system table definition are replicated into all PDBs.<br />
<span id="more-10613"></span></p>
<p>This post is an extension of a <a href="https://www.dbi-services.com/blog/multitenant-dictionary-what-is-stored-only-in-cdbroot/" target="_blank" rel="noopener noreferrer">previous blog</a> post which was on 12<em>c</em>R1. This one is on 12<em>c</em>R2.</p>
<p>As I did at Open World and will do at DOAG, I show multitenant internals by creating a metadata link procedure. When I do a simple &#8216;describe&#8217; when connected to a PDB, the sql_trace shows that the session switches to the CDB$ROOT to get the procedure information:</p>
<pre><code>
*** 2017-11-05T16:17:36.339126+01:00 (CDB$ROOT(1))
=====================
PARSING IN CURSOR #140420856738440 len=143 dep=1 uid=0 oct=3 lid=0 tim=101728244788 hv=2206365737 ad='7f60a7f0' sqlid='9fjf75a1s4y19'
select procedure#,procedurename,properties,itypeobj#, properties2 from procedureinfo$ where obj#=:1 order by procedurename desc, overload# desc
END OF STMT</code></pre>
<p>All information about the system PL/SQL procedures is stored in the root only. The PDB has only a dummy row in OBJ$ to mention that it is a metadata link. And this is why you pay for the multitenant option: consolidation of all system dictionary objects into the root only. You save space (on disk and related memory) and you have only one place to upgrade. </p>
<p>But this is implemented only for some objects, like PL/SQL procedures, but not for others like table and indexes. If you &#8216;describe&#8217; a metadata link table when connected to a PDB you will not see any switch to CDB$ROOT in the sql_trace:</p>
<pre><code>
*** 2017-11-05T13:01:53.541231+01:00 (PDB1(3))
PARSING IN CURSOR #139835947128936 len=86 dep=1 uid=0 oct=3 lid=0 tim=98244321664 hv=2195287067 ad='75f823b8' sqlid='32bhha21dkv0v'
select col#,intcol#,charsetid,charsetform from col$ where obj#=:1 order by intcol# asc
END OF STMT
PARSE #139835947128936:c=0,e=158,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=3765558045,tim=98244321664
BINDS #139835947128936:
 Bind#0
  oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
  oacflg=08 fl2=1000001 frm=00 csi=00 siz=24 off=0
  kxsbbbfp=7f2e124fef10  bln=22  avl=03  flg=05
  value=747
EXEC #139835947128936:c=1000,e=603,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=3765558045,tim=98244322311
FETCH #139835947128936:c=0,e=15,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=4,plh=3765558045,tim=98244322342
FETCH #139835947128936:c=0,e=1,p=0,cr=0,cu=0,mis=0,r=1,dep=1,og=4,plh=3765558045,tim=98244322356
FETCH #139835947128936:c=0,e=4,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,plh=3765558045,tim=98244322369
STAT #139835947128936 id=1 cnt=2 pid=0 pos=1 obj=0 op='SORT ORDER BY (cr=3 pr=0 pw=0 str=1 time=16 us cost=3 size=234 card=13)'
STAT #139835947128936 id=2 cnt=2 pid=1 pos=1 obj=21 op='TABLE ACCESS CLUSTER COL$ (cr=3 pr=0 pw=0 str=1 time=11 us cost=2 size=234 card=13)'
STAT #139835947128936 id=3 cnt=1 pid=2 pos=1 obj=3 op='INDEX UNIQUE SCAN I_OBJ# (cr=2 pr=0 pw=0 str=1 time=6 us cost=1 size=0 card=1)'
CLOSE #139835947128936:c=0,e=1,dep=1,type=3,tim=98244322439
</code></pre>
<p>Here all information about the columns is read from COL$ in the PDB. And if you look at TAB$ (tables), COL$ (table columns), IND$ (indexes), CONS$ and CDEF$ (constraints), you will see that they contain rows in a PDB where no user objects have been created. This is the case for all information related to tables: they are stored in CDB$ROOT and replicated into all other containers: PDB$SEED and all user created PDB. Only the information related to non-data objects, are stored only in one container.</p>
<p>I&#8217;ve run a query to count the rows in CDB$ROOT and PDB$SEED and here is the result:<br />
<a href="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/CaptureMultitenantNumRows.png"><img loading="lazy" decoding="async" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2022/04/CaptureMultitenantNumRows.png" alt="CaptureMultitenantNumRows" width="1134" height="852" class="aligncenter size-full wp-image-19430" /></a></p>
<p>All rows in OBJ$ are replicated, which is expected because this is where the metadata link information is stored. But you see also all information related to tables that are also replicated, such as the 100000+ columns in COL$. And this is the reason why you do not see a big consolidation benefit when you look at the size of the SYSTEM tablespace in pluggable databases which do no contain any user data:</p>
<pre><code>
List of Permanent Datafiles
===========================
File Size(MB) Tablespace           RB segs Datafile Name
---- -------- -------------------- ------- ------------------------
1    820      SYSTEM               YES     /u01/oradata/CDB1A/system01.dbf
3    630      SYSAUX               NO      /u01/oradata/CDB1A/sysaux01.dbf
4    80       UNDOTBS1             YES     /u01/oradata/CDB1A/undotbs01.dbf
5    250      PDB$SEED:SYSTEM      NO      /u01/oradata/CDB1A/pdbseed/system01.dbf
6    390      PDB$SEED:SYSAUX      NO      /u01/oradata/CDB1A/pdbseed/sysaux01.dbf
7    5        USERS                NO      /u01/oradata/CDB1A/users01.dbf
8    100      PDB$SEED:UNDOTBS1    NO      /u01/oradata/CDB1A/pdbseed/undotbs01.dbf
9    270      PDB1:SYSTEM          YES     /u01/oradata/CDB1A/PDB1/system01.dbf
10   440      PDB1:SYSAUX          NO      /u01/oradata/CDB1A/PDB1/sysaux01.dbf
11   100      PDB1:UNDOTBS1        YES     /u01/oradata/CDB1A/PDB1/undotbs01.dbf
12   5        PDB1:USERS           NO      /u01/oradata/CDB1A/PDB1/users01.dbf
</code></pre>
<p>Here I have 250MB in PDB$SEED which is supposed to contain only links to the 820GB SYSTEM tablespace, but there is a lot more than that.</p>
<p>So, basically, not all the dictionary is consolidated in multitenant but only the non-data part such as those PL/SQL packages and the dictionary views definition. You can think about the multitenant option consolidation as an extension to sharing the Oracle Home among several databases. It concerns the software part only. But the part of the dictionary which contains data about system objects is replicated into all containers, and is read locally without a context switch. This also means that a patch or upgrade on them has to be run in all containers.</p>
<p>With the fact that some information is replicated and some are not, comes the complexity to manage that in the dictionary views, and this will be the subject of the <a href="https://www.dbi-services.com/blog/multitenant-internals-int-and-intint-views/" target="_blank" rel="noopener noreferrer">next blog post</a> about INT$INT$ views.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/multitenant-dictionary-what-is-consolidated-and-what-is-not/">Multitenant dictionary: what is consolidated and what is not</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/multitenant-dictionary-what-is-consolidated-and-what-is-not/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: www.dbi-services.com @ 2026-10-16 19:27:25 by W3 Total Cache
-->