<?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 - dbi Blog</title>
	<atom:link href="https://www.dbi-services.com/blog/tag/oracle/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dbi-services.com/blog/tag/oracle/</link>
	<description></description>
	<lastBuildDate>Thu, 15 Oct 2026 15:38:42 +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 - dbi Blog</title>
	<link>https://www.dbi-services.com/blog/tag/oracle/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>OCI-managed MCP Server: Troubleshooting ORA-12170 errors with Private Endpoint connectivity</title>
		<link>https://www.dbi-services.com/blog/oci-managed-mcp-server-troubleshooting-ora-12170-errors-with-private-endpoint-connectivity/</link>
					<comments>https://www.dbi-services.com/blog/oci-managed-mcp-server-troubleshooting-ora-12170-errors-with-private-endpoint-connectivity/#respond</comments>
		
		<dc:creator><![CDATA[Jérôme Witt]]></dc:creator>
		<pubDate>Thu, 15 Oct 2026 15:38:40 +0000</pubDate>
				<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[OCI]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[MCP]]></category>
		<category><![CDATA[Oracle]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47876</guid>

					<description><![CDATA[<p>We recently worked on a customer proof of concept (PoC) involving Oracle databases hosted in Oracle Cloud Infrastructure (OCI) within private VCN subnets. The goal was to evaluate OCI-managed MCP servers and determine whether extending their available toolsets with additional database operations could reduce reliance on agent skills. By exposing reusable operations as MCP tools, [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/oci-managed-mcp-server-troubleshooting-ora-12170-errors-with-private-endpoint-connectivity/">OCI-managed MCP Server: Troubleshooting ORA-12170 errors with Private Endpoint connectivity</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">We recently worked on a customer proof of concept (PoC) involving Oracle databases hosted in Oracle Cloud Infrastructure (OCI) within private VCN subnets.</p>



<p class="wp-block-paragraph">The goal was to evaluate OCI-managed MCP servers and determine whether extending their available toolsets with additional database operations could reduce reliance on agent skills. By exposing reusable operations as MCP tools, we aimed to reduce token consumption and associated costs while improving execution efficiency, without compromising the security of databases hosted in private VCN subnets.</p>



<h2 id="h-provision-oci-resources-when-rtfm-isn-t-enough" class="wp-block-heading">Provision OCI-resources: When RTFM Isn’t Enough</h2>



<p class="wp-block-paragraph">Provisioning an MCP server requires an underlying database connection that combines database credentials with network connectivity to the target database, typically through a private endpoint. This connection allows the MCP server to access the database securely while keeping it within the private VCN.</p>



<p class="wp-block-paragraph">The detailed setup procedure is beyond the scope of this article, as Oracle provides official documentation covering the prerequisites, configuration, and provisioning for <a href="https://docs.oracle.com/en-us/iaas/database-tools/doc/private-endpoints.html">Database Tools Private Endpoints</a></p>



<p class="wp-block-paragraph">In short, the documentation mentions using Zero Trust Packet Routing (ZPR) alongside, or instead of, Network Security Groups (NSGs) to control network access.</p>



<p class="wp-block-paragraph">So far, so good. We already use NSGs exclusively to manage network traffic.</p>



<p class="wp-block-paragraph">However, the documentation does not clearly spell out which specific NSG rules are required or whether ingress rules alone are sufficient. After digging deeper into the documentation, we found the missing piece of the puzzle:</p>



<p class="wp-block-paragraph"><a href="https://docs.oracle.com/en-us/iaas/database-management/doc/create-database-management-private-endpoint.html" target="_blank" rel="noreferrer noopener">Creating a Database Management Private Endpoint</a></p>



<p class="wp-block-paragraph">The key takeaway is that the Network Security Groups (NSGs) associated with the private endpoint must have appropriate ingress and egress rules to permit the required network traffic.</p>



<p class="wp-block-paragraph">This is where things got more interesting. Our environment already hosted several databases interconnected through Oracle database links, with ingress and egress rules in place for the relevant subnets, including the newly provisioned private endpoint. Yet, despite these existing network configurations, the MCP server still could not connect to the target database.</p>



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



<h2 id="h-ora-12170-cannot-connect-tcp-connect-timeout" class="wp-block-heading">ORA-12170: Cannot connect. TCP connect timeout</h2>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="936" height="134" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/OCI-DBaaS-edp-conn-ORA-12170.png" alt="" class="wp-image-47905" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/OCI-DBaaS-edp-conn-ORA-12170.png 936w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/OCI-DBaaS-edp-conn-ORA-12170-300x43.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/OCI-DBaaS-edp-conn-ORA-12170-768x110.png 768w" sizes="(max-width: 936px) 100vw, 936px" /></figure>



<p class="wp-block-paragraph">Clearly, something was missing. We therefore dug deeper into the private endpoint’s specific network security requirements to identify the root cause of the connectivity issue.<br></p>



<h2 class="wp-block-heading">Still Getting ORA-12170, But Why?</h2>



<p class="wp-block-paragraph">Hold on a minute! We quickly went through the OCI Console to double-check our network configuration:</p>



<ul class="wp-block-list">
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Verified the private endpoint&#8217;s NSG ingress and egress rules.</li>



<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Confirmed that the databases and the private endpoint were deployed in the same subnet.</li>
</ul>



<p class="wp-block-paragraph">Everything looked correct at first glance, yet the dreaded <code>ORA-12170: TNS:Connect timeout occurred</code> error persisted.</p>



<p class="wp-block-paragraph"><strong>What were we missing? Time to bring in Oracle Support!</strong></p>



<p class="wp-block-paragraph">While working with Oracle Support, we took a closer look at the NSG configuration and the private endpoint&#8217;s VNIC. Something caught our attention: an entry was missing from the list, but we hadn&#8217;t initially considered it relevant.</p>



<p class="wp-block-paragraph">This prompted us to inspect the private endpoint configuration in greater detail using the OCI Command Line Interface (OCI CLI).</p>



<pre class="wp-block-code"><code>jerome_witt@cloudshell:~ (eu-zurich-1)$ oci dbtools private-endpoint get \
  --private-endpoint-id ${EDP_OCID}  \
  --query 'data.{"nsg-ids": "nsg-ids", "private-endpoint-vnic-id": "private-endpoint-vnic-id", "security-attributes": "security-attributes"}' \
  --output json
{
  <strong>"nsg-ids": null</strong>,
  "private-endpoint-vnic-id": "ocid1.vnic.oc1.eu-zurich-1.xxxxxxxxxxxxxxxxx",
  "security-attributes": {}
}</code></pre>



<p class="wp-block-paragraph"><br>Oh! The nsg-ids field is null! That was the missing piece of the puzzle.</p>



<p class="wp-block-paragraph">After updating the private endpoint with the appropriate NSG, we ran the following OCI CLI command:</p>



<pre class="wp-block-code"><code>oci dbtools private-endpoint update \ 
  --private-endpoint-id ${EDP_OCID} \
  --nsg-ids '&#091;"${NSG_ATTACHED_TO_DB_OCID}"]'</code></pre>



<p class="wp-block-paragraph">And then, the magic happened! The database connection succeeded, and the dreaded ORA-12170 error was gone.</p>



<p class="wp-block-paragraph">After explicitly associating the appropriate NSG with the Database Tools private endpoint, connectivity was restored. The OCI CLI output had revealed that the <code>nsg-ids</code> field was <code>null</code>, a detail we had overlooked when initially checking the configuration through the OCI Console.</p>



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



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



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="347" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/Screenshot-2026-10-15-at-17.23.54-1024x347.png" alt="" class="wp-image-47907" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/Screenshot-2026-10-15-at-17.23.54-1024x347.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/Screenshot-2026-10-15-at-17.23.54-300x102.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/Screenshot-2026-10-15-at-17.23.54-768x261.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/10/Screenshot-2026-10-15-at-17.23.54.png 1120w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">This behavior may be related to recent changes in OCI as Oracle continues to rapidly develop and roll out its core Deep Data Security capabilities.</p>



<p class="wp-block-paragraph">What had initially looked like a correctly configured network turned out to be missing a crucial piece: the NSG association explicitly configured on the Database Tools private endpoint itself.</p>



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



<h2 id="h-lessons-learned" class="wp-block-heading">Lessons Learned</h2>



<ul class="wp-block-list">
<li>Never blindly trust the console! Always verify the underlying configuration using the OCI CLI.</li>



<li>Have fun troubleshooting!<strong> </strong>Sometimes, the smallest configuration detail makes all the difference.</li>
</ul>



<p class="wp-block-paragraph">By the way, it seems we&#8217;re not alone. Others have apparently encountered similar database connectivity issues when configuring OCI Database Tools but none were using NSGs exclusively.</p>



<p class="wp-block-paragraph">For further reference, these Oracle Support knowledge base articles may be useful:</p>



<ul class="wp-block-list">
<li>KB64948 — <em>How to Connect to the Autonomous Database (Private Endpoint Access)</em></li>



<li>KB42277 — <em>OCI — Enable Database Management Fails</em></li>
</ul>



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



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



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



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



<p class="has-small-font-size wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/oci-managed-mcp-server-troubleshooting-ora-12170-errors-with-private-endpoint-connectivity/">OCI-managed MCP Server: Troubleshooting ORA-12170 errors with Private Endpoint connectivity</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/oci-managed-mcp-server-troubleshooting-ora-12170-errors-with-private-endpoint-connectivity/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Import order matters for GoldenGate instantiation filtering</title>
		<link>https://www.dbi-services.com/blog/import-order-matters-for-goldengate-instantiation-filtering/</link>
					<comments>https://www.dbi-services.com/blog/import-order-matters-for-goldengate-instantiation-filtering/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Thu, 15 Oct 2026 15:37:47 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[19c]]></category>
		<category><![CDATA[DataPump]]></category>
		<category><![CDATA[expdp]]></category>
		<category><![CDATA[extract]]></category>
		<category><![CDATA[impdp]]></category>
		<category><![CDATA[Initial load]]></category>
		<category><![CDATA[instantiation]]></category>
		<category><![CDATA[migration]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[Partitioning]]></category>
		<category><![CDATA[replicat]]></category>
		<category><![CDATA[scn]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47906</guid>

					<description><![CDATA[<p>When migrating a large database with GoldenGate, most of the volume is sometimes in old partitions which no longer change. A common shortcut is to move them first with expdp/impdp, even before GoldenGate is set up. Everything else, the active partitions and the non-partitioned tables, is exported once the extract is running, with PREPARECSN set. [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/import-order-matters-for-goldengate-instantiation-filtering/">Import order matters for GoldenGate instantiation filtering</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">When migrating a large database with GoldenGate, most of the volume is sometimes in old partitions which no longer change. A common shortcut is to move them first with <code>expdp</code>/<code>impdp</code>, even before GoldenGate is set up. Everything else, the active partitions and the non-partitioned tables, is exported once the extract is running, with <code>PREPARECSN</code> set. <a href="https://www.dbi-services.com/blog/goldengate-instantiation-csn-explained/" target="_blank" rel="noopener noreferrer">Instantiation filtering</a> then makes the replicat skip what Data Pump already loaded.</p>



<p class="wp-block-paragraph">However, there is an important aspect here: <code>dba_apply_instantiated_objects</code> has <strong>one row per table</strong>. Every <code>impdp</code> overwrites it, and there is no way for GoldenGate or Data Pump to know which import should be considered the correct one for populating the <code>dba_apply_instantiated_objects</code> view. The old partitions and the live data of a table share that row, so in a multi-import initial load the last import always wins.</p>



<h2 id="standard-scenario-importing-old-partitions-first" class="wp-block-heading">Standard scenario: importing old partitions first</h2>



<p class="wp-block-paragraph">The old partitions of all partitioned tables are moved first, in one dump, before GoldenGate is involved. Since no GoldenGate extract exists yet, <code>PREPARECSN</code> has not run either:</p>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 dumpfile=old_part.dmp tables=SALES.ORDERS_PART:P_OLD,SALES.ITEMS_PART:P_OLD
. . exported "SALES"."ITEMS_PART":"P_OLD"                 5.523 KB       5 rows
. . exported "SALES"."ORDERS_PART":"P_OLD"                5.523 KB       5 rows
Job "SALES"."SYS_EXPORT_TABLE_01" successfully completed at Sun Oct 11 13:52:08 2026 elapsed 0 00:00:10

$ impdp sales/***@PDB2 dumpfile=old_part.dmp
. . imported "SALES"."ITEMS_PART":"P_OLD"                 5.523 KB       5 rows
. . imported "SALES"."ORDERS_PART":"P_OLD"                5.523 KB       5 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:52:12 2026 elapsed 0 00:00:02</code></pre>



<p class="wp-block-paragraph">Querying the instantiation view on the target returns nothing, as expected:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_database, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'SALES';

no rows selected</code></pre>



<p class="wp-block-paragraph">Data Pump only populates this view when the table was prepared with <code>PREPARECSN</code> before the export. These first imports are invisible to GoldenGate.</p>



<h3 id="starting-the-extract" class="wp-block-heading">Starting the extract</h3>



<p class="wp-block-paragraph">The extract connects to <code>PDB1</code> and captures the <code>SALES</code> schema into the trail <code>pt</code>. It is added, registered and started in the <code>adminclient</code>, logged in to <code>PDB1</code>. The registration SCN is where capture begins:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
Successfully logged into database PDB1.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 3&gt; ADD EXTRACT EXTPT, INTEGRATED TRANLOG, BEGIN NOW
2026-10-11T13:52:13Z  INFO    OGG-08100  Integrated Extract added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 4&gt; REGISTER EXTRACT EXTPT DATABASE
2026-10-11T13:52:14Z  INFO    OGG-02003  Extract group EXTPT successfully registered with database at SCN 189554050.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 5&gt; ADD EXTTRAIL pt, EXTRACT EXTPT
2026-10-11T13:52:14Z  INFO    OGG-08100  EXTTRAIL added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 6&gt; VIEW PARAMS EXTPT
EXTRACT EXTPT
USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
EXTTRAIL pt
TABLE SALES.*;

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 7&gt; START EXTRACT EXTPT
2026-10-11T13:52:14Z  INFO    OGG-00975  Extract group EXTPT starting.
2026-10-11T13:52:14Z  INFO    OGG-15426  Extract group EXTPT started.</code></pre>



<p class="wp-block-paragraph">Some workload follows on the live data, before <code>PREPARECSN</code>:</p>



<pre class="wp-block-code"><code>SQL&gt; INSERT INTO sales.orders_part VALUES (1001, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.orders_part VALUES (1002, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.items_part VALUES (1001, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.items_part VALUES (1002, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.customers VALUES (4, 'LIVE');

1 row created.

SQL&gt; COMMIT;

Commit complete.</code></pre>



<p class="wp-block-paragraph">Then the schema is prepared in the <code>adminclient</code>. <code>ADD SCHEMATRANDATA ... ALLCOLS</code> sets <code>PREPARECSN</code> on the schema in <code>NOWAIT</code> mode:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
Successfully logged into database PDB1.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 3&gt; ADD SCHEMATRANDATA SALES ALLCOLS
2026-10-11T13:52:15Z  INFO    OGG-01788  SCHEMATRANDATA has been added on schema "SALES".
2026-10-11T13:52:15Z  INFO    OGG-01976  SCHEMATRANDATA for scheduling columns has been added on schema "SALES".
2026-10-11T13:52:15Z  INFO    OGG-01977  SCHEMATRANDATA for all columns has been added on schema "SALES".
2026-10-11T13:52:15Z  INFO    OGG-10154  Schema level PREPARECSN set to mode NOWAIT on schema "SALES"</code></pre>



<h3 id="exporting-and-importing-the-live-data" class="wp-block-heading">Exporting and importing the live data</h3>



<p class="wp-block-paragraph">The active partitions are exported next, in a single <code>expdp</code>, deliberately <strong>without</strong> <code>FLASHBACK_SCN</code>. Data Pump chooses a consistent SCN itself, for each table:</p>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 dumpfile=active_part.dmp tables=SALES.ORDERS_PART:P_ACTIVE,SALES.ITEMS_PART:P_ACTIVE
FLASHBACK automatically enabled to preserve database integrity.
. . exported "SALES"."ITEMS_PART":"P_ACTIVE"                5.5 KB       2 rows
. . exported "SALES"."ORDERS_PART":"P_ACTIVE"               5.5 KB       2 rows
Job "SALES"."SYS_EXPORT_TABLE_01" successfully completed at Sun Oct 11 13:52:29 2026 elapsed 0 00:00:12</code></pre>



<p class="wp-block-paragraph">Live inserts after that export are therefore not in the dump:</p>



<pre class="wp-block-code"><code>SQL&gt; INSERT INTO sales.orders_part VALUES (1003, 'ACTIVE');

1 row created.

SQL&gt; INSERT INTO sales.items_part VALUES (1003, 'ACTIVE');

1 row created.

SQL&gt; COMMIT;

Commit complete.</code></pre>



<p class="wp-block-paragraph">Every other table goes in a third export, at the schema level, excluding the partitioned tables. Any number of normal tables fits in this one export, and a new partitioned table is added to the exclusion:</p>



<pre class="wp-block-code"><code>$ cat rest.par
dumpfile=rest.dmp
schemas=SALES
exclude=TABLE:"IN ('ORDERS_PART','ITEMS_PART')"</code></pre>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 parfile=rest.par
FLASHBACK automatically enabled to preserve database integrity.
. . exported "SALES"."CUSTOMERS"                          5.515 KB       4 rows
Job "SALES"."SYS_EXPORT_SCHEMA_01" successfully completed at Sun Oct 11 13:52:55 2026 elapsed 0 00:00:24</code></pre>



<p class="wp-block-paragraph">A live insert after this export is not in the dump either:</p>



<pre class="wp-block-code"><code>SQL&gt; INSERT INTO sales.customers VALUES (5, 'LIVE');

1 row created.

SQL&gt; COMMIT;

Commit complete.</code></pre>



<p class="wp-block-paragraph">The partitioned tables already exist on the target, so the active partitions are appended. A regular import creates the normal tables:</p>



<pre class="wp-block-code"><code>$ impdp sales/***@PDB2 dumpfile=active_part.dmp table_exists_action=append
Table "SALES"."ORDERS_PART" exists. Data will be appended to existing table but all dependent metadata will be skipped due to table_exists_action of append
Table "SALES"."ITEMS_PART" exists. Data will be appended to existing table but all dependent metadata will be skipped due to table_exists_action of append
. . imported "SALES"."ITEMS_PART":"P_ACTIVE"                5.5 KB       2 rows
. . imported "SALES"."ORDERS_PART":"P_ACTIVE"               5.5 KB       2 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:53:00 2026 elapsed 0 00:00:03

$ impdp sales/***@PDB2 dumpfile=rest.dmp
. . imported "SALES"."CUSTOMERS"                          5.515 KB       4 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:53:04 2026 elapsed 0 00:00:02</code></pre>



<p class="wp-block-paragraph">The instantiation view now has one row per table. The partitioned tables have SCNs from the second export, while <code>CUSTOMERS</code> has one from the third export:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_database, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'SALES'
ORDER BY source_object_name;

SOURCE_DATABASE SOURCE_OBJECT_NAME INSTANTIATION_SCN
--------------- ------------------ -----------------
PDB1            CUSTOMERS                  189585331
PDB1            ITEMS_PART                 189572011
PDB1            ORDERS_PART                189572008

3 rows selected.</code></pre>



<h3 id="replicat-with-instantiation-filtering" class="wp-block-heading">Replicat with instantiation filtering</h3>



<p class="wp-block-paragraph">Each instantiation SCN applies to the whole table, and we use instantiation filtering in the replicat. It is added and started in the <code>adminclient</code>:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
Successfully logged into database PDB2.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 3&gt; ADD REPLICAT REPPT, INTEGRATED, EXTTRAIL pt, CHECKPOINTTABLE OGGADMIN.CHECKPOINT
2026-10-11T13:53:05Z  INFO    OGG-08100  Integrated Replicat added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 4&gt; VIEW PARAMS REPPT
REPLICAT REPPT
DBOPTIONS ENABLE_INSTANTIATION_FILTERING
USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
MAP SALES.*, TARGET SALES.*;

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 5&gt; START REPLICAT REPPT
2026-10-11T13:53:05Z  INFO    OGG-00975  Replicat group REPPT starting.
2026-10-11T13:53:05Z  INFO    OGG-15445  Replicat group REPPT started.</code></pre>



<p class="wp-block-paragraph">Once the replicat has caught up, the target tables have the same number of rows as the source:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT COUNT(*) FROM sales.orders_part;

COUNT(*)
--------
       8

1 row selected.

SQL&gt; SELECT COUNT(*) FROM sales.items_part;

COUNT(*)
--------
       8

1 row selected.

SQL&gt; SELECT COUNT(*) FROM sales.customers;

COUNT(*)
--------
       5

1 row selected.</code></pre>



<p class="wp-block-paragraph">In the <code>adminclient</code>, we can see the transaction done after creating the extract, on each table:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; STATS REPLICAT REPPT, TOTAL, TABLE SALES.*

Sending STATS TOTAL TABLE SALES.* request to Replicat group REPPT ...


Start of statistics at 2026-10-11 13:53:18.


Integrated Replicat statistics:

        Total transactions                                 2.00
        Redirected                                         0.00
        Replicated procedures                              0.00
        DDL operations                                     0.00
        Stored procedures                                  0.00
        Datatype functionality                             0.00
        Operation type functionality                       0.00
        Event actions                                      0.00
        Database compatibility                             0.00
        Direct transactions ratio                          0.00%

Replicating from SALES.ORDERS_PART to PDB2.SALES.ORDERS_PART:

*** Total statistics since 2026-10-11 13:53:07 ***
    Total inserts                              1.00
    Total updates                              0.00
    Total deletes                              0.00
    Total upserts                              0.00
    Total discards                             0.00
    Total operations                           1.00

Replicating from SALES.ITEMS_PART to PDB2.SALES.ITEMS_PART:

*** Total statistics since 2026-10-11 13:53:07 ***
    Total inserts                              1.00
    Total updates                              0.00
    Total deletes                              0.00
    Total upserts                              0.00
    Total discards                             0.00
    Total operations                           1.00

Replicating from SALES.CUSTOMERS to PDB2.SALES.CUSTOMERS:

*** Total statistics since 2026-10-11 13:53:07 ***
    Total inserts                              1.00
    Total updates                              0.00
    Total deletes                              0.00
    Total upserts                              0.00
    Total discards                             0.00
    Total operations                           1.00

End of statistics.</code></pre>



<p class="wp-block-paragraph">The only inserts are the three rows added after the exports, one per table. There is no <code>ORA-00001</code>: the rows inserted before the exports were replayed from the trail and skipped by the instantiation filtering. Without <code>ENABLE_INSTANTIATION_FILTERING</code> they would have hit the primary key.</p>



<p class="wp-block-paragraph"><code>FLASHBACK_SCN</code> is not needed here. The replicat compares commit CSNs against <code>instantiation_scn</code>, not against the export’s flashback point. You only need <code>PREPARECSN</code> to have run before the export of the live data.</p>



<p class="wp-block-paragraph">Foreign keys between tables that are in different exports are something to keep in mind.</p>



<h2 id="wrong-order-importing-old-partitions-after" class="wp-block-heading">Wrong order: importing old partitions after</h2>



<p class="wp-block-paragraph">What happens if, for some reason, you run the initial load of the active partitions first, with <code>PREPARECSN</code> and instantiation filtering, and then migrate the old partitions? Let’s try this, by first adding <code>trandata</code>:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
Successfully logged into database PDB1.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 3&gt; ADD TRANDATA SALES.ORDERS_PART PREPARECSN NOWAIT
2026-10-11T13:53:42Z  INFO    OGG-15132  Logging of supplemental redo data enabled for table SALES.ORDERS_PART.
2026-10-11T13:53:42Z  INFO    OGG-15133  TRANDATA for scheduling columns has been added on table SALES.ORDERS_PART.
2026-10-11T13:53:42Z  INFO    OGG-15438  Enabling logical replication (with all keys) on auto capture table SALES.ORDERS_PART.
2026-10-11T13:53:42Z  INFO    OGG-15135  TRANDATA for instantiation CSN has been added on table SALES.ORDERS_PART.</code></pre>



<p class="wp-block-paragraph">The old partitions are exported before the extract exists:</p>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 dumpfile=b_old.dmp tables=SALES.ORDERS_PART:P_OLD
FLASHBACK automatically enabled to preserve database integrity.
. . exported "SALES"."ORDERS_PART":"P_OLD"                5.523 KB       5 rows
Job "SALES"."SYS_EXPORT_TABLE_01" successfully completed at Sun Oct 11 13:53:55 2026 elapsed 0 00:00:11</code></pre>



<p class="wp-block-paragraph">The extract is then registered and started, as in the first scenario:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb1 DOMAIN OracleGoldenGate
Successfully logged into database PDB1.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 3&gt; ADD EXTRACT EXTPT, INTEGRATED TRANLOG, BEGIN NOW
2026-10-11T13:53:57Z  INFO    OGG-08100  Integrated Extract added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 4&gt; REGISTER EXTRACT EXTPT DATABASE
2026-10-11T13:54:00Z  INFO    OGG-02003  Extract group EXTPT successfully registered with database at SCN 189596053.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 5&gt; ADD EXTTRAIL pt, EXTRACT EXTPT
2026-10-11T13:54:00Z  INFO    OGG-08100  EXTTRAIL added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb1@CDB01/PDB1) 6&gt; START EXTRACT EXTPT
2026-10-11T13:54:00Z  INFO    OGG-00975  Extract group EXTPT starting.
2026-10-11T13:54:00Z  INFO    OGG-15426  Extract group EXTPT started.</code></pre>



<p class="wp-block-paragraph">Rows <code>1001</code> and <code>1002</code> are inserted into <code>P_ACTIVE</code>, and the live data is exported. Row <code>1003</code> is inserted after the export:</p>



<pre class="wp-block-code"><code>$ expdp sales/***@PDB1 dumpfile=b_active.dmp tables=SALES.ORDERS_PART:P_ACTIVE
FLASHBACK automatically enabled to preserve database integrity.
. . exported "SALES"."ORDERS_PART":"P_ACTIVE"               5.5 KB       2 rows
Job "SALES"."SYS_EXPORT_TABLE_01" successfully completed at Sun Oct 11 13:54:09 2026 elapsed 0 00:00:08</code></pre>



<p class="wp-block-paragraph">This time, on the target, <strong>the live data is imported first</strong>:</p>



<pre class="wp-block-code"><code>$ impdp sales/***@PDB2 dumpfile=b_active.dmp
. . imported "SALES"."ORDERS_PART":"P_ACTIVE"               5.5 KB       2 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:54:14 2026 elapsed 0 00:00:02</code></pre>



<p class="wp-block-paragraph">For now, the boundary is above the registration SCN <code>189596053</code>:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'SALES'
AND source_object_name = 'ORDERS_PART';

SOURCE_OBJECT_NAME INSTANTIATION_SCN
------------------ -----------------
ORDERS_PART                189610870

1 row selected.</code></pre>



<p class="wp-block-paragraph">The old partitions are then imported:</p>



<pre class="wp-block-code"><code>$ impdp sales/***@PDB2 dumpfile=b_old.dmp table_exists_action=append
Table "SALES"."ORDERS_PART" exists. Data will be appended to existing table but all dependent metadata will be skipped due to table_exists_action of append
. . imported "SALES"."ORDERS_PART":"P_OLD"                5.523 KB       5 rows
Job "SALES"."SYS_IMPORT_FULL_01" successfully completed at Sun Oct 11 13:54:17 2026 elapsed 0 00:00:01</code></pre>



<p class="wp-block-paragraph">If we check the table again, the boundary is now below the registration SCN, and more importantly below the instantiation SCN of the live data !</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'SALES'
AND source_object_name = 'ORDERS_PART';

SOURCE_OBJECT_NAME INSTANTIATION_SCN
------------------ -----------------
ORDERS_PART                189594418

1 row selected.</code></pre>



<p class="wp-block-paragraph">This means that a replicat using instantiation filtering will not filter the transactions which happened before the first import. If we add the replicat in the <code>adminclient</code>, and start it:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; DBLOGIN USERIDALIAS oggadmin_pdb2 DOMAIN OracleGoldenGate
Successfully logged into database PDB2.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 3&gt; ADD REPLICAT REPPT, INTEGRATED, EXTTRAIL pt, CHECKPOINTTABLE OGGADMIN.CHECKPOINT
2026-10-11T13:54:19Z  INFO    OGG-08100  Integrated Replicat added.

OGG (http://localhost:7810 ogg_test_01 as oggadmin_pdb2@CDB02/PDB2) 4&gt; START REPLICAT REPPT
2026-10-11T13:54:19Z  INFO    OGG-00975  Replicat group REPPT starting.
2026-10-11T13:54:19Z  INFO    OGG-15445  Replicat group REPPT started.</code></pre>



<p class="wp-block-paragraph">The replicat reads the instantiation SCN <code>189594418</code> from the target. It is now older than the live data export, so the transactions that export already contains are no longer filtered, and the replicat fails on the first trail record:</p>



<pre class="wp-block-code"><code>OGG (http://localhost:7810 ogg_test_01) 2&gt; INFO REPLICAT REPPT

Replicat   REPPT     Last Started 2026-10-11 13:54   Status ABENDED
INTEGRATED
Checkpoint Lag       00:00:00 (updated 00:00:19 ago)
Log Read Checkpoint  File pt000000000
                     First Record  RBA 0
Encryption Profile   LocalWallet</code></pre>



<p class="wp-block-paragraph">The replicat report shows the boundary and the error:</p>



<pre class="wp-block-code"><code>2026-10-11 13:54:25  INFO    OGG-10155  Instantiation CSN filtering is enabled on table SALES.ORDERS_PART at CSN 189,594,418.
2026-10-11 13:54:31  WARNING OGG-02544  Unhandled error (ORA-00001: unique constraint (.) violated
ORA-00001: unique constraint (SALES.SYS_C0010450) violated) while processing the record at SEQNO 0, RBA 1793 in Integrated mode. REPLICAT will retry in Direct mode.</code></pre>



<p class="wp-block-paragraph"><code>1001</code> and <code>1002</code> were already on the target from the live-data import, but their commit CSNs are now above the boundary.</p>



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



<ul class="wp-block-list">
<li><code>instantiation_scn</code> is <strong>per table</strong>, not per partition. The last <code>impdp</code> will decide the boundary being used by instantiation filtering.</li>



<li><code>impdp</code> overwrites it <strong>unconditionally</strong>, with no warning.</li>



<li>When using instantiation filtering, always import the dump which should contain the boundary <strong>last</strong>.</li>



<li>Keep in mind that, depending on the context of your real replication, another instantiation method might be needed. Foreign keys, DDL replication, or many aspects a GoldenGate replication might need to address, were not covered in this blog.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/import-order-matters-for-goldengate-instantiation-filtering/">Import order matters for GoldenGate instantiation filtering</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/import-order-matters-for-goldengate-instantiation-filtering/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Removing instantiation filtering from a GoldenGate replicat</title>
		<link>https://www.dbi-services.com/blog/removing-instantiation-filtering-from-a-goldengate-replicat/</link>
					<comments>https://www.dbi-services.com/blog/removing-instantiation-filtering-from-a-goldengate-replicat/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Mon, 12 Oct 2026 06:59:00 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[26ai]]></category>
		<category><![CDATA[checkpoint]]></category>
		<category><![CDATA[csn]]></category>
		<category><![CDATA[DataPump]]></category>
		<category><![CDATA[enable_instantiation_filtering]]></category>
		<category><![CDATA[instantiation]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[replicat]]></category>
		<category><![CDATA[rest api]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47507</guid>

					<description><![CDATA[<p>In a recent blog, I explained how instantiation filtering works. DBOPTIONS ENABLE_INSTANTIATION_FILTERING makes a replicat discard any record whose commit CSN is at or below a table’s instantiation CSN. It applies everything above it. This replicat parameter should be kept during the whole initial load process. At some point the replicat has processed past the [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/removing-instantiation-filtering-from-a-goldengate-replicat/">Removing instantiation filtering from a GoldenGate replicat</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">In a recent blog, I explained <a href="https://www.dbi-services.com/blog/goldengate-instantiation-csn-explained/" target="_blank" rel="noopener noreferrer">how instantiation filtering works</a>. <code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code> makes a replicat discard any record whose commit CSN is at or below a table’s instantiation CSN. It applies everything above it. This replicat parameter should be kept during the whole initial load process.</p>



<p class="wp-block-paragraph">At some point the replicat has processed past the instantiation boundary for every table, and the filter has nothing left to do. Let’s answer the subsequent and very practical question: <strong>how to confirm, before editing the parameter file, that the replicat has cleared the boundary for every table it maps?</strong></p>



<h2 id="which-scn-should-i-check-" class="wp-block-heading">Which SCN should I check ?</h2>



<p class="wp-block-paragraph">To summarize what needs to be checked: <strong>it is safe to remove <code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code> once the replicat last applied CSN is greater than the instantiation CSN of every table it maps.</strong></p>



<p class="wp-block-paragraph">To answer this question, you have two sources of information:</p>



<ul class="wp-block-list">
<li>The per-table boundary, stored in <code>dba_apply_instantiated_objects</code>, with one row per source object.</li>



<li>The replicat last applied CSN, stored in the checkpoint table in the <code>log_csn</code> column.</li>
</ul>



<h2 id="sql-check" class="wp-block-heading">SQL check</h2>



<p class="wp-block-paragraph">Read the boundaries first:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_database, source_object_owner, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
ORDER BY instantiation_scn DESC;

SOURCE_DATABASE SOURCE_OBJECT_OWNER SOURCE_OBJECT_NAME INSTANTIATION_SCN
--------------- ------------------- ------------------ -----------------
PDB1            APP_PDB1            SALES                       52642820
PDB1            APP_PDB1            ORDERS                      52642649</code></pre>



<p class="wp-block-paragraph">Then read the replicat last applied CSN from its checkpoint table:</p>



<pre class="wp-block-code"><code>SELECT group_name, log_csn FROM oggadmin.checkpoint WHERE group_name = 'REP1';</code></pre>



<p class="wp-block-paragraph">You can safely remove <code>ENABLE_INSTANTIATION_FILTERING</code> when <code>log_csn</code> is greater than the <strong>highest</strong> <code>instantiation_scn</code> among the tables it maps. A one-shot verdict, scoped to a single source database:</p>



<pre class="wp-block-code"><code>SELECT 'REP1' AS group_name,
c.log_csn,
MAX(o.instantiation_scn) AS max_boundary,
CASE WHEN c.log_csn &gt; MAX(o.instantiation_scn) THEN 'SAFE TO REMOVE' ELSE 'NOT YET' END AS verdict
FROM dba_apply_instantiated_objects o
LEFT JOIN oggadmin.checkpoint c ON c.group_name = 'REP1'
WHERE o.source_database = 'PDB1'
GROUP BY c.log_csn;</code></pre>



<p class="wp-block-paragraph">Before a replicat starts for the first time, <code>log_csn</code> is <code>NULL</code> and the verdict is <code>NOT YET</code>:</p>



<pre class="wp-block-code"><code>GROUP_NAME LOG_CSN MAX_BOUNDARY VERDICT
---------- ------- ------------ -------
REP1                   52642820 NOT YET</code></pre>



<p class="wp-block-paragraph">A replicat may map more than one source database, or only a subset of the instantiated objects. Then narrow the join to the objects applied.</p>



<p class="wp-block-paragraph"><strong>WARNING:</strong> A replicat that has never started has no checkpoint row at all, so <code>log_csn</code> returns nothing. Treat that as “not safe”: there is no applied position, so it cannot have cleared anything. The verdict query above handles this case. It starts from the instantiated objects and <strong>left joins</strong> the checkpoint table. A missing row gives a <code>NULL</code> <code>log_csn</code>, and the verdict falls to <code>NOT YET</code> instead of the query returning no row at all.</p>



<p class="wp-block-paragraph">After some time, once the replicat catches up with the initial load, the same query returns:</p>



<pre class="wp-block-code"><code>GROUP_NAME LOG_CSN  MAX_BOUNDARY VERDICT
---------- -------- ------------ --------------
REP1       52643651     52642820 SAFE TO REMOVE</code></pre>



<p class="wp-block-paragraph">You can now delete the <code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code> parameter and restart your replicat.</p>



<h2 id="adminclient-check" class="wp-block-heading"><code>adminclient</code> check</h2>



<p class="wp-block-paragraph">From the <code>adminclient</code>, <code>INFO REPLICAT REP1, DETAIL</code> reports the applied CSN without using <code>sqlplus</code>:</p>



<pre class="wp-block-code"><code>OGG&gt; INFO REPLICAT REP1, DETAIL
...
Last Committed Transaction CSN value: 52643651</code></pre>



<p class="wp-block-paragraph">Compare that number against the <code>instantiation_scn</code> from the view exactly as above.</p>



<h2 id="rest-api-check" class="wp-block-heading">REST API check</h2>



<p class="wp-block-paragraph">The CSN is reachable over the REST API, just not from a GET endpoint. You need to execute a command against the replicat using the <a href="https://docs.oracle.com/en/database/goldengate/core/26/oggra/op-services-version-replicats-replicat-command-post.html" target="_blank" rel="noopener noreferrer">POST /services/{version}/replicats/{replicat}/command</a> endpoint. Using <code>execute_command_replicat</code> in the <a href="https://juliendelattre.com/tools/ogg-python-client/" target="_blank" rel="noopener noreferrer"><code>oggrestapi</code></a> Python client:</p>



<pre class="wp-block-code"><code>from oggrestapi import OGGRestAPI

client = OGGRestAPI(url="http://localhost:7810", username="ogg", password="ogg")

resp = client.execute_command_replicat("REP1", data={"command": "INFO", "arguments": "DETAIL"})
output = "".join(r&#091;"reply"])
print(output)</code></pre>



<pre class="wp-block-code"><code>Replicat   REP1      Last Started 2026-09-20 13:29   Status RUNNING
Checkpoint Lag       00:00:00 (updated 00:00:02 ago)
Process ID           102660
Log Read Checkpoint  File aa000000002
                     2026-09-20 13:30:17.000000  RBA 18926
Encryption Profile   LocalWallet

Current Log BSN value: 52643543

Last Committed Transaction CSN value: 52646310

  Extract Source                          Begin             End

  aa000000002                             2020-01-01 00:00  2026-09-20 13:30:17.00
  aa000000000                             * Initialized *   2020-01-01 00:00:00.00
  aa000000000                             * Initialized *   First Record</code></pre>



<p class="wp-block-paragraph">To be truly automated, <code>Last Committed Transaction CSN value</code> has to be parsed out:</p>



<pre class="wp-block-code"><code>import re

match = re.search(r"Last Committed Transaction CSN value: (\d+)", output)
applied_csn = int(match.group(1))
print(applied_csn)</code></pre>



<pre class="wp-block-code"><code>52646310</code></pre>



<h2 id="information-in-the-report-file" class="wp-block-heading">Information in the report file</h2>



<p class="wp-block-paragraph">When starting a replicat during an initial load, the report file is filled with the following lines:</p>



<pre class="wp-block-code"><code>OGG-10155  Instantiation CSN filtering is enabled on table PDB1.APP_PDB1.ORDERS at CSN 52,642,649.
OGG-10185  Instantiation CSN filtering on table PDB1.APP_PDB1.ORDERS has started at CSN 52642649.
OGG-10186  Instantiation CSN filtering on table PDB1.APP_PDB1.ORDERS has ended, now passing transactions starting at CSN 52642649.</code></pre>



<ul class="wp-block-list">
<li>If the trail the replicat reads on that start contains records <strong>below</strong> a boundary, it filters them, and these lines appear.</li>



<li>If the trail begins <strong>at or after</strong> the boundary, nothing is filtered and <strong>no line appears at all</strong>.</li>
</ul>



<h2 id="performance-impact-of-leaving-enable_instantiation_filtering" class="wp-block-heading">Performance impact of leaving <code>ENABLE_INSTANTIATION_FILTERING</code></h2>



<p class="wp-block-paragraph">When migrating a database online with GoldenGate, you want your replicat to catch up quickly. I tested whether keeping <code>ENABLE_INSTANTIATION_FILTERING</code> in the parameter file had any effect on the lag of a replicat. I found no significant statistical evidence of this.</p>



<h2 id="summary" class="wp-block-heading">Summary</h2>



<ul class="wp-block-list">
<li><code>ENABLE_INSTANTIATION_FILTERING</code> is <strong>safe to remove</strong> when the replicat <code>log_csn</code> (last applied CSN) is above the <code>instantiation_scn</code> of every table it maps.</li>



<li>Get the applied CSN from <code>oggadmin.checkpoint.log_csn</code> (<code>sqlplus</code>), <code>INFO REPLICAT REP1, DETAIL</code> (<code>adminclient</code>), or <code>execute_command_replicat("REP1", data={"command": "INFO", "arguments": "DETAIL"})</code> over REST, which returns the same information.</li>



<li>The <code><a class="ogg-code-link" href="https://juliendelattre.com/tools/goldengate-error-code-explorer/OGG-10155/" target="_blank" rel="noopener noreferrer">OGG-10155</a>/10185/10186</code> report lines appear <strong>only when the replicat filters records on that start</strong>.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/removing-instantiation-filtering-from-a-goldengate-replicat/">Removing instantiation filtering from a GoldenGate replicat</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/removing-instantiation-filtering-from-a-goldengate-replicat/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)</title>
		<link>https://www.dbi-services.com/blog/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2/</link>
					<comments>https://www.dbi-services.com/blog/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2/#respond</comments>
		
		<dc:creator><![CDATA[Louis Tochon]]></dc:creator>
		<pubDate>Wed, 07 Oct 2026 06:20:08 +0000</pubDate>
				<category><![CDATA[Database management]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[migration]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47235</guid>

					<description><![CDATA[<p>Translating 4,000 Oracle views, rebuilding indexes offline on Standard Edition, and compressing clustered index to save 1T.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2/">Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)</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">At the end of&nbsp;<a href="https://www.dbi-services.com/blog/archiving-5-tb-from-oracle-into-sql-server-a-migration-logbook-part-1/">part 1</a>&nbsp;the data was across, at 42,170 rows per second into a database that could not answer a single question. Every nonclustered index disabled, the largest tables loaded as heaps with no primary key, every foreign key switched off, and 4,784 views that mostly did not compile.</p>



<p class="wp-block-paragraph">This part is about putting it back: <strong>translating the views</strong>, <strong>rebuilding the keys and indexes under a Standard Edition licence</strong> that makes every build offline, <strong>and compressing 3.2 TB of heaps</strong> that turned out to be mostly empty space.</p>



<p class="wp-block-paragraph">The logbook resumes at day 5.</p>



<h2 id="h-day-5-four-thousand-views-to-convert" class="wp-block-heading">Day 5. Four thousand views to convert</h2>



<p class="wp-block-paragraph">4,658 of the 4,784 views live in a single schema, and nobody needed all of them. We went through the list with the client first and kept the ones that actually matter to the business, which brought the scope down to about 710. SSMA converted around 600 of those on its own.</p>



<p class="wp-block-paragraph">SSMA fails mostly on <code>CONNECT BY</code>, <code>START WITH</code> and <code>SYS_CONNECT_BY_PATH</code>. For some easy cases, <code>DECODE</code> to <code>CASE</code> and <code>NVL</code> handles it well, for the record. For more complex use case, we needed to find an automatic solution process. So first, we discussed with the client to know which view were critical and needed to be migrated. Then, with this list, we leveraged the power of AI to help us converting the remaining views based on the following workflow:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="786" height="1024" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-45-786x1024.png" alt="" class="wp-image-47241" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-45-786x1024.png 786w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-45-230x300.png 230w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-45-768x1001.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-45.png 1120w" sizes="(max-width: 786px) 100vw, 786px" /></figure>



<h3 id="h-the-workflow" class="wp-block-heading">The workflow</h3>



<p class="wp-block-paragraph">SSMA converts what it can. The failures get extracted from <code>dba_views </code>into files. Then, and this is the step that changes the economics, they get<strong> classified by pattern in the Oracle source</strong> rather than one at a time by error message.</p>



<p class="wp-block-paragraph">Translation itself was done in batches of one family at a time, outside production, on extracted files, <strong>with an AI assistant</strong> and a context file describing the type mapping decisions, the column types of the referenced tables and the target version. Then human review, tests against Oracle and finally, deployment.</p>



<p class="wp-block-paragraph">The catalogue of families is the real deliverable. Eight of them closed the great majority of the cases:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Family</th><th>Verdict</th></tr></thead><tbody><tr><td><code>+</code> concatenation on numerics (<code>col + '_' + col</code>)</td><td><code>CONCAT</code></td></tr><tr><td>Untyped NULL in a UNION, cast to <code>numeric(38,10)</code> against a <code>datetime2</code> sibling</td><td>retype the cast</td></tr><tr><td>Call to a package function that was never migrated</td><td>inline it if possible</td></tr><tr><td><code>ssma_oracle.*</code> helper functions</td><td>install the extension pack</td></tr><tr><td>XML extraction (<code>.extract().getStringVal()</code>)</td><td>rewrite part of the query</td></tr><tr><td><code>TO_TIMESTAMP_TZ</code></td><td>rewrite part of the query</td></tr><tr><td>Date arithmetic landing in float</td><td>redefine type mapping</td></tr><tr><td><code>CONNECT BY</code> used as a generator</td><td>materialise the level connectors</td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><strong><span style="text-decoration: underline">And a word on the AI</span></strong>, since that is the part people ask about. <strong>No data ever left the environment</strong>: only the view definitions were processed, and every test against the live database was run by hand so we kept control of the process. We could have added MCP connectors and let a local model run the tests itself, but with the number of days allocated to the project and the small number of views left to fix, doing it manually was simpler. Around 85 percent of the views <strong>wanted by the client</strong> had already come through SSMA.</p>



<h2 id="h-day-6-putting-the-indexes-back-and-making-the-database-smaller" class="wp-block-heading">Day 6. Putting the indexes back and making the database smaller</h2>



<h3 id="h-recovering-what-i-had-thrown-away" class="wp-block-heading">Recovering what I had thrown away</h3>



<p class="wp-block-paragraph">The primary keys I dropped to load into heaps had left no trace on the SQL Server side. Nothing in <code>sys.indexes</code>, no disabled definition, nothing.</p>



<h3 id="h-what-standard-edition-costs" class="wp-block-heading">What Standard Edition costs</h3>



<p class="wp-block-paragraph">Index builds are offline and single-threaded. The table is locked for the duration, metadata reads queue behind it, but we managed to run 4 builds in parallel. <code>SORT_IN_TEMPDB = OFF</code> for the clustered builds. The sort covers the entire table, and 36 GB of tempdb was never going to hold a 900 GB sort.</p>



<p class="wp-block-paragraph">Meanwhile <code>percent_complete</code> sits at 0 for the whole build, with the serene confidence of something that has never been asked to account for itself. It works for <code>BACKUP DATABASE</code>. It does not work here. Real progress lives in <code><a href="https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-objects/sys-dm-exec-query-profiles-transact-sql?view=sql-server-ver17">sys.dm_exec_query_profile</a>s</code>, operator by operator :</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT physical_operator_name, row_count, estimate_row_count,
       CAST(row_count*100.0/NULLIF(estimate_row_count,0) AS decimal(5,1)) AS pct
FROM sys.dm_exec_query_profiles WITH (NOLOCK)
WHERE session_id = &lt;spid of the build&gt; 
ORDER BY node_id;

</pre></div>

<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="399" height="87" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-46.png" alt="" class="wp-image-47252" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-46.png 399w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-46-300x65.png 300w" sizes="auto, (max-width: 399px) 100vw, 399px" /></figure>
</div>


<p class="wp-block-paragraph">Those are not three parallel counters. They are three sequential phases of one pipeline, and only one of them moves at a time. The scan reads the source table, and while it runs its percentage is the only honest progress figure available. The sort is a blocking operator: it cannot emit a single row until it has consumed all 31.9 million, so it stays at zero through the whole scan and then through the whole sort. That is the blind spot. Once the scan reaches 100 percent you get a long stretch where all three counters report nothing while the server works flat out, and if the sort spills to disk that stretch can outlast the other two phases combined. Index Insert starts moving only when the sort begins handing rows over, and it is the first number that means the end is actually in sight.</p>



<h3 id="h-why-compress-and-what-it-returned" class="wp-block-heading">Why compress, and what it returned</h3>



<p class="wp-block-paragraph">On a read-only database, PAGE compression has no recurring write cost. You keep the read benefit and pay nothing back. The argument is rarely this clean.</p>



<p class="wp-block-paragraph">Compression trades CPU for I/O, and on an archive that trade is one-sided. These pages are read rarely, and when they are, nothing heavy is competing for the processor, so the decompression cost lands on a machine with nothing better to do. What you get back is density: more rows per page, which means fewer reads to find the one row somebody asked for and more of the table sitting in memory at once.</p>



<p class="wp-block-paragraph">The data was unusually well suited to it, for a reason worth spelling out. These tables carry roughly 200 numeric columns, mostly null, plus five <code>nvarchar(200)</code> aggregation labels that repeat endlessly. Here is the part that surprises people: <strong>a fixed-length numeric column that is NULL still occupies its full width</strong> in SQL Server. “Mostly null” saves nothing before compression. It is precisely why the heap was that large, and precisely what the page dictionary destroys.</p>



<p class="wp-block-paragraph">The allocated space per row tells the story:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Table</th><th>Rows</th><th>GB (heap)</th><th>bytes allocated per row</th></tr></thead><tbody><tr><td>TABLE1</td><td>251,777,518</td><td>960.5</td><td>~4,100</td></tr><tr><td>TABLE2</td><td>229,881,717</td><td>876.9</td><td>~4,100</td></tr><tr><td>TABLE3</td><td>343,997,550</td><td>874.8</td><td>~2,700</td></tr><tr><td>TABLE4</td><td>447,972,849</td><td>227.9</td><td>~550</td></tr><tr><td>TABLE5</td><td>519,727,931</td><td>120.2</td><td>~250</td></tr><tr><td>TABLE6</td><td>505,819,544</td><td>117.0</td><td>~250</td></tr></tbody></table></figure>



<p class="wp-block-paragraph"><strong>Three tables hold 2.7 TB of the 3.2 TB total</strong>. At around 4 KB of allocated space per row against an 8 KB page, the top two fit around two rows per page and waste most of what is left.</p>



<p class="wp-block-paragraph">Then the first build, on 505,819,544 rows with a five-column primary key:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
ALTER TABLE &lt;TABLE6&gt;
ADD CONSTRAINT PK_TABLE6 PRIMARY KEY CLUSTERED
    (&lt;COLUMNS_NAME&gt;)
WITH (DATA_COMPRESSION = PAGE, SORT_IN_TEMPDB = OFF);

</pre></div>


<p class="wp-block-paragraph"><strong>117 GB to 30.7 GB. A ratio of 3.81.</strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="721" height="121" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-47.png" alt="" class="wp-image-47258" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-47.png 721w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/image-47-300x50.png 300w" sizes="auto, (max-width: 721px) 100vw, 721px" /></figure>



<p class="wp-block-paragraph">Finally, as you can see above, more than 2.5 T have been reclaimed (datafiles were full with the heaps). Compression has made huge benefits and that means less data on disks, which means fewer pages to take in the buffer (less IO).</p>



<h3 id="h-the-script-that-puts-everything-back" class="wp-block-heading">The script that puts everything back</h3>



<p class="wp-block-paragraph">Across every schema that is two cursors, one rebuilding the disabled indexes with compression and one re-enabling the foreign keys. It is driven by the same&nbsp;<code>#Schemas</code>&nbsp;table as the disable script in part 1:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
SET NOCOUNT ON;
DECLARE @Execute bit = 0;   -- 0 = preview ; 1 = execute
  
DECLARE @cmd nvarchar(max);
DECLARE @n   int = 0;
  
DECLARE reb_cur CURSOR LOCAL FAST_FORWARD FOR
    SELECT N&#039;ALTER INDEX &#039; + QUOTENAME(i.name)
         + N&#039; ON &#039; + QUOTENAME(s.name) + N&#039;.&#039; + QUOTENAME(t.name)
         + N&#039; REBUILD WITH (DATA_COMPRESSION = PAGE);&#039;
    FROM   sys.indexes  i
    JOIN   sys.tables   t ON i.object_id = t.object_id
    JOIN   sys.schemas  s ON t.schema_id = s.schema_id
    WHERE  s.name IN (SELECT schema_name FROM #Schemas)
      AND  i.is_disabled = 1          
    ORDER BY s.name, t.name, i.name;
  
OPEN reb_cur;
FETCH NEXT FROM reb_cur INTO @cmd;
WHILE @@FETCH_STATUS = 0
BEGIN
    SET @n += 1;
    PRINT @cmd;
    IF @Execute = 1
    BEGIN
        BEGIN TRY EXEC sys.sp_executesql @cmd; END TRY
        BEGIN CATCH PRINT N&#039;   -- FAILED: &#039; + ERROR_MESSAGE(); END CATCH
    END
    FETCH NEXT FROM reb_cur INTO @cmd;
END
CLOSE reb_cur;
DEALLOCATE reb_cur;
  
PRINT N&#039;---&#039;;
PRINT CONVERT(nvarchar(10), @n) + N&#039; index &#039;
    + CASE WHEN @Execute = 1 THEN N&#039;rebuilt.&#039; ELSE N&#039;to be rebuilt (preview).&#039; END;
GO
  
  
SET NOCOUNT ON;
DECLARE @Execute bit = 1;   -- 0 = preview ; 1 = executer
  
DECLARE @cmd nvarchar(max);
DECLARE @n   int = 0;
  
DECLARE fkc_cur CURSOR LOCAL FAST_FORWARD FOR
    SELECT N&#039;ALTER TABLE &#039; + QUOTENAME(s.name) + N&#039;.&#039; + QUOTENAME(t.name)
         + N&#039; WITH NOCHECK CHECK CONSTRAINT &#039; + QUOTENAME(fk.name) + N&#039;;&#039;
    FROM   sys.foreign_keys fk
    JOIN   sys.tables  t ON fk.parent_object_id = t.object_id
    JOIN   sys.schemas s ON t.schema_id = s.schema_id
    WHERE  s.name IN (SELECT schema_name FROM #Schemas);
  
OPEN fkc_cur;
FETCH NEXT FROM fkc_cur INTO @cmd;
WHILE @@FETCH_STATUS = 0
BEGIN
    SET @n += 1;
    PRINT @cmd;
    IF @Execute = 1
    BEGIN
        BEGIN TRY EXEC sys.sp_executesql @cmd; END TRY
        BEGIN CATCH PRINT N&#039;   -- FAILED : &#039; + ERROR_MESSAGE(); END CATCH
    END
    FETCH NEXT FROM fkc_cur INTO @cmd;
END
CLOSE fkc_cur;
DEALLOCATE fkc_cur;
  
PRINT N&#039;---&#039;;
PRINT CONVERT(nvarchar(10), @n) + N&#039; FK &#039;
    + CASE WHEN @Execute = 1 THEN N&#039;activated and validated.&#039; ELSE N&#039;to be activated (preview).&#039; END;
GO

</pre></div>


<h3 id="h-the-foreign-keys-which-turned-out-to-be-free" class="wp-block-heading">The foreign keys, which turned out to be free</h3>



<p class="wp-block-paragraph">All 6,072 foreign keys were already back, active and marked not trusted, because the loading script re-enabled them with <code>NOCHECK</code>. Revalidating them with <code>WITH CHECK</code> would mean full joins against the largest tables in the database.</p>



<p class="wp-block-paragraph">On a read-only archive whose source integrity <strong>was enforced by Oracle</strong> for fifteen years, that buys one thing: the optimizer regains the right to eliminate redundant joins. Worth having on a schema of deeply nested views, not worth days of scanning here. They stayed not trusted.</p>



<h2 id="h-day-7-testing-600-views" class="wp-block-heading">Day 7. Testing 600 views</h2>



<p class="wp-block-paragraph">The principle is simple: run every view, record what happened, and never lose the record. Each one gets a <code>SELECT TOP (100) *</code> inside a <code>TRY/CATCH</code>, so a view that fails is logged rather than fatal, and the outcome lands in a table with its duration in milliseconds. The cursor only picks up views that are not already recorded, which means an interrupted run resumes where it stopped instead of starting over, and the duration column is what you sort on once it is done:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
SET NOCOUNT ON;
 
IF OBJECT_ID(&#039;dbo.ViewTestResults&#039;) IS NULL
    CREATE TABLE dbo.ViewTestResults (
        schema_name  sysname,
        view_name    sysname,
        statut       varchar(20),
        duree_ms     int,
        message      nvarchar(2048),
        tested_at    datetime2 DEFAULT SYSDATETIME(),
        CONSTRAINT PK_ViewTestResults PRIMARY KEY (schema_name, view_name)
    );
 
DECLARE @sch sysname, @vue sysname, @sql nvarchar(max), @t0 datetime2, @ms int, @i int = 0, @n int;
 
SELECT @n = COUNT(*)
FROM   sys.views v JOIN sys.schemas s ON v.schema_id = s.schema_id
WHERE  s.name = &#039;SCDAT&#039;
  AND  NOT EXISTS (SELECT 1 FROM dbo.ViewTestResults r
                   WHERE r.schema_name = s.name AND r.view_name = v.name);
 
RAISERROR(&#039;=== %d view remaining ===&#039;, 0, 1, @n) WITH NOWAIT;
 
DECLARE c CURSOR FAST_FORWARD FOR
  SELECT s.name, v.name
  FROM   sys.views v JOIN sys.schemas s ON v.schema_id = s.schema_id
  WHERE  s.name = &#039;SCDAT&#039;
    AND  NOT EXISTS (SELECT 1 FROM dbo.ViewTestResults r
                     WHERE r.schema_name = s.name AND r.view_name = v.name)
  ORDER  BY v.name;
 
OPEN c;
FETCH NEXT FROM c INTO @sch, @vue;
WHILE @@FETCH_STATUS = 0
BEGIN
    SET @i += 1;
    RAISERROR(&#039;&#x5B;%d/%d] %s.%s&#039;, 0, 1, @i, @n, @sch, @vue) WITH NOWAIT;
 
    SET @t0 = SYSDATETIME();
    BEGIN TRY
        SET @sql = N&#039;SELECT TOP (100) * FROM &#039; + QUOTENAME(@sch) + N&#039;.&#039; + QUOTENAME(@vue) + N&#039;;&#039;;
        EXEC sp_executesql @sql;
        SET @ms = DATEDIFF(MILLISECOND, @t0, SYSDATETIME());
        INSERT dbo.ViewTestResults (schema_name, view_name, statut, duree_ms, message)
        VALUES (@sch, @vue, &#039;OK&#039;, @ms, NULL);
    END TRY
    BEGIN CATCH
        SET @ms = DATEDIFF(MILLISECOND, @t0, SYSDATETIME());
        INSERT dbo.ViewTestResults (schema_name, view_name, statut, duree_ms, message)
        VALUES (@sch, @vue, &#039;FAILED&#039;, @ms, ERROR_MESSAGE());
        RAISERROR(&#039;    -&gt; FAILED: %s&#039;, 0, 1, @vue) WITH NOWAIT;
    END CATCH
 
    FETCH NEXT FROM c INTO @sch, @vue;
END
CLOSE c; DEALLOCATE c;
 
RAISERROR(&#039;=== Done ===&#039;, 0, 1) WITH NOWAIT;
 
-- summary
SELECT statut, COUNT(*) AS nb, SUM(duree_ms) AS duree_totale_ms
FROM dbo.ViewTestResults GROUP BY statut;
 
-- failed
SELECT schema_name, view_name, duree_ms, message
FROM dbo.ViewTestResults WHERE statut=&#039;FAILED&#039; ORDER BY view_name;
 
-- all
SELECT schema_name, view_name, statut, duree_ms
FROM dbo.ViewTestResults ORDER BY duree_ms DESC;

</pre></div>


<p class="wp-block-paragraph">The result looked excellent: <strong>604 views OK, zero errors</strong>, against real data. Six skipped because they are not implemented to be run without an explicit filter added. <code>SELECT TOP 100 *</code> without a filter <strong>is not a representative test</strong> but still confirms that definition and joins are working fine. So: test with the filter the users will actually write. A view that only misbehaves when queried in a way nobody queries it is not a broken view, but you cannot know that from a green tick.</p>



<h2 id="h-day-8-closing-the-hatch" class="wp-block-heading">Day 8. Closing the hatch</h2>



<p class="wp-block-paragraph">Database set to read-only, final backup taken and user&#8217;s specific queries tested with success, the story now comes to a happy end.</p>



<h2 id="h-what-i-would-tell-the-next-person" class="wp-block-heading">What I would tell the next person</h2>



<p class="wp-block-paragraph">An archive migration is not a cheap application migration. It is a different decision set, and read-only is the input that drives most of it. 25,000 triggers are pointless, PAGE compression free, and untrusted foreign keys perfectly acceptable. It even tells you how to index: broadly and in advance, because nobody can tell you which row an auditor will want in three years. Before anything else, ask what the target will be used for. The answer rewrites the plan before the first byte moves.</p>



<p class="wp-block-paragraph">Measure, and distrust anything that presents itself as progress. The SSMA counter refreshes per batch and sits still for minutes at a time. <code>percent_complete </code>stays at zero for an entire index build. A green tick on<code> SELECT TOP 100</code> confirms that the joins resolve and nothing beyond that. In every one of those cases the real number was sitting in a DMV.</p>



<p class="wp-block-paragraph">On views, the conversion rate is the least useful number in the report. What made four thousand views a finite piece of work was not the translation but the two steps around it: agreeing the real scope with the client first, then classifying what remained by pattern in the Oracle source rather than one error message at a time. <strong>An AI assistant</strong> translates a family of twenty views very well. It validates nothing, and the test against the source stays yours.</p>



<p class="wp-block-paragraph">Compression on an archive is the rare decision with nothing to weigh against it. 117 GB down to 30.7 GB on the first table, <strong>more than 2.5 TB</strong> reclaimed across the set, and not a single write to pay it back with. If your heap is mostly null numeric columns, it is bigger than you think, and it will compress better than you expect.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2/">Archiving 5 TB from Oracle into SQL Server: views, indexes and compression (part 2)</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/archiving-5-tb-from-oracle-into-sql-server-views-indexes-and-compression-part-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Oracle Fusion Middleware to the Cloud</title>
		<link>https://www.dbi-services.com/blog/oracle-fusion-middleware-to-the-cloud/</link>
					<comments>https://www.dbi-services.com/blog/oracle-fusion-middleware-to-the-cloud/#respond</comments>
		
		<dc:creator><![CDATA[David Diab]]></dc:creator>
		<pubDate>Thu, 01 Oct 2026 07:02:26 +0000</pubDate>
				<category><![CDATA[Application integration & Middleware]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Non classifié(e)]]></category>
		<category><![CDATA[Fusion Middleware]]></category>
		<category><![CDATA[Oracle]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=45506</guid>

					<description><![CDATA[<p>More and more often, we are contacted to assess, challenge, and advise on the migration of Oracle Fusion Middleware platforms to the cloud. These discussions usually involve critical environments based on Oracle WebLogic Server, Oracle Forms, and sometimes Oracle Reports. The objective is rarely just to “move to the cloud”. It is about understanding what [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/oracle-fusion-middleware-to-the-cloud/">Oracle Fusion Middleware to the Cloud</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">More and more often, we are contacted to assess, challenge, and advise on the migration of Oracle Fusion Middleware platforms to the cloud.</p>



<p class="wp-block-paragraph">These discussions usually involve critical environments based on Oracle WebLogic Server, Oracle Forms, and sometimes Oracle Reports. The objective is rarely just to “move to the cloud”. It is about understanding what can realistically be migrated, what should be modernized, what must be validated, and where the main technical, operational, and licensing risks are.</p>



<span id="more-45506"></span>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="576" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/FMW-to-Cloud-1024x576.png" alt="" class="wp-image-46947" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/FMW-to-Cloud-1024x576.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/FMW-to-Cloud-300x169.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/FMW-to-Cloud-768x432.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/FMW-to-Cloud-1536x864.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/FMW-to-Cloud.png 1672w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Based on these discussions, I would like to share a few practical considerations that may be useful when evaluating a cloud migration strategy for Oracle Fusion Middleware workloads.</p>



<h2 id="h-lift-and-shift-is-not-a-shortcut-it-is-a-controlled-migration-strategy" class="wp-block-heading">Lift and shift is not a shortcut, it is a controlled migration strategy</h2>



<p class="wp-block-paragraph">For Oracle WebLogic, Forms and Reports platforms, a cloud migration should not automatically be presented as an application modernization project.</p>



<p class="wp-block-paragraph">In many cases, the first realistic step is a <strong>lift-and-shift migration</strong>. The goal is not to rewrite Forms modules, redesign the full application, or replace every legacy component immediately. The goal is to rebuild a controlled target platform in the cloud while preserving the application behavior as much as possible.</p>



<p class="wp-block-paragraph">However, lift and shift does not mean copying virtual machines and hoping that everything will work.</p>



<p class="wp-block-paragraph">A serious migration requires a detailed comparison between the existing platform and the target one: WebLogic domain configuration, Managed Servers, Datasources, Wallets, Certificates, JVM version, Patch level, Forms runtime configuration, Reports configuration, File system dependencies, Fonts, Printers, Batch jobs, Monitoring, Backup, Restart procedures, and Operational scripts.</p>



<p class="wp-block-paragraph">Oracle documents several WebLogic migration approaches to <a href="https://www.oracle.com/cloud/" target="_blank" rel="noreferrer noopener">OCI</a>, including manual deployment, WebLogic Deploy Tooling, and WLST-based deployment scripts targeted to a new domain. Oracle also describes WebLogic Server for OCI as a way to provision a WebLogic domain in OCI more quickly than a traditional on-premises setup.</p>



<p class="wp-block-paragraph">This is why lift and shift should be seen as an engineering exercise, not as a low-effort infrastructure move.</p>



<h2 id="h-why-oci-is-naturally-aligned-with-oracle-fusion-middleware" class="wp-block-heading">Why OCI is naturally aligned with Oracle Fusion Middleware</h2>



<p class="wp-block-paragraph">It would not be accurate to say that Oracle Fusion Middleware can only run on OCI. Oracle’s own cloud licensing policy lists AWS, Azure, and Google Cloud Platform as &#8220;Authorized Cloud Environments&#8221; for specific Oracle software licensing rules. The same policy also explains how vCPUs must be counted in these environments and states that the Oracle Processor Core Factor Table is not applicable there.</p>



<p class="wp-block-paragraph">So the real point is not that other clouds are impossible. The point is that <strong>OCI is naturally aligned with Oracle Fusion Middleware workloads</strong>.</p>



<p class="wp-block-paragraph">For WebLogic, Oracle provides OCI-specific deployment models, Marketplace-based options, and documented migration patterns. For Kubernetes-based deployments, Oracle also documents WebLogic Server for OKE using a Marketplace stack, including lifecycle-management components such as Jenkins and the WebLogic Kubernetes Operator.</p>



<p class="wp-block-paragraph">From an operational and licensing perspective, OCI also gives Oracle customers options such as BYOL and Universal Credits-based models. Oracle’s Support Rewards FAQ states that BYOL services on the Universal Credits Model rate card are eligible for the Support Rewards program.</p>



<p class="wp-block-paragraph">This does not remove the need for a proper license review. But it does mean that for Oracle middleware workloads, OCI often provides a more Oracle-native discussion: infrastructure, middleware, licensing, support model, and target architecture can be addressed within the same ecosystem.</p>



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



<h2 id="h-oracle-reports-must-be-treated-as-a-specific-risk" class="wp-block-heading">Oracle Reports must be treated as a specific risk</h2>



<p class="wp-block-paragraph">For Oracle Forms and WebLogic, a structured lift-and-shift approach to OCI can be a realistic option. For Oracle Reports, the discussion must be more careful.</p>



<p class="wp-block-paragraph">Oracle Reports is still present in many enterprise Forms platforms, but it has technical characteristics that must be validated before any cloud migration. Reports cluster members or clients use multicast to discover other nodes, and that there is no workaround for using multicast in that context.</p>



<p class="wp-block-paragraph">This is a critical point because multicast is generally not available in all cloud network architectures.</p>



<p class="wp-block-paragraph">In practical terms, Oracle Reports should not be treated as a simple checkbox in the migration plan. It requires a dedicated technical validation: Reports Server configuration, naming service, generated output, cache behavior, fonts, printers, file paths, batch execution, restart behavior, and integration with the Forms application.</p>



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



<p class="wp-block-paragraph">The conclusion is therefore simple: WebLogic and Forms can be good candidates for a controlled lift-and-shift migration to OCI. Oracle Reports must be assessed separately, tested carefully, and challenged architecturally.</p>



<p class="wp-block-paragraph">OCI can reduce complexity for Oracle Fusion Middleware migrations, but it does not remove the need for deep product expertise. A successful migration is not only a cloud project, it is a WebLogic, Forms, Reports, licensing, operations, and validation project.</p>



<p class="wp-block-paragraph">If you are planning to migrate an Oracle Fusion Middleware platform to the cloud, or if you simply want to assess whether such a migration is realistic for your environment, feel free to contact me. I would be happy to help you challenge the architecture, identify the risks, and define a pragmatic migration approach.</p>



<p class="wp-block-paragraph">More related blogs <a href="https://www.dbi-services.com/blog/category/application-integration-middleware/weblogic/" target="_blank" rel="noreferrer noopener">here</a>.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/oracle-fusion-middleware-to-the-cloud/">Oracle Fusion Middleware to the Cloud</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-fusion-middleware-to-the-cloud/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>GoldenGate instantiation CSN explained</title>
		<link>https://www.dbi-services.com/blog/goldengate-instantiation-csn-explained/</link>
					<comments>https://www.dbi-services.com/blog/goldengate-instantiation-csn-explained/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Mon, 28 Sep 2026 06:12:00 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[26]]></category>
		<category><![CDATA[26ai]]></category>
		<category><![CDATA[aftercsn]]></category>
		<category><![CDATA[atcsn]]></category>
		<category><![CDATA[csn]]></category>
		<category><![CDATA[DataPump]]></category>
		<category><![CDATA[expdp]]></category>
		<category><![CDATA[extract]]></category>
		<category><![CDATA[flashback_scn]]></category>
		<category><![CDATA[impdp]]></category>
		<category><![CDATA[Initial load]]></category>
		<category><![CDATA[instantiation]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[ORA-01555]]></category>
		<category><![CDATA[preparecsn]]></category>
		<category><![CDATA[replicat]]></category>
		<category><![CDATA[scn]]></category>
		<category><![CDATA[snapshot too old]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=47082</guid>

					<description><![CDATA[<p>For more than ten years (GoldenGate 12.2, released in 2015), GoldenGate has provided a way of performing initial load of tables with a per-table instantiation CSN. But when migrating a database or just performing the initial load of a single table to prepare a GoldenGate replication, the known procedure is very often still the old [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-instantiation-csn-explained/">GoldenGate instantiation CSN explained</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">For more than ten years (GoldenGate 12.2, released in 2015), GoldenGate has provided a way of performing initial load of tables with a <strong>per-table instantiation CSN</strong>.</p>



<p class="wp-block-paragraph">But when migrating a database or just performing the initial load of a single table to prepare a GoldenGate replication, the known procedure is very often still the old one: start an extract, note down an SCN, run <code>expdp</code> with <code>FLASHBACK_SCN=&lt;that SCN&gt;</code>, import the data on the target, and finally start a replicat with the <code>AFTERCSN</code> clause.</p>



<p class="wp-block-paragraph">It takes a long time to get used to new habits, so let’s refresh your memory, explaining how instantiation CSN work in GoldenGate.</p>



<h2 id="historic-flashback_scn--aftercsn-initial-load" class="wp-block-heading">Historic <code>FLASHBACK_SCN</code> + <code>AFTERCSN</code> initial load</h2>



<p class="wp-block-paragraph">Historically, precise instantiation of a table worked like this:</p>



<ul class="wp-block-list">
<li><code>START EXTRACT</code>. It is the first step, to ensure that no transaction is lost.</li>



<li>Wait for long running transactions to be committed.</li>



<li>Select an SCN, which should not be prior to the registration SCN of the extract.</li>



<li>Export the data with <code>expdp ... FLASHBACK_SCN=&lt;scn&gt;</code> so the dump is consistent.</li>



<li>Import the data with <code>impdp</code> on the target.</li>



<li>Start the replicat with the <code>AFTERCSN &lt;csn&gt;</code> clause, so the replicat does not replicate anything committed at or before the instantiation CSN.</li>
</ul>



<p class="wp-block-paragraph">While still perfectly valid, and even necessary under certain conditions, this method becomes hard to operate on complex replications or when the size of the database increases:</p>



<ul class="wp-block-list">
<li><code>UNDO</code> retention: very often, using the <code>FLASHBACK_SCN</code> clause will generate <code>ORA-01555: snapshot too old</code> errors.</li>



<li>Splitting the initial load is not possible, since the <code>AFTERCSN</code> clause will apply on the whole replicat.</li>
</ul>



<h2 id="instantiation-csn-in-practice" class="wp-block-heading">Instantiation CSN in practice</h2>



<p class="wp-block-paragraph">GoldenGate instantiation CSN provides a simple solution: a <strong>per table instantiation CSN, on the target database</strong>, instead of a single one applied on the replicat.</p>



<p class="wp-block-paragraph">This instantiation CSN is stored on the target database in the <code>DBA_APPLY_INSTANTIATED_OBJECTS</code> table, with one row per source object.</p>



<pre class="wp-block-code"><code>SET PAGES 100 LINES 200
COL source_object_owner FORMAT A20
COL source_object_name FORMAT A30
COL instantiation_scn FORMAT 999999999999999
SELECT source_object_owner, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
ORDER BY 1, 2;</code></pre>



<p class="wp-block-paragraph">This table is <strong>populated by Data Pump</strong>, and is then <strong>read by GoldenGate</strong>. When applying changes, a replicat will compare the commit CSN of a record to the table’s instantiation CSN. If the commit CSN is higher, the transaction will be discarded.</p>



<p class="wp-block-paragraph">Because the comparison is done for every table, there is no need for them to share one instantiation CSN using <code>FLASHBACK_SCN</code>.</p>



<h2 id="why-flashback_scn-is-no-longer-required" class="wp-block-heading">Why <code>FLASHBACK_SCN</code> is no longer required</h2>



<p class="wp-block-paragraph">The instantiation CSN is the <strong>read-consistent SCN</strong> as of which Data Pump reads the table during export. It is stored in the dump and imported on the target when using <code>impdp</code>. It does <strong>not</strong> come from when the source table is prepared with <code>PREPARECSN</code>. <code>PREPARECSN</code> is what makes the table <em>eligible</em> for instantiation CSN.</p>



<p class="wp-block-paragraph">Let’s see the difference with three different scenarios:</p>



<ul class="wp-block-list">
<li>Standard initial load without <code>FLASHBACK_SCN</code></li>



<li>Historic method with <code>FLASHBACK_SCN</code></li>



<li>Instantiation with <code>PREPARECSN NONE</code></li>
</ul>



<p class="wp-block-paragraph">I will use the same two tables, <code>HR.EMPLOYEES</code> and <code>HR.DEPARTMENTS</code>.</p>



<h2 id="enable_instantiation_filtering-vs-atcsnaftercsn" class="wp-block-heading"><code>ENABLE_INSTANTIATION_FILTERING</code> vs <code>ATCSN</code>/<code>AFTERCSN</code></h2>



<p class="wp-block-paragraph">Before running the three scenarios, here is what the instantiation filtering replaces:</p>



<figure class="wp-block-table"><table><thead><tr><th></th><th><code>ATCSN</code>/<code>AFTERCSN</code></th><th><code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code></th></tr></thead><tbody><tr><td>Boundary granularity</td><td>One CSN for the whole replicat</td><td>One CSN per table</td></tr><tr><td>Where the boundary is set</td><td>On the <code>START REPLICAT</code> command</td><td>Read from <code>dba_apply_instantiated_objects</code> on the target</td></tr><tr><td>Who sets the boundary</td><td>The operator, by hand</td><td>Data Pump automatically (or <code>SET INSTANTIATION CSN</code> manually)</td></tr><tr><td>Fits a load split across several <code>expdp</code>/<code>impdp</code> jobs at different SCNs</td><td>No (every mapped table must share the one CSN)</td><td>Yes</td></tr><tr><td>Extra source-side step</td><td>None</td><td><code>ADD TRANDATA</code>/<code>ADD SCHEMATRANDATA ... PREPARECSN</code> before export</td></tr></tbody></table></figure>



<h3 id="1-prepare-the-tables-on-the-source" class="wp-block-heading">1. Prepare the tables on the source</h3>



<p class="wp-block-paragraph">Using <a href="https://docs.oracle.com/en/database/goldengate/core/26/gclir/add-schematrandata.html" target="_blank" rel="noopener noreferrer"><code>ADD SCHEMATRANDATA</code></a>, we will start by preparing the table on the source. The full clause is <code>ADD SCHEMATRANDATA &lt;schema_name&gt; PREPARECSN {WAIT | LOCK | NOWAIT | NONE}</code>, and <code>NOWAIT</code> is the <strong>default</strong>. In other words, <code>ADD SCHEMATRANDATA</code> prepares tables for instantiation whether you include <code>PREPARECSN</code> or not.</p>



<pre class="wp-block-code"><code>OGG&gt; DBLOGIN USERIDALIAS gg_source DOMAIN OracleGoldenGate
OGG&gt; ADD SCHEMATRANDATA HR
2026-07-26 09:41:07  INFO    OGG-01788  SCHEMATRANDATA has been added on schema HR.
2026-07-26 09:41:07  INFO    OGG-10154  Schema level PREPARECSN set to mode NOWAIT on schema HR.</code></pre>



<p class="wp-block-paragraph">If you do not want instantiation CSN filtering for a schema, you can opt out explicitly with <code>ADD SCHEMATRANDATA HR PREPARECSN NONE</code>. It is the only way to get a table with zero rows in <code>dba_apply_instantiated_objects</code> after a load. Everything else on this page happens by default.</p>



<p class="wp-block-paragraph">The full clause, <code>PREPARECSN {WAIT | LOCK | NOWAIT | NONE}</code>, controls how invasive the preparation is on a live table:</p>



<figure class="wp-block-table"><table><thead><tr><th>Mode</th><th>Behavior</th></tr></thead><tbody><tr><td><code>NOWAIT</code></td><td>Prepares immediately without waiting on in-flight transactions; the default.</td></tr><tr><td><code>WAIT</code></td><td>Waits for currently open transactions on the table to complete before marking it prepared.</td></tr><tr><td><code>LOCK</code></td><td>Takes a lock on the table to guarantee a clean preparation point; the most disruptive, reserved for tables where <code>WAIT</code> cannot be tolerated.</td></tr><tr><td><code>NONE</code></td><td>Disables CSN instantiation.</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">For an OLTP table with a lot of concurrent DML, <code>WAIT</code> or <code>LOCK</code> can stall application sessions while GoldenGate waits for or takes the lock. <code>NOWAIT</code> is almost always the correct option.</p>



<p class="wp-block-paragraph">Confirm both tables are prepared:</p>



<pre class="wp-block-code"><code>OGG&gt; INFO SCHEMATRANDATA HR
Schema HR has 2 prepared tables for instantiation.</code></pre>



<h3 id="2-start-the-extract" class="wp-block-heading">2. Start the extract</h3>



<p class="wp-block-paragraph">Capture has to be running before the export, so that any change committed during and after the dump is safely in the trail:</p>



<pre class="wp-block-code"><code>OGG&gt; START EXTRACT EXTA</code></pre>



<h3 id="3-wait-for-long-running-transactions-to-finish" class="wp-block-heading">3. Wait for long running transactions to finish</h3>



<p class="wp-block-paragraph">A transaction that began before the extract could be missed, so wait for every long running transaction to finish before exporting. See <a href="https://www.dbi-services.com/blog/checking-long-running-transactions-in-goldengate/" target="_blank" rel="noopener noreferrer">Checking Long Running Transactions in GoldenGate</a> for how to list them with the <code>adminclient</code> and the REST API.</p>



<h3 id="4-export-without-flashback_scn" class="wp-block-heading">4. Export without <code>FLASHBACK_SCN</code></h3>



<pre class="wp-block-code"><code>expdp hr/*** dumpfile=hr.dmp schemas=HR</code></pre>



<p class="wp-block-paragraph">Without the <code>FLASHBACK_SCN</code> clause, each table’s boundary will be the SCN as of which Data Pump reads it during the export, recorded automatically because the table was prepared with <code>PREPARECSN</code>.</p>



<h3 id="5-import-on-the-target" class="wp-block-heading">5. Import on the target</h3>



<pre class="wp-block-code"><code>impdp gg_target/*** dumpfile=hr.dmp full=y</code></pre>



<p class="wp-block-paragraph">As part of this import, Data Pump populates <code>dba_apply_instantiated_objects</code> for every prepared table it loads. No extra option is needed.</p>



<h3 id="6-retrieve-the-instantiation-csn-for-each-table" class="wp-block-heading">6. Retrieve the instantiation CSN for each table</h3>



<p class="wp-block-paragraph">Let’s query the table that replaces the SCN you used to write down by hand:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_object_owner, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'HR'
ORDER BY 1, 2;

SOURCE_OBJECT_OWNER SOURCE_OBJECT_NAME INSTANTIATION_SCN
------------------- ------------------ -----------------
HR                  DEPARTMENTS                 17662585
HR                  EMPLOYEES                   17662582</code></pre>



<p class="wp-block-paragraph">The two tables come from the same export, but with <strong>two different <code>INSTANTIATION_SCN</code></strong>. Without <code>FLASHBACK_SCN</code> on the export, each table was read (and stamped) as the dump progressed. That per-table granularity is the entire point, and it is exactly what a single <code>AFTERCSN</code> could never express.</p>



<h3 id="7-configure-the-replicat-and-start-it-without-aftercsn" class="wp-block-heading">7. Configure the replicat and start it, without <code>AFTERCSN</code></h3>



<pre class="wp-block-code"><code>REPLICAT REPA
DBOPTIONS ENABLE_INSTANTIATION_FILTERING
USERIDALIAS gg_target DOMAIN OracleGoldenGate
MAP HR.*, TARGET HR.*;</code></pre>



<pre class="wp-block-code"><code>OGG&gt; START REPLICAT REPA</code></pre>



<p class="wp-block-paragraph"><code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code> tells the replicat to look each table’s boundary in <code>dba_apply_instantiated_objects</code> instead of expecting an <code>AFTERCSN</code> on the start command. The replicat discards trail records committed at or before its boundary and applies everything after it. Both tables are in sync with the source, with no flashback SCN ever chosen.</p>



<h2 id="same-initial-load-with-flashback_scn" class="wp-block-heading">Same initial load with <code>FLASHBACK_SCN</code></h2>



<p class="wp-block-paragraph">Let’s run the same initial load again for a second schema, but add <code>FLASHBACK_SCN</code> to the export and watch what changes in the instantiation table. Pick an SCN and pass it to <code>expdp</code>:</p>



<pre class="wp-block-code"><code>expdp system/*** schemas=HR2 flashback_scn=17663448 dumpfile=hr2.dmp
impdp system/*** dumpfile=hr2.dmp full=y</code></pre>



<p class="wp-block-paragraph">Then the same step 6 query against the target:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_object_owner, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'HR2'
ORDER BY 1, 2;

SOURCE_OBJECT_OWNER SOURCE_OBJECT_NAME INSTANTIATION_SCN
------------------- ------------------ -----------------
HR2                 DEPARTMENTS                 17663448
HR2                 EMPLOYEES                   17663448</code></pre>



<p class="wp-block-paragraph">This time, both tables carry the <strong>same instantiation CSN</strong>, and it is <strong>exactly the <code>FLASHBACK_SCN</code></strong> (<code>17663448</code>). <code>FLASHBACK_SCN</code> pins the whole export to one consistent point in time, so Data Pump stamps every table in the dump with that single SCN. Please note that <code>FLASHBACK_SCN</code> is still not <em>required</em> for the instantiation mechanism to work. It only gives a consistent export.</p>



<h2 id="same-initial-load-with-preparecsn-none" class="wp-block-heading">Same initial load with <code>PREPARECSN NONE</code></h2>



<p class="wp-block-paragraph">Let’s see what happens when we disable instantiation, by preparing a third schema with <code>PREPARECSN NONE</code> and run the same export and import:</p>



<pre class="wp-block-code"><code>OGG&gt; ADD SCHEMATRANDATA HR3 ALLCOLS, PREPARECSN NONE
INFO OGG-10154  Schema level PREPARECSN set to mode NONE on schema "HR3"
OGG&gt; INFO SCHEMATRANDATA HR3
Schema "HR3" has 0 prepared tables for instantiation</code></pre>



<pre class="wp-block-code"><code>expdp system/*** schemas=HR3 dumpfile=hr3.dmp
impdp system/*** dumpfile=hr3.dmp full=y</code></pre>



<p class="wp-block-paragraph">The import succeeds and the rows land on the target as normal, but the instantiation table stays empty for this schema:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_object_owner, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner = 'HR3';

no rows selected</code></pre>



<p class="wp-block-paragraph">In that case, a replicat running with <code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code> would find no boundary for these tables and therefore would not filter them. It would apply every trail record it sees, including changes the dump already contains.</p>



<h3 id="the-boundary-outlives-the-schema" class="wp-block-heading">The boundary outlives the schema</h3>



<p class="wp-block-paragraph"><code>dba_apply_instantiated_objects</code> is independent from the schema it describes. Dropping the target schema with <code>DROP USER ... CASCADE</code> and recreating it does <strong>not</strong> clear its row:</p>



<pre class="wp-block-code"><code>SQL&gt; DROP USER hr4 CASCADE;

User dropped.

SQL&gt; CREATE USER hr4 IDENTIFIED BY Welcome1;

User created.

SQL&gt; SELECT source_object_owner, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner='HR4';

SOURCE_OBJECT_OWNER SOURCE_OBJECT_NAME INSTANTIATION_SCN
------------------- ------------------ -----------------
HR4                 EMPLOYEES                   52356545</code></pre>



<p class="wp-block-paragraph">A stale boundary from a previous initial load can silently attach itself to the next import of an object with the same name. The same table, re-imported a second time from a dump whose source was never prepared with <code>PREPARECSN</code>, does not delete the row from the first prepared initial load.</p>



<p class="wp-block-paragraph">A replicat with <code>ENABLE_INSTANTIATION_FILTERING</code> reading this table afterward would filter every record against a boundary that has no relationship to what the last import actually put on the target.</p>



<p class="wp-block-paragraph">A command exists in the <code>adminclient</code> to clear the instantiation table:</p>



<pre class="wp-block-code"><code>OGG&gt; DBLOGIN USERIDALIAS gg_target DOMAIN OracleGoldenGate
OGG&gt; CLEAR INSTANTIATION CSN FOR hr.employees FROM PDB_SOURCE
INFO OGG-10464  Instantiation CSN has been cleared successfully.</code></pre>



<pre class="wp-block-code"><code>SQL&gt; SELECT source_object_owner, source_object_name, instantiation_scn
FROM dba_apply_instantiated_objects
WHERE source_object_owner='HR';

no rows selected</code></pre>



<p class="wp-block-paragraph">Remember to check the <code>dba_apply_instantiated_objects</code> view before reusing a target schema name for an initial load.</p>



<h2 id="when-should-i-still-set-instantiation-scn-by-hand-" class="wp-block-heading">When should I still set instantiation SCN by hand ?</h2>



<p class="wp-block-paragraph">There are multiple scenarios where automatic instantiation filtering does not work.</p>



<ul class="wp-block-list">
<li>If your initial load is not done by Data Pump. GoldenGate initial extract, a manual <code>INSERT ... SELECT</code> over a database link, transportable tablespaces, do not alter the <code>dba_apply_instantiated_objects</code> view.</li>



<li>If the workload during the initial load is not just DML. Instantiation filtering does not work on DDL, for instance.</li>
</ul>



<p class="wp-block-paragraph">In that case, you can either go back to the <code>FLASHBACK_SCN</code> method, or set the instantiation CSN by hand in the <code>adminclient</code>:</p>



<pre class="wp-block-code"><code>OGG&gt; DBLOGIN USERIDALIAS gg_target DOMAIN OracleGoldenGate
OGG&gt; SET INSTANTIATION CSN FOR hr.legacy_orders CSN 12345678 FROM PDB_SOURCE</code></pre>



<h2 id="common-questions" class="wp-block-heading">Common questions</h2>



<h3 id="a-table-added-after-preparecsn-never-gets-a-boundary" class="wp-block-heading">A table added after <code>PREPARECSN</code> never gets a boundary</h3>



<p class="wp-block-paragraph">If a table is created on the source after you ran <code>ADD SCHEMATRANDATA HR</code>, it is not part of that preparation. Exporting it later with <code>expdp</code> means Data Pump has no CSN bookkeeping to carry into the dump, and <code>dba_apply_instantiated_objects</code> is not populated during import. The fix is to run <code>ADD TRANDATA ...</code> before exporting it.</p>



<h3 id="removing-the-parameter-once-instantiation-is-complete" class="wp-block-heading">Removing the parameter once instantiation is complete</h3>



<p class="wp-block-paragraph"><code>DBOPTIONS ENABLE_INSTANTIATION_FILTERING</code> can be removed from the replicat parameter file once it has processed every transaction past the instantiation CSN of every table it maps (there is no harm in leaving it in indefinitely).</p>



<h2 id="summary" class="wp-block-heading">Summary</h2>



<ul class="wp-block-list">
<li>The instantiation CSN is a per-table replication boundary, stored in <code>dba_apply_instantiated_objects</code> on the target, compared against each record’s commit CSN.</li>



<li>Data Pump stamps it automatically on <code>impdp</code>, because <code>ADD SCHEMATRANDATA</code>/<code>ADD TRANDATA</code> prepare tables for instantiation by default (<code>PREPARECSN NOWAIT</code>). Use <code>PREPARECSN NONE</code> to not use instantiation SCN.</li>



<li>Because that boundary is per table and set by Data Pump, <code>FLASHBACK_SCN</code> and <code>AFTERCSN</code> are no longer needed just to define where the dump ends and replication begins. Retrieve the boundary with a query instead of tracking an SCN by hand.</li>



<li><code>FLASHBACK_SCN</code> still has a use (making one large export internally consistent), but it is no longer needed just to drive the replication boundary. Data Pump records that boundary either way.</li>



<li>The boundary outlives the schema it describes. Dropping and recreating a target schema does not clear its row in <code>dba_apply_instantiated_objects</code>. Check the view, and use <code>CLEAR INSTANTIATION CSN</code> if you see stale rows.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/goldengate-instantiation-csn-explained/">GoldenGate instantiation CSN explained</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/goldengate-instantiation-csn-explained/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>DDL filtering in GoldenGate extract and replicat</title>
		<link>https://www.dbi-services.com/blog/ddl-filtering-in-goldengate-extract-and-replicat/</link>
					<comments>https://www.dbi-services.com/blog/ddl-filtering-in-goldengate-extract-and-replicat/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Mon, 21 Sep 2026 07:11:00 +0000</pubDate>
				<category><![CDATA[GoldenGate]]></category>
		<category><![CDATA[Oracle]]></category>
		<category><![CDATA[26]]></category>
		<category><![CDATA[26ai]]></category>
		<category><![CDATA[ddl]]></category>
		<category><![CDATA[extract]]></category>
		<category><![CDATA[filtering]]></category>
		<category><![CDATA[ogg]]></category>
		<category><![CDATA[replicat]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=46803</guid>

					<description><![CDATA[<p>When replicating data with GoldenGate, you often want DDL changes (table alterations, new tables, dropped indexes, etc.) to follow alongside DML. But replicating all DDL is sometimes dangerous (TRUNCATE, DROP TABLE) or simply impossible (architecture differences between source and target). DDL filtering gives you precise control over this. This blog covers multiple aspects, including latest [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/ddl-filtering-in-goldengate-extract-and-replicat/">DDL filtering in GoldenGate extract and replicat</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">When replicating data with GoldenGate, you often want DDL changes (table alterations, new tables, dropped indexes, etc.) to follow alongside DML. But replicating <em>all</em> DDL is sometimes dangerous (<code>TRUNCATE</code>, <code>DROP TABLE</code>) or simply impossible (architecture differences between source and target). DDL filtering gives you precise control over this.</p>



<p class="wp-block-paragraph">This blog covers multiple aspects, including latest changes introduced in GoldenGate 26ai:</p>



<ul class="wp-block-list">
<li><a href="#extract-vs-replicat-ddl-filtering">Difference between extract and replicat filtering</a></li>



<li><a href="#scopes-mapped-unmapped-other-all">DDL filtering model and syntax</a></li>



<li><a href="#andor-logic">AND/OR logic</a></li>



<li><a href="#ddloptions-parameter"><code>DDLOPTIONS</code> parameter</a></li>



<li><a href="#tag-based-ddl-filtering-excludetag-and-includetag">Tag-based filtering (<code>EXCLUDETAG</code>/<code>INCLUDETAG</code>)</a></li>



<li><a href="#edge-cases">Edge cases</a></li>
</ul>



<h2 id="extract-vs-replicat-ddl-filtering" class="wp-block-heading">Extract vs Replicat DDL filtering</h2>



<p class="wp-block-paragraph">Before diving into the syntax, you should understand that DDL filtering means something <strong>different for an extract and a replicat</strong>.</p>



<p class="wp-block-paragraph"><strong>For an extract</strong>, filtering controls what gets <strong>written to the trail file</strong>. If a DDL statement is excluded at the extract level, it is gone forever, and will never reach any downstream replicat. This makes extract-level filtering absolute and decisive. On the other side, it keeps trail files smaller and ensures no downstream replicat ever sees unwanted DDL.</p>



<p class="wp-block-paragraph"><strong>For a replicat</strong>, filtering controls what gets <strong>applied to the target database</strong>. In that case, the DDL statement is considered to be already in the trail file. The replicat simply chooses to apply it or skip it. This gives more flexibility. For instance, multiple replicats can read the same trail and each apply a different subset of DDL. Of course, the <strong>trail files are larger</strong>, and generate more I/O.</p>



<p class="wp-block-paragraph">This does not mean that you should choose one or another. A common use case is to do <strong>generic filtering at the extract level</strong> (removing clearly irrelevant DDL like <code>CREATE USER</code> or <code>GRANT</code>), and more <strong>targeted filtering for each replicat</strong> (for example, suppressing <code>TRUNCATE</code> on a target holding additional rows).</p>



<h2 id="enabling-ddl-replication" class="wp-block-heading">Enabling DDL replication</h2>



<p class="wp-block-paragraph">DDL replication is <strong>disabled by default</strong>. You must explicitly enable it in both extract and replicat parameter files.</p>



<p class="wp-block-paragraph">The simplest form is:</p>



<pre class="wp-block-code"><code>DDL</code></pre>



<p class="wp-block-paragraph">This enables DDL replication with no filtering. All DDL is captured (extract) or applied (replicat). In practice this is almost never what you want. You will almost always pair it with <code>INCLUDE</code> or <code>EXCLUDE</code> clauses.</p>



<h2 id="scopes-mapped-unmapped-other-all" class="wp-block-heading">Scopes: <code>MAPPED</code>, <code>UNMAPPED</code>, <code>OTHER</code>, <code>ALL</code></h2>



<p class="wp-block-paragraph">Every DDL statement GoldenGate encounters is classified into one of four scopes based on whether the object it operates on matches your <code>TABLE</code> (extract) or <code>MAP</code> (replicat) parameter list.</p>



<figure class="wp-block-table"><table><thead><tr><th>Scope</th><th>Meaning</th></tr></thead><tbody><tr><td><code>MAPPED</code></td><td>The DDL object matches a <code>TABLE</code>/<code>MAP</code> entry</td></tr><tr><td><code>UNMAPPED</code></td><td>The DDL object does NOT match any <code>TABLE</code>/<code>MAP</code> entry</td></tr><tr><td><code>OTHER</code></td><td>DDL not associated with a specific object at all (e.g. <code>CREATE USER</code>, <code>CREATE TABLESPACE</code>)</td></tr><tr><td><code>ALL</code></td><td><code>MAPPED</code>, <code>UNMAPPED</code>, and <code>OTHER</code></td></tr></tbody></table></figure>



<p class="wp-block-paragraph">If you do not specify a scope, <strong>GoldenGate defaults to <code>ALL</code></strong>.</p>



<h3 id="example" class="wp-block-heading">Example</h3>



<p class="wp-block-paragraph">With this extract parameter file:</p>



<pre class="wp-block-code"><code>EXTRACT EXT
USERIDALIAS source_cdb DOMAIN OracleGoldenGate
EXTTRAIL pdb1/aa
SOURCECATALOG PDB1
DDL INCLUDE ALL
TABLE APP_SCHEMA.*;</code></pre>



<ul class="wp-block-list">
<li><code>CREATE TABLE APP_SCHEMA.ORDERS (...)</code> is <strong><code>MAPPED</code></strong> (matches <code>APP_SCHEMA.*</code>)</li>



<li><code>CREATE TABLE AUDIT_SCHEMA.LOGS (...)</code> is <strong><code>UNMAPPED</code></strong> (does not match <code>APP_SCHEMA.*</code>)</li>



<li><code>GRANT SELECT ON APP_SCHEMA.ORDERS TO READONLY</code> is <span class="proof-marker"><strong><code>MAPPED</code></strong> (a <code>GRANT</code> on a specific object follows that object’s scope, not <code>OTHER</code>. <code>OTHER</code> is only for DDL with no target object at all, like <code>CREATE USER</code>)</span></li>
</ul>



<p class="wp-block-paragraph">Switching to <code>DDL INCLUDE MAPPED</code> would capture the first and third statements.</p>



<p class="wp-block-paragraph">The DDL statement’s scope depends on the operation: see “Interaction with <code>TABLE</code> and <code>MAP</code> Parameters” below for more details.</p>



<h2 id="filter-keywords" class="wp-block-heading">Filter keywords</h2>



<p class="wp-block-paragraph">On top of scope filtering, you can also filter DDL by <strong>object type, operation type, object name, and DDL text content</strong>. These filters are specified as comma-separated conditions within a single <code>DDL</code> clause. All conditions in a single clause are added together (logical <code>AND</code>).</p>



<h3 id="objtype" class="wp-block-heading"><code>OBJTYPE</code></h3>



<p class="wp-block-paragraph">Filters by the type of database object the DDL operates on.</p>



<p class="wp-block-paragraph">Common Oracle object types: <code>TABLE</code>, <code>INDEX</code>, <code>SEQUENCE</code>, <code>VIEW</code>, <code>PROCEDURE</code>, <code>FUNCTION</code>, <code>TRIGGER</code>, <code>PACKAGE</code>, <code>TYPE</code>, <code>SYNONYM</code>, <code>DATABASE LINK</code>, <code>TABLESPACE</code>, <code>USER</code>.</p>



<pre class="wp-block-code"><code>-- Only capture DDL on tables
DDL INCLUDE MAPPED, OBJTYPE 'TABLE'

-- Only capture DDL on indexes
DDL INCLUDE MAPPED, OBJTYPE 'INDEX'</code></pre>



<h3 id="optype" class="wp-block-heading"><code>OPTYPE</code></h3>



<p class="wp-block-paragraph">Filters by the type of DDL operation.</p>



<p class="wp-block-paragraph">Common operation types: <code>CREATE</code>, <code>ALTER</code>, <code>DROP</code>, <code>RENAME</code>, <code>TRUNCATE</code>, <code>GRANT</code>, <code>REVOKE</code>, <code>COMMENT</code>.</p>



<pre class="wp-block-code"><code>-- Only capture CREATE TABLE statements
DDL INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE CREATE

-- Exclude DROP operations on all object types
DDL EXCLUDE MAPPED, OPTYPE DROP</code></pre>



<p class="wp-block-paragraph"><span class="proof-marker"><code>OPTYPE RENAME</code> exists and works, but classification depends on the exact SQL used. A <code>RENAME old TO new;</code> statement matches <code>OPTYPE RENAME</code> (GoldenGate logs <code>OGG-10457 DDL RENAME found ... RENAME converted to ALTER TABLE</code> while writing it, then classifies the resulting <code>OGG-10451</code>/<code>OGG-10452</code> line as <code>optype [RENAME]</code>). But <code>ALTER TABLE ... RENAME TO ...</code> (the form most people write, since <code>RENAME</code> cannot be schema-qualified) matches <code>OPTYPE ALTER</code> instead, with no <code>RENAME</code> classification and no conversion message at all.</span></p>



<h3 id="objname" class="wp-block-heading"><code>OBJNAME</code></h3>



<p class="wp-block-paragraph">Filters by object name. Supports wildcards (<code>*</code> for any sequence of characters, <code>?</code> for a single character). For Oracle, use the <code>schema.object</code> format, or <code>container.schema.object</code> in multitenant. <span class="proof-marker">Write the value with no quotes at all. <code>OBJNAME 'APP_SCHEMA.*'</code> (single-quoted) abends with <code>OGG-06377 Single quote marks cannot be used to delimit OBJNAME</code>, and <code>OBJNAME "APP_SCHEMA.*"</code> (double-quoted) abends differently, with <code>OGG-06585 No schema is specified</code>. A double-quoted value is taken as one literal token and never splits on its dot into schema and object.</span></p>



<pre class="wp-block-code"><code>-- Only capture DDL for tables starting with ORD_ in APP_SCHEMA
DDL INCLUDE MAPPED, OBJTYPE 'TABLE', OBJNAME APP_SCHEMA.ORD_*

-- Exclude DDL on any staging table
DDL EXCLUDE MAPPED, OBJNAME APP_SCHEMA.*_STG</code></pre>



<h3 id="instr" class="wp-block-heading"><code>INSTR</code></h3>



<p class="wp-block-paragraph">Filters based on whether a literal string appears anywhere in the DDL text (case-sensitive). This is especially useful for <strong>excluding</strong> certain kinds of DDL.</p>



<pre class="wp-block-code"><code>-- Exclude any DDL containing NOLOGGING
DDL EXCLUDE MAPPED, INSTR 'NOLOGGING'

-- Exclude DDL referencing a specific tablespace by name
DDL EXCLUDE MAPPED, INSTR 'ARCHIVE_TBS'</code></pre>



<h3 id="instrwords" class="wp-block-heading"><code>INSTRWORDS</code></h3>



<p class="wp-block-paragraph">Similar to <code>INSTR</code>, but performs word-level matching. The order of the words does not matter, and partial matches within longer words are not counted.</p>



<pre class="wp-block-code"><code>-- Exclude DDL that contains both the words COMPRESS and OLTP (in any order)
DDL EXCLUDE MAPPED, INSTRWORDS 'COMPRESS OLTP'</code></pre>



<h3 id="eventactions" class="wp-block-heading"><code>EVENTACTIONS</code></h3>



<p class="wp-block-paragraph">Unlike the keywords above, <code>EVENTACTIONS</code> is not specific to <code>DDL</code>: it is a general GoldenGate keyword also usable on <code>TABLE</code> and <code>MAP</code> clauses, for triggering an action (<code>REPORT</code>, <code>LOG</code>, and others) whenever a record matches that clause. It happens to be usable inside a <code>DDL</code> clause too, comma-separated alongside <code>OBJTYPE</code>/<code>OPTYPE</code>/<code>OBJNAME</code>/<code>INSTR</code> like any other filter keyword, and it comes with real, DDL-specific behavior worth knowing.</p>



<p class="wp-block-paragraph"><code>EVENTACTIONS (REPORT)</code> forces the same <code>DDL replication statistics</code> block that <code>DDLOPTIONS REPORT</code> writes on demand (see the <code>DDLOPTIONS</code> section below). However, it <strong>only works on an <code>INCLUDE</code> clause</strong>:</p>



<pre class="wp-block-code"><code>DDL INCLUDE MAPPED, EVENTACTIONS (REPORT)</code></pre>



<pre class="wp-block-code"><code>2026-09-05 11:52:47  INFO    OGG-01487  DDL found, operation &#091;create table ddltest.evt1 (id number) (size 37)], start SCN &#091;24368602], commit SCN &#091;24368634] instance &#091; (1)], DDL seqno &#091;0], marker seqno &#091;0].

2026-09-05 11:52:47  INFO    OGG-10451  DDL operation included &#091;INCLUDE MAPPED, EVENTACTIONS (REPORT)], optype &#091;CREATE], objtype &#091;TABLE], catalog "PDB1", objowner "DDLTEST", objname "EVT1".

***********************************************************************
*                   ** Run Time Statistics **                         *
***********************************************************************


Report at 2026-09-05 11:52:47.388472 (activity since 2026-09-05 11:50:36.261708)

Output to dt:



DDL replication statistics:

                    Operations:         0
             Mapped operations:         0
           Unmapped operations:         0
              Other operations:         0
           Excluded operations:         0


No records extracted.</code></pre>



<p class="wp-block-paragraph"><strong>This is verbose.</strong> A second <code>CREATE TABLE</code> matching the same clause produced a second, identical-shaped block right after the <code>OGG-10451</code> line. This means that every matching DDL gets a full statistics dump on top of the usual <code>OGG-01487</code>/<code>OGG-10451</code>/<code>OGG-10452</code> lines. On a replication with any real amount of DDL traffic, this multiplies report file growth well past what <code>DDLOPTIONS REPORT</code> alone produces.  Try to restrict <code>EVENTACTIONS (REPORT)</code> to a narrow <code>INCLUDE</code> clause: a specific <code>OBJTYPE</code>, <code>OPTYPE</code>, or <code>OBJNAME</code> you want to log in the report file.</p>



<p class="wp-block-paragraph">Please note that the behavior is different for <code>EXCLUDE</code> clauses. The same <code>EVENTACTIONS (REPORT)</code> written on an <code>EXCLUDE</code> clause instead (see below example) excludes a <code>TRUNCATE</code> normally (<code>OGG-10452 DDL operation excluded [EXCLUDE MAPPED, OPTYPE TRUNCATE, EVENTACTIONS (REPORT)]</code>), but no <code>DDL replication statistics</code> block follows it.</p>



<pre class="wp-block-code"><code>DDL INCLUDE MAPPED &amp;
    EXCLUDE MAPPED, OPTYPE TRUNCATE, EVENTACTIONS (REPORT)</code></pre>



<h2 id="andor-logic" class="wp-block-heading"><code>AND</code>/<code>OR</code> Logic</h2>



<p class="wp-block-paragraph"><span class="proof-marker"><strong>Only one <code>DDL</code> parameter is allowed per parameter file.</strong> A parameter file with two separate <code>DDL</code> lines abends at startup with <code>OGG-00403 There can be only one DDL filtering statement</code>: “Only one DDL parameter can be used in a parameter file, but you can combine multiple inclusion and exclusion options to filter the DDL to the required level.” Everything below happens <strong>inside that single <code>DDL</code> parameter</strong>: each clause after the first drops the leading <code>DDL</code> keyword and starts directly with <code>INCLUDE</code> or <code>EXCLUDE</code>, and the whole thing is continued with <code>&amp;</code>.</span></p>



<h3 id="and-conditions-within-a-single-ddl-clause" class="wp-block-heading"><code>AND</code>: conditions within a single <code>DDL</code> clause</h3>



<p class="wp-block-paragraph">A DDL statement must satisfy <strong>all</strong> conditions to match.</p>



<pre class="wp-block-code"><code>-- Capture only CREATE TABLE in APP_SCHEMA (all three conditions must be true)
DDL INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE CREATE, OBJNAME APP_SCHEMA.*</code></pre>



<p class="wp-block-paragraph">A <code>DROP TABLE APP_SCHEMA.ORDERS</code> would not match this clause because <code>OPTYPE DROP</code> is different from <code>OPTYPE CREATE</code>.</p>



<h3 id="or-multiple-includeexclude-clauses" class="wp-block-heading"><code>OR</code>: multiple <code>INCLUDE</code>/<code>EXCLUDE</code> clauses</h3>



<p class="wp-block-paragraph">Only one <code>DDL</code> parameter is allowed per parameter file, but that parameter can carry multiple <code>INCLUDE</code>/<code>EXCLUDE</code> clauses, separated with <code>&amp;</code>. Multiple <code>INCLUDE</code> clauses are evaluated as a logical <code>OR</code>: a DDL statement is captured if it matches <strong>any</strong> <code>INCLUDE</code> clause. The indentation is optional.</p>



<pre class="wp-block-code"><code>-- Capture CREATE TABLE or CREATE SEQUENCE (either clause is sufficient)
DDL INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE CREATE &amp;
    INCLUDE MAPPED, OBJTYPE 'SEQUENCE', OPTYPE CREATE</code></pre>



<h3 id="exclude-always-wins-over-include" class="wp-block-heading"><code>EXCLUDE</code> always wins over <code>INCLUDE</code></h3>



<p class="wp-block-paragraph">If a DDL statement matches both an <code>INCLUDE</code> clause and an <code>EXCLUDE</code> clause, the <code>EXCLUDE</code> wins. This lets you set broader <code>INCLUDE</code> rules and choose specific exceptions. An <code>EXCLUDE</code> clause always needs a corresponding <code>INCLUDE</code> clause in the same <code>DDL</code> parameter, it cannot stand alone.</p>



<pre class="wp-block-code"><code>-- Include all MAPPED DDL, but exclude TRUNCATE
DDL INCLUDE MAPPED &amp;
    EXCLUDE MAPPED, OPTYPE TRUNCATE</code></pre>



<p class="wp-block-paragraph"><span class="proof-marker">A lone <code>EXCLUDE</code> clause with no corresponding <code>INCLUDE</code> still abends (<code>OGG-06374 Invalid DDL statement: No INCLUDE</code>). <code>EXCLUDE</code> narrows an <code>INCLUDE</code>; it cannot stand alone.</span> Every multi-clause example in this post uses this single-<code>DDL</code>-parameter, <code>&amp;</code>-continued form.</p>



<h2 id="filtering-examples" class="wp-block-heading">Filtering Examples</h2>



<p class="wp-block-paragraph">You can build nearly all sorts of DDL filtering in GoldenGate. But you should <strong>always ensure</strong> that they are <strong>relevant to the workload on the source and target</strong> databases !</p>



<h3 id="example-1-structural-ddl-only-no-destructive-operations" class="wp-block-heading">Example 1: Structural DDL only, no destructive operations</h3>



<p class="wp-block-paragraph">Goal: replicate <code>CREATE</code> and <code>ALTER</code> on tables and sequences, but never <code>DROP</code> or <code>TRUNCATE</code>.</p>



<pre class="wp-block-code"><code>DDL INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE CREATE &amp;
    INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE ALTER &amp;
    INCLUDE MAPPED, OBJTYPE 'SEQUENCE', OPTYPE CREATE &amp;
    INCLUDE MAPPED, OBJTYPE 'SEQUENCE', OPTYPE ALTER</code></pre>



<p class="wp-block-paragraph">Any <code>DROP TABLE</code>, <code>TRUNCATE TABLE</code>, or <code>DROP SEQUENCE</code> on a mapped object will be silently ignored.</p>



<h3 id="example-2-everything-except-nologging-directives" class="wp-block-heading">Example 2: Everything except <code>NOLOGGING</code> directives</h3>



<p class="wp-block-paragraph">Here, the goal is to replicate all <code>MAPPED</code> DDL, but exclude any DDL containing the <code>NOLOGGING</code> directive.</p>



<pre class="wp-block-code"><code>DDL INCLUDE MAPPED &amp;
    EXCLUDE MAPPED, INSTR 'NOLOGGING'</code></pre>



<h3 id="example-3-replicat-applies-only-a-subset-of-a-rich-trail" class="wp-block-heading">Example 3: Replicat applies only a subset of a rich trail</h3>



<p class="wp-block-paragraph">If a trail was generated with <code>DDL INCLUDE ALL</code> at the extract level, and you need the replicat to apply only <code>CREATE TABLE</code> and <code>ALTER TABLE</code> on mapped objects:</p>



<pre class="wp-block-code"><code>DDL INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE CREATE &amp;
    INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE ALTER
MAP APP_SCHEMA.*, TARGET TARGET_SCHEMA.*;</code></pre>



<p class="wp-block-paragraph">All other DDLs in the trail (<code>DROP</code>, <code>TRUNCATE</code>, index changes, sequences) are skipped by this replicat.</p>



<h3 id="example-4-mapped-objects-plus-a-specific-unmapped-schema" class="wp-block-heading">Example 4: Mapped objects plus a specific unmapped schema</h3>



<pre class="wp-block-code"><code>-- Capture DDL for APP_SCHEMA (mapped) and also for SHARED_SCHEMA (not in the TABLE filtering)
DDL INCLUDE MAPPED &amp;
    INCLUDE UNMAPPED, OBJNAME SHARED_SCHEMA.* &amp;
    EXCLUDE MAPPED, OPTYPE TRUNCATE &amp;
    EXCLUDE UNMAPPED, OPTYPE DROP
TABLE APP_SCHEMA.*;</code></pre>



<h2 id="ddloptions-parameter" class="wp-block-heading"><code>DDLOPTIONS</code> parameter</h2>



<p class="wp-block-paragraph"><code>DDLOPTIONS</code> often comes with <code>DDL</code> and controls what happens when DDL is processed. It is a global setting which applies to <strong>all</strong> DDL replication.</p>



<h3 id="report" class="wp-block-heading"><code>REPORT</code></h3>



<p class="wp-block-paragraph">Writes each DDL statement encountered (whether included or excluded) to the extract or replicat report file.</p>



<pre class="wp-block-code"><code>DDLOPTIONS REPORT</code></pre>



<p class="wp-block-paragraph">With <code>DDL INCLUDE ALL</code>, a <code>CREATE TABLE DDLTEST.PROMOTIONS</code> on the source produces entries like these in the report file:</p>



<pre class="wp-block-code"><code>2026-09-05 16:10:12  INFO    OGG-01487  DDL found, operation &#091;create table ddltest.promotions (id number, discount number) (size 60)], start SCN &#091;23026101], commit SCN &#091;23026117] instance &#091; (1)], DDL seqno &#091;0], marker seqno &#091;0].
2026-09-05 16:10:12  INFO    OGG-10451  DDL operation included &#091;INCLUDE ALL], optype &#091;CREATE], objtype &#091;TABLE], catalog "PDB1", objowner "DDLTEST", objname "PROMOTIONS".</code></pre>



<p class="wp-block-paragraph">This is very useful for <strong>testing, or when the DDL volume is low</strong>. However, <strong>never include <code>DDLOPTIONS REPORT</code></strong> if you have a lot of DDLs. Your report files will grow without control.</p>



<p class="wp-block-paragraph">A running process also keeps a running count of DDL statements by scope, separate from the per-statement <code>DDLOPTIONS REPORT</code> lines above. Request it on demand with <code>SEND EXTRACT &lt;name&gt;, REPORT</code> (or <code>SEND REPLICAT &lt;name&gt;, REPORT</code>) through the <code>adminclient</code>. It also gets written automatically whenever the process stops.</p>



<pre class="wp-block-code"><code>***********************************************************************
*                   ** Run Time Statistics **                         *
***********************************************************************

Report at 2026-09-05 11:34:23.315905 (activity since 2026-09-05 11:31:27.204191)

DDL replication statistics:

                    Operations:         1
             Mapped operations:         1
           Unmapped operations:         0
              Other operations:         0
           Excluded operations:         0</code></pre>



<p class="wp-block-paragraph">The report (<code>Mapped</code>/<code>Unmapped</code>/<code>Other</code>/<code>Excluded</code>) mirrors the scope model from the “Scopes” section above, so it is a quick way to check whether your filtering is doing what you expect without scrolling through every individual <code>OGG-01487</code>/<code>OGG-10451</code>/<code>OGG-10452</code> line.</p>



<h3 id="ddlerror-replicat-only" class="wp-block-heading"><code>DDLERROR</code> (replicat only)</h3>



<p class="wp-block-paragraph">Controls what happens when a DDL statement fails on the target. The default is to abend.</p>



<pre class="wp-block-code"><code>DDLERROR DEFAULT DISCARD</code></pre>



<p class="wp-block-paragraph"><code>DDLERROR</code> is a top-level parameter, not a <code>DDLOPTIONS</code> sub-keyword. As you can imagine, use <code>DISCARD</code> with caution. It could silently leave the target schema out of sync.</p>



<h2 id="tag-based-ddl-filtering-excludetag-and-includetag" class="wp-block-heading">Tag-based DDL filtering: <code>EXCLUDETAG</code> and <code>INCLUDETAG</code></h2>



<p class="wp-block-paragraph">Oracle GoldenGate 26ai adds a second, independent filtering layer on top of everything above. You can now filter by <strong>tag</strong> rather than by scope or object. Every DDL statement GoldenGate encounters carries a tag identifying its origin.</p>



<p class="wp-block-paragraph">By default, an extract applies <code>DDLOPTIONS EXCLUDETAG +</code>, which <strong>excludes all tagged DDL</strong> unless a tag is explicitly allowed with <code>INCLUDETAG</code>:</p>



<pre class="wp-block-code"><code>-- Exclude every tagged DDL statement by default (implicit default)
DDLOPTIONS EXCLUDETAG +

-- Explicitly allow DDL carrying tag 00 (Data Pump)
DDLOPTIONS INCLUDETAG 00</code></pre>



<p class="wp-block-paragraph">The practical impact is on tools that stamp a session tag on the DDL they issue. For instance, <code>impdp</code> tags its DDL with tag <code>00</code>. Under GoldenGate 26ai default behavior, that DDL is excluded from replication unless the extract explicitly adds <code>DDLOPTIONS INCLUDETAG 00</code>. Please note that this is a behavioral <strong>change from earlier versions</strong> of GoldenGate. Before, <code>impdp</code> DDL was captured without any extra configuration needed. If an import via <code>impdp</code> is expected to replicate its DDL, check for this setting before assuming the default behavior still applies.</p>



<p class="wp-block-paragraph"><code>EXCLUDETAG</code>/<code>INCLUDETAG</code> do not replace the scope and filter-keyword model. A DDL statement still has to pass the <code>DDL INCLUDE</code>/<code>DDL EXCLUDE</code> scope and keyword filtering described above. Both filtering systems have to allow the DDL.</p>



<h2 id="interaction-with-table-and-map-parameters" class="wp-block-heading">Interaction with <code>TABLE</code> and <code>MAP</code> parameters</h2>



<p class="wp-block-paragraph">Here are the scope (<code>MAPPED</code>/<code>UNMAPPED</code>/…) assignment rules for standard <code>TABLE</code> DDL.</p>



<figure class="wp-block-table"><table><thead><tr><th>DDL Operation</th><th>Scope evaluated on</th></tr></thead><tbody><tr><td><code>CREATE TABLE</code></td><td>The <em>new</em> object (checked against <code>TABLE</code>/<code>MAP</code> wildcard)</td></tr><tr><td><code>ALTER TABLE</code></td><td>The <em>existing</em> object</td></tr><tr><td><code>DROP TABLE</code></td><td>The <em>dropped</em> object (its name before the drop)</td></tr><tr><td><code>RENAME TABLE</code></td><td>The <em>original</em> name (before renaming)</td></tr><tr><td><code>TRUNCATE TABLE</code></td><td>The <em>existing</em> object</td></tr></tbody></table></figure>



<h2 id="edge-cases" class="wp-block-heading">Edge cases</h2>



<h3 id="ddl-errors-from-cascading-object-dependencies" class="wp-block-heading">DDL errors from cascading object dependencies</h3>



<p class="wp-block-paragraph">Some DDL operations have cascading effects that GoldenGate does not replicate automatically.</p>



<p class="wp-block-paragraph"><strong>Source action:</strong></p>



<pre class="wp-block-code"><code>DROP TABLE DDLTEST.CUSTOMERS CASCADE CONSTRAINTS;</code></pre>



<p class="wp-block-paragraph">GoldenGate captures and replicates the <code>DROP TABLE</code>. On the source, any views or constraints that depended on <code>DDLTEST.CUSTOMERS</code> are automatically dropped or invalidated. On the target, dependent objects created outside of the GoldenGate replication remain, as invalid objects.</p>



<p class="wp-block-paragraph">For instance, if you create a view named <code>DDLTEST_TGT.CUSTOMERS_V</code> directly on the target (outside of GoldenGate) on <code>DDLTEST_TGT.CUSTOMERS</code>, the <code>DROP TABLE ... CASCADE CONSTRAINTS</code> on the source replicates and applies cleanly:</p>



<pre class="wp-block-code"><code>2026-09-05 20:23:38  INFO    OGG-00482  DDL found, operation &#091;drop table ddltest.customers cascade constraints].
2026-09-05 20:23:38  INFO    OGG-00489  DDL is of mapped scope, after mapping new operation &#091;drop table "DDLTEST_TGT"."CUSTOMERS" cascade constraints].
2026-09-05 20:23:38  INFO    OGG-10451  DDL operation included &#091;INCLUDE MAPPED], optype &#091;DROP], objtype &#091;TABLE], catalog "PDB1", objowner "DDLTEST_TGT", objname "CUSTOMERS".
2026-09-05 20:23:38  INFO    OGG-00484  Executing DDL operation.
2026-09-05 20:23:38  INFO    OGG-00483  DDL operation successful.</code></pre>



<p class="wp-block-paragraph">But <code>DDLTEST_TGT.CUSTOMERS_V</code> is left behind, now referencing a table that no longer exists:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT object_name, object_type, status
FROM dba_objects
WHERE owner = 'DDLTEST_TGT'
AND object_name = 'CUSTOMERS_V';

OBJECT_NAME OBJECT_TYPE STATUS
----------- ----------- -------
CUSTOMERS_V VIEW        INVALID</code></pre>



<p class="wp-block-paragraph">There is no built-in GoldenGate mechanism for cascading DDL effects. You must either manage dependent objects manually, or filter the <code>DROP</code> and handle cleanup separately.</p>



<h2 id="complete-parameter-file-examples" class="wp-block-heading">Complete parameter file examples</h2>



<p class="wp-block-paragraph">Here is an example of an extract/replicat pair, and how you could build complex DDL handling.</p>



<pre class="wp-block-code"><code>EXTRACT EXT
USERIDALIAS source_cdb DOMAIN OracleGoldenGate
EXTTRAIL pdb1/aa
SOURCECATALOG PDB1

-- Capture structural DDL on mapped objects, exclude storage directives,
-- and also capture DDL on a shared utility schema not in the TABLE list
DDL INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE CREATE &amp;
    INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE ALTER &amp;
    INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE RENAME &amp;
    INCLUDE MAPPED, OBJTYPE 'SEQUENCE', OPTYPE CREATE &amp;
    INCLUDE MAPPED, OBJTYPE 'SEQUENCE', OPTYPE ALTER &amp;
    EXCLUDE MAPPED, INSTR 'NOLOGGING' &amp;
    EXCLUDE MAPPED, INSTR 'COMPRESS' &amp;
    INCLUDE UNMAPPED, OBJNAME SHARED_SCHEMA.*, OBJTYPE 'TABLE', OPTYPE CREATE &amp;
    INCLUDE UNMAPPED, OBJNAME SHARED_SCHEMA.*, OBJTYPE 'TABLE', OPTYPE ALTER

-- Write DDL activity to the report file for auditing
DDLOPTIONS REPORT

TABLE APP_SCHEMA.*;
TABLE SHARED_SCHEMA.*;</code></pre>



<pre class="wp-block-code"><code>REPLICAT REP
USERIDALIAS target_db DOMAIN OracleGoldenGate

-- Apply only CREATE and ALTER TABLE on mapped objects, plus shared-schema DDL;
-- skip sequences, drops, truncates, etc.
DDL INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE CREATE &amp;
    INCLUDE MAPPED, OBJTYPE 'TABLE', OPTYPE ALTER &amp;
    INCLUDE MAPPED, OBJNAME SHARED_SCHEMA.*

-- Write DDL activity to report file
DDLOPTIONS REPORT

-- Write discarded/failed DDL to discard file rather than abending
DDLERROR DEFAULT DISCARD

MAP APP_SCHEMA.*, TARGET TARGET_SCHEMA.*;
MAP SHARED_SCHEMA.*, TARGET SHARED_SCHEMA.*;</code></pre>



<h2 id="summary" class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">DDL filtering in GoldenGate is a layered system:</p>



<ul class="wp-block-list">
<li><strong>Scope</strong> (<code>MAPPED</code>/<code>UNMAPPED</code>/<code>OTHER</code>/<code>ALL</code>) restricts DDL replication, based on your <code>TABLE/MAP</code> configuration</li>



<li><strong>Filter keywords</strong> (<code>OBJTYPE</code>, <code>OPTYPE</code>, <code>OBJNAME</code>, <code>INSTR</code>, <code>INSTRWORDS</code>) restricts DDL replication within the scope</li>



<li><strong>Only one <code>DDL</code> parameter is allowed per parameter file</strong>, but it can carry multiple <code>INCLUDE</code>/<code>EXCLUDE</code> clauses continued with <code>&amp;</code>. <strong><code>AND</code> logic</strong> applies within a single clause, while <strong><code>OR</code> logic</strong> applies across clauses. <strong><code>EXCLUDE</code> always wins</strong> over <code>INCLUDE</code> and always needs a corresponding <code>INCLUDE</code></li>



<li><strong><code>DDLOPTIONS</code></strong> controls what happens when DDL is captured or replicated.</li>



<li><strong>Extract filtering is irreversible</strong>. You should filter at the replicat level when multiple replicats need different DDL settings.</li>
</ul>
<p>L’article <a href="https://www.dbi-services.com/blog/ddl-filtering-in-goldengate-extract-and-replicat/">DDL filtering in GoldenGate extract and replicat</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/ddl-filtering-in-goldengate-extract-and-replicat/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Listing and querying Oracle Enterprise Manager metric extensions</title>
		<link>https://www.dbi-services.com/blog/listing-and-querying-oracle-enterprise-manager-metric-extensions/</link>
					<comments>https://www.dbi-services.com/blog/listing-and-querying-oracle-enterprise-manager-metric-extensions/#respond</comments>
		
		<dc:creator><![CDATA[Julien Delattre]]></dc:creator>
		<pubDate>Fri, 18 Sep 2026 16:00:33 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[emcli]]></category>
		<category><![CDATA[enterprise-manager]]></category>
		<category><![CDATA[metric-extension]]></category>
		<category><![CDATA[OEM]]></category>
		<category><![CDATA[query]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=46799</guid>

					<description><![CDATA[<p>I recently wanted to work on Metric Extensions automation, and realized the task was a bit more complicated than I thought. My original idea was to look into the emcli verb list, but I was unlucky. In fact, emcli does not allow you to retrieve information about Metric Extensions. I had to dig further, looking [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/listing-and-querying-oracle-enterprise-manager-metric-extensions/">Listing and querying Oracle Enterprise Manager metric extensions</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">I recently wanted to work on Metric Extensions automation, and realized the task was a bit more complicated than I thought. My original idea was to look into the <code>emcli</code> verb list, but I was unlucky. In fact, <code>emcli</code> does not allow you to retrieve information about Metric Extensions.</p>



<pre class="wp-block-code"><code>$ emcli help | grep -i metric_extension
    export_metric_extension           -- Export a metric extension to an archive file
    get_unused_metric_extensions      -- Get a list of unused metric extensions
    import_metric_extension           -- Import a metric extension archive file
    publish_metric_extension          -- Publish a metric extension for use by all administrators
    save_metric_extension_draft       -- Save a deployable draft of a metric extension</code></pre>



<p class="wp-block-paragraph">I had to dig further, looking into the Enterprise Manager <code>SYSMAN</code> schema.</p>



<h2 id="metric-extensions-are-not-stored-under-mgmt-views" class="wp-block-heading">Metric extensions are not stored under <code>MGMT$</code> views</h2>



<p class="wp-block-paragraph">At first, I checked in the <code>MGMT$</code> views, in the <code>SYSMAN</code> schema. They usually hold all the relevant information you need when doing automation with the Enterprise Manager. The documentation gave me nothing about Metric Extensions there, and I did not find anything either:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT owner, object_name, object_type
FROM dba_objects
WHERE owner = 'SYSMAN'
AND UPPER(object_name) LIKE '%EXTENSION%'
ORDER BY object_name;

OWNER  OBJECT_NAME       OBJECT_TYPE
------ ----------------- ------------
SYSMAN EM_TYPE_EXTENSION PACKAGE
SYSMAN EM_TYPE_EXTENSION PACKAGE BODY</code></pre>



<p class="wp-block-paragraph">After a lot of searching, I found the undocumented <code>MEXT</code> views, where <strong>all the information on Metric Extensions is stored</strong>.</p>



<pre class="wp-block-code"><code>SELECT owner, object_name, object_type
FROM dba_objects
WHERE object_name LIKE '%MEXT%'
AND object_type IN ('TABLE', 'VIEW')
ORDER BY object_name;</code></pre>



<p class="wp-block-paragraph">This query returns more than a hundred objects. Among them, you can find a <code>EM_MEXT_VERSIONS</code> table.</p>



<h2 id="em_mext_versions-is-the-catalog" class="wp-block-heading"><code>EM_MEXT_VERSIONS</code> is the catalog</h2>



<p class="wp-block-paragraph"><code>EM_MEXT_VERSIONS</code> has one row per Metric Extension. Drafts, editable versions and published metrics are all stored here, with the same <code>name</code> column.</p>



<pre class="wp-block-code"><code>SQL&gt; DESC sysman.em_mext_versions

Name            Null?    Type
--------------- -------- -------------
MEXT_VERSION_ID NOT NULL NUMBER
TARGET_TYPE     NOT NULL VARCHAR2(256)
NAME            NOT NULL VARCHAR2(64)
VERSION         NOT NULL NUMBER
DISPLAY_NAME    NOT NULL VARCHAR2(64)
STATUS          NOT NULL NUMBER(2)
CREATION_DATE            DATE
...</code></pre>



<p class="wp-block-paragraph">Using a few useful columns, let’s see what’s inside:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT name, version, display_name, target_type, status, creation_date
FROM sysman.em_mext_versions
ORDER BY name, version;

NAME                          VERSION DISPLAY_NAME         TARGET_TYPE STATUS CREATION_DATE
----------------------------- ------- -------------------- ----------- ------ -------------
ME$minimal_uptime_check             1 Minimal Uptime Check host             2 24-AUG-26
ME$minimal_uptime_check             2 Minimal Uptime Check host             2 24-AUG-26
ME$minimal_uptime_check_clone       1 Minimal Uptime Check host             2 24-AUG-26</code></pre>



<p class="wp-block-paragraph">Every Metric Extension name starts with the <code>ME$</code> prefix. If you ever created a Metric Extension by hand in the web UI, the prefix is automatically added in the creation wizard. Regarding the <code>status</code> column, they are defined in the <code>SYSMAN.EM_MEXT</code> package:</p>



<ul class="wp-block-list">
<li><strong>0 &#8211; <code>MEXT_STATUS_EDITABLE</code></strong>: for Metric Extensions still under development. They can still be edited, but can’t be deployed to targets.</li>



<li><strong>1 &#8211; <code>MEXT_STATUS_DEPLOYABLE_DRAFT</code></strong>: for Metric Extensions which can’t be edited anymore, but can be deployed. Accessible only to you.</li>



<li><strong>2 &#8211; <code>MEXT_STATUS_PUBLISHED</code></strong>: for published Metric Extensions, accessible by any EM user.</li>
</ul>



<p class="wp-block-paragraph">Every row above shows <code>status = 2</code>, meaning that all versions were published.</p>



<p class="wp-block-paragraph">You can of course combine multiple <code>SYSMAN</code> views to retrieve the list of Metric Extensions and the targets they are deployed on:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT t.target_name, t.target_type, v.name AS metric_name, v.version, v.display_name AS metric_display_name
FROM sysman.em_mext_versions v
JOIN sysman.em_mext_target_assoc ta ON ta.mext_version_id = v.mext_version_id
JOIN sysman.mgmt$target t ON t.target_guid = ta.target_guid
ORDER BY t.target_name, v.name, v.version;

TARGET_NAME TARGET_TYPE METRIC_NAME             VERSION METRIC_DISPLAY_NAME
----------- ----------- ----------------------- ------- --------------------
vmogg       host        ME$minimal_uptime_check       1 Minimal Uptime Check
...</code></pre>



<h2 id="how-to-know-what-a-metric-extension-runs-" class="wp-block-heading">How to know what a metric extension runs ?</h2>



<p class="wp-block-paragraph">If a Metric Extension runs OS commands, querying <code>SYSMAN.EM_MEXT_VERSIONS</code> is enough to know everything about it. This becomes wrong if the Metric Extension carries a custom uploaded script or runs against the repository instead of a target. For these Metric Extensions, you should query <code>SYSMAN.EM_MEXT_ATTACHMENTS</code>, where the <code>FILENAME</code> gives you the name of the script being shipped with the metric:</p>



<pre class="wp-block-code"><code>SQL&gt; DESC sysman.em_mext_attachments

Name            Null?    Type
--------------- -------- -------------
MEXT_VERSION_ID NOT NULL NUMBER
FILENAME        NOT NULL VARCHAR2(255)
FILE_CONTENTS            BLOB</code></pre>



<p class="wp-block-paragraph">I cloned <code>ME$minimal_uptime_check</code> with <strong>Create Like</strong>, then in the Adapter step changed the command from a literal <code>/bin/echo</code> to <code>%perlBin%/perl</code> running an uploaded script, referenced as <code>%scriptsDir%/uptime_check.pl</code>. Custom files are attached from the same step, in the <strong>Upload Custom Files</strong> panel:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="370" height="110" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/001_mext-upload-attachment-dialog-7f2a19-1.png" alt="" class="wp-image-46984" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/001_mext-upload-attachment-dialog-7f2a19-1.png 370w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/001_mext-upload-attachment-dialog-7f2a19-1-300x89.png 300w" sizes="auto, (max-width: 370px) 100vw, 370px" /></figure>
</div>


<p class="wp-block-paragraph">Once confirmed, the file shows up in the adapter’s file list with its size, and gets packaged into the Metric Extension itself:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="700" height="245" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/002_mext-adapter-upload-custom-files-8b4c21-1.png" alt="" class="wp-image-46985" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/002_mext-adapter-upload-custom-files-8b4c21-1.png 700w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/002_mext-adapter-upload-custom-files-8b4c21-1-300x105.png 300w" sizes="auto, (max-width: 700px) 100vw, 700px" /></figure>
</div>


<p class="wp-block-paragraph">Querying the repository afterward confirms it landed exactly where the package source implied:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT v.name, v.version, a.filename, DBMS_LOB.GETLENGTH(a.file_contents) AS bytes
FROM sysman.em_mext_versions v
JOIN sysman.em_mext_attachments a ON a.mext_version_id = v.mext_version_id
WHERE v.name = 'ME$script_uptime_check';

NAME                   VERSION FILENAME        BYTES
---------------------- ------- --------------- -----
ME$script_uptime_check       1 uptime_check.pl    39</code></pre>



<h2 id="retrieving-the-full-metric-definition" class="wp-block-heading">Retrieving the full metric definition</h2>



<p class="wp-block-paragraph">There are a few other relevant LOB columns in <code>SYSMAN.EM_MEXT_VERSIONS</code>: <code>METADATA_DEFINITION</code>, <code>COLLECTION_DEFINITION</code> and <code>MEXT_ARCHIVE</code>. Selecting <code>METADATA_DEFINITION</code> for <code>minimal_uptime_check</code> returns the whole <code>&lt;Metric&gt;</code> definition as XML:</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT metadata_definition
FROM sysman.em_mext_versions
WHERE name = 'ME$minimal_uptime_check'
AND version = 1;

&lt;Metric NAME="ME$minimal_uptime_check" TYPE="TABLE"&gt;
    &lt;Display&gt;
        &lt;Label NLSID="NLS_METRIC_hostME$minimal_uptime_check"&gt;Minimal Uptime Check&lt;/Label&gt;
        &lt;Description NLSID="NLS_DESCRIPTION_hostME$minimal_uptime_check"&gt;Minimal test ME - single OS command column, no credentials.&lt;/Description&gt;
    &lt;/Display&gt;
    &lt;TableDescriptor&gt;
        &lt;ColumnDescriptor NAME="value" TYPE="NUMBER"&gt;
            &lt;Display&gt;
                &lt;Label NLSID="NLS_COLUMN_hostME$minimal_uptime_checkvalue"&gt;Value&lt;/Label&gt;
            &lt;/Display&gt;
        &lt;/ColumnDescriptor&gt;
    &lt;/TableDescriptor&gt;
    &lt;QueryDescriptor FETCHLET_ID="OSLineToken"&gt;
        &lt;Property NAME="command" SCOPE="GLOBAL"&gt;/bin/echo&lt;/Property&gt;
        &lt;Property NAME="scriptLoc" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;%scriptsDir%&lt;/Property&gt;
        &lt;Property NAME="args" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=1&lt;/Property&gt;
        &lt;Property NAME="delimiter" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;|&lt;/Property&gt;
        &lt;Property NAME="startsWith" SCOPE="GLOBAL" OPTIONAL="TRUE"&gt;em_result=&lt;/Property&gt;
        &lt;Property NAME="ENVEM_TARGET_NAME" SCOPE="INSTANCE" OPTIONAL="TRUE"&gt;NAME&lt;/Property&gt;
        &lt;CredentialRef NAME="OSCreds"&gt;&lt;/CredentialRef&gt;
    &lt;/QueryDescriptor&gt;
&lt;/Metric&gt;</code></pre>



<p class="wp-block-paragraph">A few observations here:</p>



<ul class="wp-block-list">
<li><code>FETCHLET_ID="OSLineToken"</code> is the fetchlet</li>



<li><code>command</code>, <code>args</code>, <code>delimiter</code> and <code>startsWith</code> are the fields filled when creating the metric.</li>
</ul>



<p class="wp-block-paragraph">Another column, <code>COLLECTION_DEFINITION</code>, gives the schedule of the Metric Extension. Here, it is set to run every 15 minutes, which is the default schedule for Metric Extensions.</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT collection_definition
FROM sysman.em_mext_versions
WHERE name = 'ME$minimal_uptime_check'
AND version = 1;

&lt;CollectionItem NAME="ME$minimal_uptime_check" UPLOAD="YES"&gt;
	&lt;Schedule&gt;
		&lt;IntervalSchedule INTERVAL="15" TIME_UNIT="Min"/&gt;
	&lt;/Schedule&gt;
	&lt;MetricColl NAME="ME$minimal_uptime_check"&gt;
	&lt;/MetricColl&gt;
&lt;/CollectionItem&gt;</code></pre>



<p class="wp-block-paragraph">Finally, <code>MEXT_ARCHIVE</code> holds the whole archive as a single <code>BLOB</code>. Exporting it to a file with <code>UTL_FILE</code> gives us a <code>.zip</code> file which holds the full definition of the Metric Extension. But you don’t need to do all of this, because this is the exact same zip that <code>emcli export_metric_extension</code> generates.</p>



<p class="wp-block-paragraph">A side note on <strong>repository-side Metric Extensions</strong>, which can also be created from the web UI. For these, there are a few pre-requisites: <code>TARGET_GUID</code> <strong>must be the first column</strong>, the query can only reference <code>MGMT$</code> repository views (not other <code>SYSMAN</code> tables), and it runs as the <code>MGMT_VIEW</code> user.</p>



<p class="wp-block-paragraph">Querying <code>SYSMAN.EM_MEXT_VERSIONS</code> gives you the full SQL query, under the <code>REPOS_SQL</code> column. Use <code>IS_REPOSITORY</code> to filter repository-side Metric Extensions.</p>



<pre class="wp-block-code"><code>SQL&gt; SELECT name, version, is_repository, repos_sql
FROM sysman.em_mext_versions
WHERE name = 'ME$sql_target_count';

NAME                VERSION IS_REPOSITORY REPOS_SQL
------------------- ------- ------------- --------------------------------------------------------------------------------------------
ME$sql_target_count       1             1 SELECT target_guid, target_type, 1 AS host_count FROM mgmt$target WHERE target_type = 'host'</code></pre>



<p class="wp-block-paragraph">I hope this will help you navigate Metric Extensions outside of the web UI, for automation work. This gave us a first glimpse of the archive format, which deserves a closer look.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/listing-and-querying-oracle-enterprise-manager-metric-extensions/">Listing and querying Oracle Enterprise Manager metric extensions</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/listing-and-querying-oracle-enterprise-manager-metric-extensions/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>TNS_ADMIN &#8211; which *.ora files Oracle will use</title>
		<link>https://www.dbi-services.com/blog/tns_admin-which-ora-files-oracle-will-use/</link>
					<comments>https://www.dbi-services.com/blog/tns_admin-which-ora-files-oracle-will-use/#respond</comments>
		
		<dc:creator><![CDATA[Martin Bracher]]></dc:creator>
		<pubDate>Thu, 17 Sep 2026 12:31:39 +0000</pubDate>
				<category><![CDATA[Oracle]]></category>
		<category><![CDATA[background-processes]]></category>
		<category><![CDATA[dblinks]]></category>
		<category><![CDATA[name-resolution]]></category>
		<category><![CDATA[TNS_ADMIN]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=41089</guid>

					<description><![CDATA[<p>Introduction Oracle uses some configuration files (*.ora) related to the network. Location of the *.ora files used Per default, Oracle will look for these files in $ORACLE_HOME/network/admin; If you have more than one ORACLE_HOME, then you have to maintain it for each home, or you can create symbolic links to the &#8220;master&#8221; file. And if [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/tns_admin-which-ora-files-oracle-will-use/">TNS_ADMIN &#8211; which *.ora files Oracle will use</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="h-introduction" class="wp-block-heading">Introduction</h2>



<p class="wp-block-paragraph">Oracle uses some configuration files (*.ora) related to the network.</p>



<ul class="wp-block-list">
<li><kbd>tnsnames.ora</kbd>: A mapping of an alias name to a connect string for Oracle. A kind of &#8220;/etc/hosts&#8221; for Oracle databases</li>



<li><kbd>ldap.ora</kbd>: Configuration of ldap servers to use. A kind of &#8220;/etc/resolv.conf&#8221; for Oracle products.</li>



<li><kbd>sqlnet.ora</kbd>: General network settings like domain, timeout-parameters, wallet-location and more.</li>



<li><kbd>listener.ora</kbd>: Configuration of the listeners</li>
</ul>



<h2 id="h-location-of-the-ora-files-used" class="wp-block-heading">Location of the *.ora files used</h2>



<p class="wp-block-paragraph">Per default, Oracle will look for these files in $ORACLE_HOME/network/admin; If you have more than one ORACLE_HOME, then you have to maintain it for each home, or you can create symbolic links to the &#8220;master&#8221; file.</p>



<p class="wp-block-paragraph">And if you use Readonly-Oracle-Home (&#8220;rooh&#8221;, <code>roohctl -enable</code>). That is a great feature to relocate all database- and configuration specific files from the software directory to the ORACLE_BASE. The dbs-directories of all homes will be centralized in $ORACLE_BASE/dbs, and the home-specific configuration- and log-files will be moved to the ORACLE_BASE/homes/<strong><em>homename</em></strong>. With rooh enabled, Oracle looks for the TNS configuration in $ORACLE_BASE/homes/<em><strong>homename</strong></em>/network/admin; <strong><em>homename</em></strong> is the name in the oraInventory. To show the current top-level directory, use</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
oracle&gt;  orabasehome
/u00/app/oracle/homes/OraDB23Home4
</pre></div>


<p class="wp-block-paragraph">Instead of the default, depending on the ORACLE_HOME, you can set the variable TNS_ADMIN in your environment to a central directory containing above *.ora files.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
oracle&gt;  export TNS_ADMIN=/u01/app/oracle/network/admin
</pre></div>


<p class="wp-block-paragraph">If this variable is set, then Oracle uses this directory instead of the default $ORACLE_HOME/network/admin or orabasehome/network/admin.</p>



<p class="wp-block-paragraph">In this context, &#8220;Oracle&#8221; means the binary you use. If $TNS_ADMIN is set in the shell where the binary is started, it takes the value of it to look for Oracle&#8217;s network files.</p>



<p class="wp-block-paragraph">If you use sqlplus, then it looks for the *.ora files in $TNS_ADMIN to resolve the given connect alias of your connect string. </p>



<p class="wp-block-paragraph">If you start the Oracle database-instance, then it uses the *.ora files in $TNS_ADMIN to resolve connect aliases for database links and data guard connects. If unset, it uses the default location as fallback.</p>



<p class="wp-block-paragraph">In summary the priority of the TNS locations:</p>



<ol class="wp-block-list">
<li>$TNS_ADMIN, if it is set</li>



<li>$ORACLE_BASE/homes/&lt;home_name&gt;/network/admin, if readonly-oracle_home is activated</li>



<li>$ORACLE_HOME/network/admin</li>
</ol>



<h2 id="h-find-out-the-tns-location-that-is-used" class="wp-block-heading">Find out the TNS location that is used</h2>



<h3 id="h-tnsping" class="wp-block-heading">tnsping</h3>



<p class="wp-block-paragraph">For sqlplus it is easy to find out what it will use. You can check it before start in your shell with tnsping:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
oracle&gt;  tnsping DB01

TNS Ping Utility for IBM/AIX RISC System/6000: Version 19.0.0.0.0 - Production on 12-DEC-2025 09:16:10

Copyright (c) 1997, 2025, Oracle.  All rights reserved.

Used parameter files:
/u01/app/oracle/network/sqlnet.ora

Used LDAP adapter to resolve the alias
Attempting to contact (DESCRIPTION=...)
</pre></div>


<p class="wp-block-paragraph">&#8220;Used parameter files&#8221; shows the directory it looks for the *.ora files.</p>



<p class="wp-block-paragraph">If we start an instance interactively with sqlplus, we also know the location, as described above. </p>



<p class="wp-block-paragraph">But caution: If you use Grid Infrastructrue (clusterware) and you start the instance with &#8220;srvctl&#8221;, then a TNS_ADMIN in your current shell is irrelevant. The instance is started by clusterware in background and it uses a minimal shell. It does not source the shell-profile of Oracle (.profile, .bash_profile), so probably your TNS_ADMIN is unknown to clusterware. </p>



<p class="wp-block-paragraph">That means, if a database is started interactively or via clusterware, it may behave differently. Maybe with one method db-links are working, with the other not. </p>



<p class="wp-block-paragraph">Best practice: Alwas start database instances with the same mechanism. If using clusterware, always use srvctl  to start it.</p>



<p class="wp-block-paragraph">But if we have issuses with connects of db-links or Data Guard, how we can find out what directory the instance uses to look for the network files?</p>



<h3 id="h-os-tools" class="wp-block-heading">OS-tools</h3>



<p class="wp-block-paragraph">On the database server, we can find it out by OS tools:</p>



<p class="wp-block-paragraph">Get the process-ID of an instance-process, e.g. pmon (<code>ps -ef |grep pmon_$ORACLE_SID</code>)</p>



<p class="wp-block-paragraph">Then, for this process-ID we can show the environment-variables of this process (ps eww ID) and look for TNS_ADMIN. If we do not find it, TNS_ADMIN is the default $ORACLE_HOME/network/admin or $ORACLE_BASE/homes/<em>homename</em>/network/admin</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
ps eww `ps -ef |grep -v grep|grep pmon_$ORACLE_SID|awk &#039;{print $2}&#039;`  \
   | grep TNS_|sed -e &#039;s/.*TNS_ADMIN=//&#039; |sed -e &#039;s/ .*$//g&#039;
</pre></div>


<p class="wp-block-paragraph">Caution! On Linux, often it does not work for the process owner oracle, even if /proc/&lt;pid&gt;/environment shows readable for Oracle. If $ORACLE_HOME/bin/oracle has the suid-bit (-rws-s&#8211;x), the kernel blocks the access. In this case, you need root privileges to run above command. If the 1st line (without \) does not return any variables, then you are affected by this security limitation.</p>



<h3 id="h-inside-the-database" class="wp-block-heading">Inside the database</h3>



<p class="wp-block-paragraph">But, can we find it out by connecting to the database by running the following code?</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
set serveroutput on;
DECLARE
  V_ENVIROMENT VARCHAR2(500);
BEGIN
  SYS.DBMS_SYSTEM.GET_ENV(&#039;TNS_ADMIN&#039;, V_ENVIROMENT);
  DBMS_OUTPUT.PUT_LINE(&#039;TNS_ADMIN=&#039;||V_ENVIROMENT);
END;
/
</pre></div>


<p class="wp-block-paragraph">Unfortunately not. The Oracle process connecting the database inherits the environment of our environment. For a direct connect (/ as sysdba) it is our shell-environment, for a remote-connect (@sqlnet-alias) it is the environment of the listener. This way we get the TNS_ADMIN environment of our process, and not that of the server processes.</p>



<p class="wp-block-paragraph">But how can we get the environment of the running server processes? For this we need a little trick: We make the query via a DBMS_SCHEDULER job. This means that it is executed via a server process.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
 DBMS_SCHEDULER.create_job (
      job_name   =&gt; &#039;TNS_ADMIN&#039;,
      job_type   =&gt; &#039;PLSQL_BLOCK&#039;,
      job_action =&gt; q&#039;&#x5B;
        DECLARE
          v_tns_admin VARCHAR2(4000);
        BEGIN
          DBMS_SYSTEM.GET_ENV(&#039;TNS_ADMIN&#039;, v_tns_admin);
          DBMS_OUTPUT.PUT_LINE(v_tns_admin);
        END;
      ]&#039;,
      start_date =&gt; SYSTIMESTAMP,
      enabled    =&gt; TRUE,
      auto_drop  =&gt; TRUE
    );
</pre></div>


<p class="wp-block-paragraph">The result can be found in ALL_SCHEDULER_JOB_RUN_DETAILS. Relevant are only records after startup time, because the environment can only be changed at startup. That&#8217;s why we should get exactly 1 result with &#8220;distinct&#8221;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
select distinct output from ALL_SCHEDULER_JOB_RUN_DETAILS 
where job_name=&#039;TNS_ADMIN&#039; 
and log_date &gt; (select startup_time from v$instance);
</pre></div>


<p class="wp-block-paragraph">An empty result means that the TNS_ADMIN variable was not set and the database is using the default (see above).</p>



<h2 id="h-debugging-name-resolution-problems" class="wp-block-heading">Debugging name resolution problems</h2>



<p class="wp-block-paragraph">Next time, if you get errors using a database link in a scheduled job (using the server processes environment):</p>



<pre class="wp-block-code"><code>ORA-12154: TNS:could not resolve the connect identifier specified</code></pre>



<p class="wp-block-paragraph">Check what the database link will connect to (e.g. DB01.domain) and what TNS_ADMIN (or default-value) your database is using.  </p>



<p class="wp-block-paragraph">For Data Gurad debugging it is not so easy. For the primary site above trick works, but not on the standby site. It is mounted or read-only. If you have root-access, you can check the environment file of the process (see above). Otherwise I recommend to restart the standby database, then you know what TNS_ADMIN was set. </p>
<p>L’article <a href="https://www.dbi-services.com/blog/tns_admin-which-ora-files-oracle-will-use/">TNS_ADMIN &#8211; which *.ora files Oracle will use</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/tns_admin-which-ora-files-oracle-will-use/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Oracle Forms: Java Web Start or FSAL?</title>
		<link>https://www.dbi-services.com/blog/oracle-forms-java-web-start-or-fsal/</link>
					<comments>https://www.dbi-services.com/blog/oracle-forms-java-web-start-or-fsal/#respond</comments>
		
		<dc:creator><![CDATA[David Diab]]></dc:creator>
		<pubDate>Thu, 17 Sep 2026 07:30:51 +0000</pubDate>
				<category><![CDATA[Oracle Fusion Middleware]]></category>
		<category><![CDATA[WebLogic]]></category>
		<category><![CDATA[FMW]]></category>
		<category><![CDATA[Fusion Middleware]]></category>
		<category><![CDATA[Oracle]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=46823</guid>

					<description><![CDATA[<p>A customer recently asked me about the difference between Java Web Start and the Forms Standalone Launcher for running Oracle Forms applications. I found it a very good question. From the user’s perspective, both methods look similar: the Forms application opens in its own Java window. The real difference is how the application is launched, [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/oracle-forms-java-web-start-or-fsal/">Oracle Forms: Java Web Start or FSAL?</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">A customer recently asked me about the difference between Java Web Start and the Forms Standalone Launcher for running Oracle Forms applications.</p>



<p class="wp-block-paragraph">I found it a very good question. From the user’s perspective, both methods look similar: the Forms application opens in its own Java window. The real difference is how the application is launched, deployed, secured, and maintained on the workstation.</p>



<span id="more-46823"></span>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="576" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/JWS_VS_FSAL-1024x576.png" alt="" class="wp-image-46827" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/JWS_VS_FSAL-1024x576.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/JWS_VS_FSAL-300x169.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/JWS_VS_FSAL-768x432.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/JWS_VS_FSAL-1536x864.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/09/JWS_VS_FSAL.png 1672w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Here is the practical answer, based on experience with Oracle Forms environments.</p>



<p class="wp-block-paragraph" id="h-first-both-methods-use-the-same-forms-architecture"><strong>First, both methods use the same Forms architecture</strong></p>



<p class="wp-block-paragraph">Whether you use Java Web Start or FSAL:</p>



<ul class="wp-block-list">
<li>The Forms application remains hosted centrally on the application server.</li>



<li>The <code>.fmx</code>, <code>.mmx</code>, and <code>.plx</code> modules stay on the server.</li>



<li>A Java-based Forms client runs on the user’s workstation.</li>



<li>The client communicates with Oracle Forms Services, generally hosted on WebLogic Server.</li>



<li>The application itself normally does not need to be rewritten when changing the launch method.</li>
</ul>



<p class="wp-block-paragraph">Therefore, FSAL is not a return to the old Forms 6i client/server architecture. It only provides a similar desktop experience,</p>



<h1 class="wp-block-heading">Java Web Start</h1>



<p class="wp-block-paragraph">Java Web Start, or JWS, launches the application using a JNLP file.</p>



<p class="wp-block-paragraph">The user normally opens a Forms URL:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
https://forms.example.com/forms/frmservlet?config=webstart
</pre></div>


<p class="wp-block-paragraph">The server generates a <code>.jnlp</code> file, which is downloaded and opened by Java Web Start. The Forms application then runs in a separate Java window.</p>



<p class="wp-block-paragraph">Oracle describes Java Web Start as a <strong>semi-browserless configuration</strong>: the browser can be used to initiate the application, but the application does not run inside the browser window.</p>



<h3 class="wp-block-heading">Advantages of Java Web Start</h3>



<ul class="wp-block-list">
<li>Users can start the application from a familiar web link.</li>



<li>The JNLP configuration is generated centrally by the Forms server.</li>



<li>Java archives can be downloaded and cached automatically.</li>



<li>It can integrate well with environments where authentication starts in the browser.</li>



<li>It usually requires limited change when replacing an older browser-based Forms deployment.</li>
</ul>



<h3 class="wp-block-heading">Disadvantages of Java Web Start</h3>



<ul class="wp-block-list">
<li>It depends on Java Web Start technology, which is associated mainly with Java 8.</li>



<li>Java Web Start is no longer included in modern JDK releases.</li>



<li>JNLP file associations can behave differently depending on the workstation and browser.</li>



<li>Browser security settings may prevent the JNLP file from opening automatically.</li>



<li>Java cache problems are relatively common.</li>



<li>Certificate changes, proxies, and local Java security settings can affect application startup.</li>



<li>Troubleshooting frequently involves the user’s workstation rather than the Forms server.</li>
</ul>



<h3 class="wp-block-heading">My experience with Java Web Start</h3>



<p class="wp-block-paragraph">Once correctly configured, Java Web Start works well. However, many operational incidents are related to the client environment:</p>



<ul class="wp-block-list">
<li>The <code>.jnlp</code> file is downloaded but not opened.</li>



<li>The workstation uses the wrong Java version.</li>



<li>The operating system loses the JNLP file association.</li>



<li>An old JAR remains in the Java cache.</li>



<li>A certificate renewal causes a new security warning.</li>



<li>Browser updates change how downloaded JNLP files are handled.</li>
</ul>



<p class="wp-block-paragraph">These are rarely Forms application problems, but they still generate support requests.</p>



<h3 class="wp-block-heading">When should you choose Java Web Start?</h3>



<p class="wp-block-paragraph">Java Web Start remains reasonable when:</p>



<ul class="wp-block-list">
<li>The organization already has a stable and managed Java 8 environment.</li>



<li>The existing JWS deployment works reliably.</li>



<li>Users need to launch Forms from a browser-based portal.</li>



<li>The authentication or SSO process requires a browser.</li>



<li>The objective is a short-term migration with minimal client-side change.</li>
</ul>



<p class="wp-block-paragraph">I would be more cautious about selecting JWS for a completely new, long-term deployment because of its dependency on legacy Java deployment technology.</p>



<h1 class="wp-block-heading">Forms Standalone Launcher</h1>



<p class="wp-block-paragraph">The Forms Standalone Launcher, or FSAL, launches Oracle Forms without relying on a browser or Java Web Start.</p>



<p class="wp-block-paragraph">The workstation runs the <code>frmsal.jar</code> launcher directly:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
java -jar frmsal.jar -url &quot;https://forms.example.com/forms/frmservlet?config=standaloneapp&quot;
</pre></div>


<p class="wp-block-paragraph">In practice, users do not need to enter this command. It can be included in a desktop shortcut, shell script, batch file, or software deployment package.</p>



<h3 class="wp-block-heading">Advantages of FSAL</h3>



<ul class="wp-block-list">
<li>It is fully independent of the browser.</li>



<li>It does not require Java Web Start or a browser plug-in.</li>



<li>It provides a clean desktop application experience.</li>



<li>Browser updates do not affect the application launch.</li>



<li>The startup command can be standardized and controlled by the IT team.</li>



<li>Java parameters, memory settings, truststores, and logging can be explicitly configured.</li>



<li>It is generally easier to reproduce and troubleshoot startup issues.</li>



<li>It aligns better with current Java versions and Oracle’s modern Forms direction.</li>
</ul>



<h3 class="wp-block-heading">Disadvantages of FSAL</h3>



<ul class="wp-block-list">
<li>The <code>frmsal.jar</code> launcher must be distributed to every workstation.</li>



<li>The launcher version must match the Oracle Forms server version and patch level.</li>



<li>IT must maintain the desktop shortcut or startup script.</li>



<li>Java and the required certificates must be correctly deployed on each workstation.</li>



<li>Updating or patching Forms may require redistributing <code>frmsal.jar</code>.</li>



<li>SSO, single sign-off, JavaScript integration, and communication with web pages require careful validation.</li>



<li>The initial workstation packaging may require more preparation than simply publishing a URL.</li>
</ul>



<h3 class="wp-block-heading">My experience with FSAL</h3>



<p class="wp-block-paragraph">FSAL generally requires more preparation during the initial rollout. The Java runtime, <code>frmsal.jar</code>, truststore, startup parameters, and shortcut must be packaged correctly.</p>



<p class="wp-block-paragraph">Once that work is completed, the result is usually more predictable.</p>



<p class="wp-block-paragraph">The application is no longer affected by browser behavior, JNLP file associations, or Java Web Start cache issues. When a startup problem occurs, the launch command and console output also provide a clearer troubleshooting path.</p>



<p class="wp-block-paragraph">One important operational point is the management of <code>frmsal.jar</code>. It should not be treated as a generic launcher that can be reused forever. It is version-specific and must remain aligned with the Forms installation.</p>



<h3 class="wp-block-heading">When should you choose FSAL?</h3>



<p class="wp-block-paragraph">FSAL is generally the better choice when:</p>



<ul class="wp-block-list">
<li>You are implementing a new Oracle Forms deployment.</li>



<li>You are upgrading to a recent Oracle Forms release.</li>



<li>You want to remove browser dependencies.</li>



<li>You want to use a modern and centrally managed Java runtime.</li>



<li>Workstations are managed using tools such as Microsoft Intune, SCCM, or another software distribution platform.</li>



<li>You need predictable startup parameters and easier troubleshooting.</li>



<li>Forms is primarily used as a business desktop application.</li>
</ul>



<h1 class="wp-block-heading">Direct comparison</h1>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Area</th><th>Java Web Start</th><th>FSAL</th></tr><tr><td>Launch method</td><td>URL and JNLP file</td><td>Java command, script, or shortcut</td></tr><tr><td>Browser dependency</td><td>Partial</td><td>None</td></tr><tr><td>Java Web Start required</td><td>Yes</td><td>No</td></tr><tr><td>User experience</td><td>Separate Java window</td><td>Separate Java window</td></tr><tr><td>Client deployment</td><td>JNLP association and compatible Java</td><td>Java runtime and <code>frmsal.jar</code></td></tr><tr><td>Central configuration</td><td><code>formsweb.cfg</code> and JNLP</td><td><code>formsweb.cfg</code> and launch package</td></tr><tr><td>Main operational risk</td><td>Java cache, JNLP, browser and file association</td><td>Launcher distribution and version alignment</td></tr><tr><td>Troubleshooting</td><td>Often distributed across browser, Java and OS</td><td>Generally more direct through command and logs</td></tr><tr><td>Best fit</td><td>Existing stable JWS deployment</td><td>New or modernized deployment</td></tr></tbody></table></figure>



<h1 class="wp-block-heading">My recommendation</h1>



<p class="wp-block-paragraph">For an existing, stable Java Web Start environment, I would not migrate to FSAL only for the sake of changing technology. The migration should provide a clear operational or security benefit.</p>



<p class="wp-block-paragraph">For a new deployment or a major Oracle Forms upgrade, I would generally choose FSAL.</p>



<p class="wp-block-paragraph">It removes the browser and Java Web Start dependencies, gives the IT team better control over the client runtime, and provides a more predictable operational model.</p>



<p class="wp-block-paragraph">The final choice should nevertheless be validated against:</p>



<ul class="wp-block-list">
<li>The Oracle Forms version</li>



<li>The certified client Java version</li>



<li>The SSO implementation</li>



<li>WebUtil and custom JAR usage</li>



<li>JavaScript integration</li>



<li>Proxy and TLS requirements</li>



<li>The workstation deployment strategy</li>
</ul>



<p class="wp-block-paragraph">The key point is simple: <strong>Java Web Start is easier to expose through a URL, while FSAL is easier to control as a managed desktop application.</strong></p>



<p class="wp-block-paragraph"><strong><em>Oracle documentation</em></strong></p>



<ul class="wp-block-list">
<li><a href="https://docs.oracle.com/en/middleware/developer-tools/forms/12.2.1.4/working-forms/deploying-applications.html" target="_blank" rel="noreferrer noopener">Oracle Forms 12.2.1.4 – Client Configurations, Java Web Start and FSAL</a></li>



<li><a href="https://docs.oracle.com/en/middleware/developer-tools/forms/14.1.2/working-forms/oracle-forms-standalone-launcher.html" target="_blank" rel="noreferrer noopener">Oracle Forms 14.1.2 – Forms Standalone Launcher</a></li>



<li><a href="https://docs.oracle.com/cd/G29095_01/developer-tools/forms/14.1.2/working-forms/working-oracle-forms.pdf" target="_blank" rel="noreferrer noopener">Oracle Forms 14.1.2 – Working with Oracle Forms</a></li>
</ul>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/oracle-forms-java-web-start-or-fsal/">Oracle Forms: Java Web Start or FSAL?</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-forms-java-web-start-or-fsal/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 20:02:21 by W3 Total Cache
-->