Oracle Data Guard physical standbys keep redo synchronized automatically — until the primary adds a datafile that the standby can’t place on disk. When STANDBY_FILE_MANAGEMENT=MANUAL, the standby refuses to guess where the new file should live. Managed Standby Recovery stops instead, leaving the control file ahead of the actual datafiles and the standby parked at a consistent SCN.
This post walks through a real 19c multitenant incident: MRP0 hit ORA-01274 after a new datafile couldn’t be added, recovery stalled, and the fix required creating the missing file inside the correct pluggable database container, then resuming and verifying redo apply. In this incident, the missing datafile belonged to PROD. Creating the file from within the owning PDB resolved the placeholder, after which Redo Apply could be restarted from CDB$ROOT.
Failure Diagnosis
The alert log during redo apply: Recovery of Online Redo Log: Thread 1 Group 4 Seq 105902 Reading mem 0 Mem# 0: /uora/PROD/db/product/19.0.0/dbs/broken3 2025-05-12T08:49:56.027319-04:00 PROD(3):File #433 added to control file as ‘UNNAMED00433’ because PROD(3):the parameter STANDBY_FILE_MANAGEMENT is set to MANUAL PROD(3):The file should be manually created to continue. PR00 (PID:2796228): MRP0: Background Media Recovery terminated with error 1274 2025-05-12T08:49:56.092976-04:00 Errors in file /uora/PROD/db/diag/rdbms/proddr/cdbPROD/trace/cdbPROD_pr00_2796228.trc: ORA-01274: cannot add data file that was originally created as ‘/uora/PROD/data/proddata/users02.dbf’ PR00 (PID:2796228): Managed Standby Recovery not using Real Time Apply Recovery interrupted! 2025-05-12T08:49:57.933554-04:00 Recovery stopped due to failure in applying recovery marker (opcode 17.30). Datafiles are recovered to a consistent state at change 36278361292 but controlfile could be ahead of datafiles. Stopping change tracking
Three things happening in sequence:
1. **Control file registered a file that doesn’t exist.** The redo stream carried a datafile creation from the primary. The standby’s control file logged it — but because STANDBY_FILE_MANAGEMENT=MANUAL, the standby recorded it as UNNAMED00433 instead of creating it.
2. **Recovery stopped on the recovery marker.** Opcode 17.30 is the file operation that couldn’t be satisfied. The standby parked at SCN 36278361292 — a point where datafiles are internally consistent, but the control file believes file 433 exists when it doesn’t.
3. **MRP0 exited.** The managed standby process shut down cleanly. The alert log shows “not using Real Time Apply,” indicating recovery was interrupted and the standby could not continue applying the current redo stream.
**Root cause:** Configuration, not corruption. STANDBY_FILE_MANAGEMENT=MANUAL means every new primary datafile must be created manually on the standby. With AUTO and proper DB_FILE_NAME_CONVERT / PDB_FILE_NAME_CONVERT setup, new files appear automatically. This environment chose MANUAL, so a routine tablespace addition on the primary became a recovery outage on the standby.
Confirm the standby’s role and find the placeholder. Run from CDB$ROOT:
SET ECHO OFF PAGESIZE 60 LINESIZE 120 TRIMSPOOL ON COL NAME FORMAT A50 SQL> SELECT name, open_mode, database_role FROM v$database; NAME OPEN_MODE DATABASE_ROLE ——— ——————– —————- CDBSTBY MOUNTED PHYSICAL STANDBY SQL> SELECT FILE#, NAME FROM V$DATAFILE WHERE NAME LIKE ‘%UNNAMED%’; FILE# NAME ———- ————————————————– 433 /u01/app/oracle/product/19.0.0/dbhome_1/dbs/UNNAMED00433
File number is 433. Which PDB owns it? Use CON_ID:
COL CON_ID FORMAT 9999 COL FILE# FORMAT 9999 COL NAME FORMAT A50 SQL> SELECT con_id, file#, name FROM V$DATAFILE WHERE file# = 433; CON_ID FILE# NAME ———- —— ————————————————– 3 433 /u01/app/oracle/product/19.0.0/dbhome_1/dbs/UNNAMED00433
CON_ID = 3 is PROD. That’s where the fix must run.
## Step 2 — PDB Recovery: Create the File in the Right Container
From CDB$ROOT, the command appeared to succeed:
SQL> ALTER DATABASE CREATE DATAFILE ‘UNNAMED00433’ AS ‘/uora/PROD/data/proddrdata/users02.dbf’; Database altered. “`
But checking from the PDB showed the placeholder still registered:
SQL> ALTER SESSION SET CONTAINER = PROD; Session altered. SQL> SELECT FILE#, NAME FROM V$DATAFILE WHERE NAME LIKE ‘%UNNAMED%’; FILE# NAME ———- ————————————————– 433 /uora/PROD/db/product/19.0.0/dbs/UNNAMED00433
After switching to the PROD container and reissuing the command:
SQL> ALTER DATABASE CREATE DATAFILE ‘UNNAMED00433’ AS ‘/uora/PROD/data/proddrdata/users02.dbf’; Database altered. SQL> SELECT FILE#, NAME FROM V$DATAFILE WHERE NAME LIKE ‘%UNNAMED%’; no rows selected
**Important:** Validate this behavior against Oracle 19c documentation and test in non-production before applying to your standby.
## Step 3 — Redo Apply Resumption
SQL> ALTER SESSION SET CONTAINER = CDB$ROOT; Session altered. SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION; Database altered. SQL> SELECT PROCESS, STATUS, THREAD#, SEQUENCE# FROM V$MANAGED_STANDBY; PROCESS STATUS THREAD# SEQUENCE# ———- ————— ——- ——— MRP0 APPLYING_LOG 1 105902 ARCH CLOSING 1 105905 RFS IDLE 1 105906
MRP0 is back in APPLYING_LOG.
## Step 4 — Expect More: The Second UNNAMED File
Recovery resumed and encountered another placeholder:
“`sql SQL> ALTER SESSION SET CONTAINER = PROD; SQL> SELECT FILE#, NAME FROM V$DATAFILE WHERE NAME LIKE ‘%UNNAMED%’; FILE# NAME ———- ————————————————– 435 /uora/PROD/db/product/19.0.0/dbs/UNNAMED00435 SQL> ALTER DATABASE CREATE DATAFILE ‘UNNAMED00435’ AS ‘/uora/PROD/data/proddrdata/transaction_table_16.dbf’; Database altered.
Expect a loop. Every datafile added on the primary after the gap will appear as another UNNAMED entry. Grep the alert log for the full backlog first:
ALTER SESSION SET CONTAINER = CDB$ROOT; ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION; “`
Verify:
SELECT con_id, file#, name FROM V$DATAFILE WHERE name LIKE ‘%UNNAMED%’; SELECT process, status, thread#, sequence# FROM V$MANAGED_STANDBY WHERE process LIKE ‘MRP%’; SELECT * FROM V$DATAGUARD_STATS;
Clean results: zero UNNAMED rows, MRP0 in APPLYING_LOG, apply lag returning to normal.
## Alternative: Restore from Backup
If redo is unavailable or purged:
1. Identify primary backup with missing file 2. Restore to standby location 3. Register with control file via RMAN 4. Resume recovery
Follow Oracle RMAN and Data Guard documentation for your configuration.
## Lessons Learned
**Decide STANDBY_FILE_MANAGEMENT deliberately.** MANUAL works for locked-down environments. AUTO prevents this class of manual intervention when file naming and storage support it. ما شاء الله — the infrastructure, when well-designed, removes recovery work.
**Session container context can affect recovery steps.** In this incident, CREATE DATAFILE from CDB$ROOT appeared to succeed but didn’t resolve the placeholder until run from PROD PDB. Validate against Oracle 19c documentation and test in non-production.
**Expect a loop.** Every datafile added after the gap arrives as another UNNAMED entry. Grep the alert log for full backlog first.
**Verify before closing.** Confirm zero UNNAMED files, MRP0 running, settled lag before closing incident.
## Conclusion
ORA-01274 on a physical standby is a configuration boundary, not data loss. This incident resolved via CREATE DATAFILE in the PROD PDB, followed by managed recovery restart from CDB$ROOT. Before applying: Verify steps against Oracle 19c documentation, test in non-production, verify standby status and lag.
Alhamdulillah, this post brings together one of the more involved upgrade journeys I have worked through recently — taking an Oracle E-Business Suite 12.2 database stack from 12.1.0.2 all the way to 19c, on a single standalone host running Oracle Restart.
What made this engagement worth writing up is that it was not a clean, textbook upgrade. The Grid Infrastructure layer presented several unexpected challenges — a chain of CLSRSC failures that had to be cleared before it would move, and once Grid was finally on 19c, the database upgrade and the non-CDB to PDB conversion had their own set of gotchas around ASM registration, password files, and UTL_FILE_DIR.
I have combined both phases — the Grid Infrastructure upgrade and the database upgrade plus multitenant conversion — into one walkthrough, because in the real world they are not separate stories. The GI upgrade forms the foundation for every subsequent phase. If you only read GI upgrade blogs in isolation, you miss how the pieces connect.
All hostnames, database names, ASM diskgroup names, and paths below are genericized. The technical substance is exactly as it played out.
Published: June 2026
Environment: Oracle EBS 12.2.4 | AD/TXK Delta 13 | Oracle Database 19c (19.30) (Versions shown are from the environment used in this engagement, not a minimum certified baseline — always confirm your own certified levels.)
Executive Summary
This upgrade involved moving an Oracle E-Business Suite 12.2 database environment from Oracle Database 12.1.0.2 to 19c on a standalone Oracle Restart host. The journey consisted of four major phases:
Oracle Restart (Grid Infrastructure) upgrade from 12.1.0.2 to 19c
Database upgrade from 12.1.0.2 to 19c using DBUA
Non-CDB to PDB conversion using Oracle EBS TXK utilities
EBS application-tier reconfiguration and validation
The most time-consuming portions were not the upgrades themselves, but resolving legacy Oracle Restart configuration issues, rebuilding HAS components, handling ASM registration after the GI upgrade, addressing password-file requirements during the PDB conversion, and resolving UTL_FILE_DIR migration challenges.
This guide covers an end-to-end Oracle EBS 12.2.4 upgrade to Oracle Database 19c, including Oracle Restart upgrade, DBUA upgrade, non-CDB to PDB conversion, AutoConfig remediation, and post-upgrade validation.
Environment overview
Component
Detail
Platform
Single standalone Linux host (dbhost) running Oracle Restart / standalone HAS — not full RAC Clusterware
Apps tier
Separate node (appshost) under the applmgr-style owner
EBS release
12.2.4
AD level
AD.C.Delta.13
TXK level
TXK.C.Delta.13
Source GI
12.1.0.2
Target GI
19c (19.30)
Source DB
Non-CDB EBS database, SID ebsdb, on 12.1.0.2
Target DB
19c (19.30), ebsdb plugged in as a lowercase PDB inside CDB EBSCDB
The apps tier and ASM storage stay in place; everything between them moves up to 19c, and the standalone non-CDB becomes a pluggable database inside a container.
A note on certification before you begin
Do not assume any upgrade path is valid. Before starting, confirm that your EBS code level, AD/TXK delta level, database RU, and interoperability patches are certified per the Oracle EBS 12.2 and Oracle Database 19c certification and interoperability documentation. Skipping this check is the most common reason an upgrade that “worked in the lab” falls apart in the field. Database RU levels and EBS interoperability certifications change frequently, so always verify the latest certification matrix in My Oracle Support before implementation — and treat the 19.30 RU referenced here as the level used in this engagement, not the only supported target.
Rough timeline (for maintenance-window planning)
Phase
Typical duration
GI (Oracle Restart) upgrade
30–60 min
DBUA database upgrade
2–4 hrs
Non-CDB → PDB conversion
~15 min
AutoConfig & validation
30–60 min
These are working estimates from this engagement, not guarantees — database size, invalid-object count, and hardware all move the numbers. Treat the GI and DBUA phases as the long poles when you size the window.
Phase 1 — Grid Infrastructure 12.1.0.2 → 19c
The 19c Grid software was already laid down (gold image extracted into the new home), and the OUI installer had paused — as it always does — waiting for rootupgrade.sh to be run as root. That is where everything went sideways.
The first wall: CLSRSC-324 — Could not open old init.cssd
Running the upgrade script:
/u01/app/19.0.0/grid/rootupgrade.sh
failed almost immediately:
CLSRSC-324: Could not open old init.cssd
Died at /u01/app/19.0.0/grid/crs/install/s_crsutils.pm line 1569
The 19c rootupgrade.sh reads the old configuration from init.cssd to learn where the previous CRS home lived. On this host, /etc/init.d/init.cssd was simply missing — it had been stripped out during earlier deconfig attempts before I picked the task up.
First I confirmed what the upgrade actually expected as the old home:
The third major blocker: CLSRSC-348 / CLSRSC-318 — old HAS stack will not stop or start
With the bootstrap files back, rootupgrade.sh progressed to step 2 (GetOldConfig) and then stopped again:
CLSRSC-192: Unable to stop Oracle Restart
CLSRSC-348: The Oracle Restart stack failed to stop
The upgrade was trying to cleanly stop the old 12.1 HAS stack — but the old stack was not even contactable:
/u01/app/12.1.0.2/grid/bin/crsctl check has
CRS-4639: Could not contact Oracle High Availability Services
So I tried to start it, and hit the classic library-mismatch error:
/u01/app/12.1.0.2/grid/bin/crsctl start has
CRS-4652: Failure 3 in clsvswversion for the local node
Root Cause
The original 12.1 Oracle Restart stack was not in a healthy state. Previous deconfiguration attempts had removed critical bootstrap files and left HAS only partially configured.
Because Oracle only upgrades an existing, functioning stack, rootupgrade.sh could not proceed until the old stack was cleanly rebuilt and verified. The fix is to deconfigure it fully, rebuild it on 12.1, confirm it is online — and only then let the 19c upgrade take over.
Rebuild the old 12.1 HAS stack using the 12.1 perl binary (do not use the system perl — use the one shipped with the old Grid home):
/u01/app/12.1.0.2/grid/perl/bin/perl \
-I/u01/app/12.1.0.2/grid/perl/lib \
-I/u01/app/12.1.0.2/grid/crs/install \
/u01/app/12.1.0.2/grid/crs/install/roothas.pl
This reconfigured Oracle Restart successfully on 12.1 — note this is the script rebuilding the old stack, confirmed by:
CRS-4123: Oracle High Availability Services has been started.
CLSRSC-327: Successfully configured Oracle Restart for a standalone server
Verify HAS is genuinely online before going further:
/u01/app/12.1.0.2/grid/bin/crsctl check has
CRS-4638: Oracle High Availability Services is online
Clearing stale checkpoints and finally running the upgrade
The earlier failed rootupgrade.sh runs leave behind checkpoint files that block any retry — the script thinks it has already partly completed and refuses to start fresh. Clear them:
This time it walked cleanly through all 12 steps (10–20 minutes), pinned the node, backed up the OLR at both the 12.1 and 19c homes, and finished with:
CRS-4123: Oracle High Availability Services has been started.
CLSRSC-327: Successfully configured Oracle Restart for a standalone server
Back in the OUI GUI, clicking OK finalized the installer. ما شاء الله — Grid was now on 19c.
One of the most common post-GI issues: ASM was never registered
This one is worth its own warning, because it does not surface during the GI upgrade — it surfaces later, when you go to create the CDB and DBCA fails with something like DBT-06604: insufficient free space. The real reason is that ASM was not registered with Oracle Restart, so the tools silently fell back to filesystem storage, which did not have the room.
A point worth being precise about: depending on the configuration, ASM may already exist and simply not be running — in which case srvctl start asm (or mounting the diskgroups) is all that is needed. Check before you add — running srvctl add asm when ASM is already configured will error, so confirm the current state with srvctl status asm / srvctl config asm first. In this environment ASM was not registered correctly, so it had to be added back:
# verify first — does Oracle Restart already know about ASM?
srvctl status asm
srvctl config asm
# in this environment it was not registered, so:
srvctl add asm
srvctl start asm
And if the diskgroups are not mounted, mount them explicitly from the ASM instance:
ALTER DISKGROUP DATA MOUNT;
ALTER DISKGROUP RECO MOUNT;
Lesson: after any Oracle Restart GI upgrade, always confirm srvctl status asm before you trust any DBCA/DBUA storage decision.
Lessons Learned
Verify Oracle Restart is healthy before attempting an upgrade.
Confirm init.cssd and olr.loc exist and point to valid locations.
Clear stale checkpoint files before retrying rootupgrade.sh.
Validate ASM registration immediately after the upgrade.
Phase 2 — Database 12.1.0.2 → 19c via DBUA
With 19c Grid and ASM healthy, and the 19c CDB (EBSCDB) already created and patched (datapatch + catmgd.sql for the MGDSYS schema), the next phase was upgrading the non-CDB ebsdb in place from 12.1.0.2 to 19c. It stays a non-CDB for now — the PDB conversion is a deliberately separate Phase 3.
For EBS environments, launch DBUA using the -keepEvents option so that the EBS-required events and hidden (underscore) parameters are preserved during the upgrade. Dropping those silently breaks the apps tier later.
X11 forwarding across a user switch
A very common, very annoying blocker: you SSH in as one OS user, then sudo to oracle, and X11 forwarding breaks because DISPLAY and the Xauthority cookie do not carry across the user switch. DBUA is a GUI tool, so this stops you cold.
The fix is to capture the cookie as your login user and add it under oracle:
# as the login user
xauth list # note the cookie line for your display, e.g. dbhost/unix:11 MIT-MAGIC-COOKIE-1 <cookie>
xdpyinfo | head # must return display info, not "unable to open display"
Tip: do not reuse the runbook author’s DISPLAY value (their own workstation name). It will never work for you — set your own.
DBUA wizard choices that matter for EBS
Screen
Choice
CDB / PDB conversion
Leave unchecked — conversion is a separate later phase
Recovery option
“I have my own backup/restore strategy” (RMAN backup already taken) — never leave this blank
Recompile invalid objects
Enabled (parallel)
Upgrade timezone data
Enabled
compatible
Leave at 12.1.0.2 during the upgrade — preserves rollback. Raise it later, deliberately.
Listener migration
Keep the CDB listener migration checked
A couple of things that look like errors but are not:
The DBUA summary may show db_unique_name where you expect db_name. If your unique name differs from the SID, this is just DBUA displaying the unique name — verify against the live DB and move on.
The CDB listener (here on port 1523) must be up before DBUA can pass the network screen. If it is down, start it from the 19c home first: lsnrctl start <listener_name>.
Wrap the whole thing in screen (or tmux) so an SSH drop does not kill the upgrade mid-flight. If screen is not installed, at minimum use nohup-style protection — a dropped DBUA mid-upgrade is a bad day.
DBUA for an EBS 12.1.0.2 → 19c database typically runs 2–4 hours, plus 30–60 minutes of recompile/datapatch. Expect some ORA- noise in the logs (recompile warnings); watch for new error patterns, not the routine ones.
Post-DBUA housekeeping
Once the upgrade completes and all post-upgrade validation is complete (still as a non-CDB), raise COMPATIBLE to 19.0.0. Note that after COMPATIBLE is increased, rollback to the previous release is no longer possible — so this is a deliberate post-validation step, not an automatic next one:
-- raise compatibility — this is the point of no return, do it deliberately
ALTER SYSTEM SET compatible='19.0.0' SCOPE=SPFILE;
-- compatible only takes effect after a restart
SHUTDOWN IMMEDIATE;
STARTUP;
The exact value depends on your site standards and Oracle’s recommendation for the release — some shops set 19.0.0, others use the fully-qualified 19.0.0.0.0. Both forms are accepted; confirm which one your organization standardizes on before setting it.
Then:
Snapshot a pfile from the spfile.
Relocate the spfile into the 19c home’s dbs/.
Remove obsolete parameters that 19c rejects — notably sec_case_sensitive_logon and utl_file_dir.
Run the XDB migration scripts: dbmsxdbschmig.sql then prvtxdbschmig.plb.
Run utlrp.sql (twice) to recompile. In this environment the invalid count dropped from over 12,000 to under 30 across the two passes — normal for an EBS database.
At this point ebsdb is a healthy 19c non-CDB, ready to be plugged in.
Lessons Learned
Always launch DBUA with -keepEvents for EBS — it preserves the required underscore parameters and event settings.
Fix X11 forwarding across the user switch before launching; never reuse the runbook author’s DISPLAY value.
Leave compatible at 12.1.0.2 during the upgrade and raise it deliberately afterward to preserve rollback.
Treat the db_unique_name-vs-db_name display in the summary as cosmetic, and start the CDB listener before the network screen.
Wrap the run in screen/tmux so an SSH drop cannot kill the upgrade mid-flight.
Phase 3 — Non-CDB to PDB conversion
This is where ebsdb becomes a lowercase PDB inside EBSCDB. EBS ships a set of txk driver scripts for exactly this path; the lowercase, case-sensitive PDB name is a detail that bites you in every subsequent command (ALTER SESSION SET CONTAINER="ebsdb" — with the quotes).
This generates the PDB descriptor XML (ebsdb_PDBDesc.xml, the output of DBMS_PDB.DESCRIBE, carrying the <ncdb2pdb>1</ncdb2pdb> flag) and shuts the non-CDB ebsdb down permanently — once you are on this path, there is no casual restart of the old non-CDB.
This reported 8 violations — and per the runbook rules, all were ignorable: standard SQL-patch warnings and the usual benign messages, no character-set mismatch (a character-set violation is the one you must not ignore). Always read each violation; do not blanket-ignore.
Step 3 — Create the PDB (plug in)
perl $AD_TOP/patch/115/bin/txkCreatePDB.pl
This performs a NOCOPY plug-in, allowing the existing ASM datafiles to remain in place while the database is converted into a PDB (the exact ASM file layout varies by environment and Oracle version), and runs noncdb_to_pdb.sql to finish the conversion. It completed in roughly 14 minutes, the PDB state was saved, and ebsdb showed up as CON_ID 3, READ WRITE. ما شاء الله.
This is where the real fights happened. Three blockers, all around password files:
a) ASM password file pre-staging. The script needs the CDB password file in ASM. Pre-stage it with orapwd from the DB home (not the Grid home) into the ASM path for the CDB, then register it:
b) ORA-65066 on the SYSTEM password. Setting the SYSTEM password inside the new PDB threw ORA-65066 until it was applied across all containers:
ALTER USER SYSTEM IDENTIFIED BY "<StrongPwd#1>" CONTAINER=ALL;
c) OPW-00029 complexity failure.orapwd rejected the chosen SYSTEM password with OPW-00029 until it satisfied 19c’s special-character complexity rules. Use a value that genuinely meets the 19c verifier (uppercase + lowercase + digit + special character), e.g. <StrongPwd#1>.
Step 5 — Post-PDB AutoConfig and the UTL_FILE_DIR trap
Running AutoConfig on the DB tier after the conversion initially failed with a UTL_FILE_DIR error. The trace pointed at /usr/tmp being a symlink rather than a real directory, which the new UTL_FILE_DIR-replacement mechanism in 19c does not tolerate.
The fix is to drive the directory configuration explicitly with txkCfgUtlfileDir.pl. First, manually create the directory-list file with real (non-symlink) paths:
# e.g. /u01/app/oracle/product/19.0.0/dbhome_1/dbs/ebsdb_utlfiledir.txt
After that, AutoConfig on the DB tier completed cleanly.
Worth flagging for the longer term: UTL_FILE_DIR is deprecated, and Oracle’s recommended direction is to use database directory objects (CREATE DIRECTORY) for PL/SQL file I/O wherever your EBS code level supports it. The txkCfgUtlfileDir.pl mechanism above is the EBS-supported bridge for this release; treat directory objects as the strategic target.
Lessons Learned
The lowercase, case-sensitive PDB name ("ebsdb" with quotes) propagates into every subsequent ALTER SESSION SET CONTAINER — get it right once and stay consistent.
Read every compatibility violation; ignore only the benign ones, and never ignore a character-set mismatch.
Budget real time for password files: pre-stage the ASM password file from the DB home, expect ORA-65066 (apply CONTAINER=ALL) and OPW-00029 (19c complexity rules).
A symlinked /usr/tmp breaks post-PDB AutoConfig — drive UTL_FILE_DIR explicitly via txkCfgUtlfileDir.pl with real paths.
Phase 4 — Application-tier cutover (overview)
With the database tier healthy at 19c as a PDB, the apps-tier work on appshost is the final phase:
Run AutoConfig on both filesystems (fs1 and fs2) to point the apps tier at the new 19c PDB connection details.
Clean up any stale adop sessions before starting fresh — an abandoned session from days earlier will block a new cycle. Run adop cleanup (cleanup_mode=full) and then a fresh adop phase=prepare.
Validate HugePages, the env files, and Configuration Manager.
I will cover the full apps-tier cutover in detail in a follow-up post, since it deserves its own walkthrough.
Final Validation Checklist
After the upgrade and conversion were complete, the following validations were performed:
✓ Oracle Restart resources online and managed by srvctl ✓ ASM instance and diskgroups mounted ✓ CDB and PDB open in READ WRITE mode ✓ Database listener services registered correctly ✓ AutoConfig completed successfully on database and application tiers ✓ Concurrent Managers started successfully ✓ FNDSM service validated ✓ Internal Monitor running normally ✓ Forms and WebLogic services accessible ✓ adop phase=prepare completed without errors ✓ Invalid objects reviewed and reduced to an acceptable count ✓ Application smoke testing completed ✓ All EBS services registered in the listener ✓ Concurrent Manager processing validated ✓ Test concurrent request submitted and completed ✓ Forms login verified ✓ Workflow Mailer status verified ✓ OACORE, FORMS, and OAFM managed servers healthy ✓ No critical errors in AutoConfig logs ✓ adopscanlog reviewed with no blocking errors ✓ Database and ASM resources managed successfully through srvctl
Quick post-upgrade SQL validation
A handful of SQL checks confirm the database came back healthy — the component registry, the container/PDB state, and the invalid-object count. Set column formatting first so the output stays readable:
1. Component registry — every component should report VALID (or UPGRADED) at 19.0.0:
SET LINESIZE 200 PAGESIZE 100
COL comp_name FORMAT A45
COL version FORMAT A18
COL status FORMAT A12
SELECT comp_name, version, status
FROM dba_registry
ORDER BY comp_name;
2. Container and PDB state — for a 19c multitenant environment, V$PDBS is the most useful check because it shows OPEN_MODE directly (the EBS PDB should be READ WRITE):
COL name FORMAT A20
SELECT con_id, name, open_mode
FROM v$pdbs
ORDER BY con_id;
DBA_PDBS complements it by showing the persisted state — NORMAL for a healthy plugged-in PDB:
COL pdb_name FORMAT A20
COL status FORMAT A12
SELECT pdb_name, status
FROM dba_pdbs
ORDER BY pdb_name;
3. Invalid objects — review the breakdown by owner, then the overall total (an EBS database normally settles to a small count after two utlrp.sql passes):
COL owner FORMAT A22
COL object_type FORMAT A22
SELECT NVL(owner,'(none)') AS owner,
object_type,
COUNT(*) AS invalid_count
FROM dba_objects
WHERE status = 'INVALID'
GROUP BY owner, object_type
ORDER BY invalid_count DESC;
-- overall total
SELECT COUNT(*) AS total_invalid
FROM dba_objects
WHERE status = 'INVALID';
Although every environment is different, the upgrade itself proved relatively straightforward once the underlying Oracle Restart configuration issues were corrected. The majority of effort was spent validating and repairing legacy infrastructure assumptions rather than executing the upgrade utilities themselves.
Key takeaways
A GI upgrade is only as clean as the old stack underneath it. Most CLSRSC-318 / CLSRSC-324 / CLSRSC-348 failures trace back to an old HAS stack that was never properly configured or was half-deconfigured. Rebuild it cleanly on the old version first, then upgrade.
Verify the bootstrap files yourself. Do not assume the OLR is <hostname>.olr — check cdata/localhost/ and verify with ocrcheck -local.
Stale checkpoints block retries silently. Clear ROOTHAS_STACK / ROOTCRS_STACK before re-running rootupgrade.sh.
After an Oracle Restart GI upgrade, register ASM (srvctl add asm / srvctl start asm) before you trust any storage-related tool.
Use dbua -keepEvents for EBS, and fix X11 forwarding across the user switch before you launch.
The PDB conversion’s hardest part is password files — ORA-65066, OPW-00029, and ASM password-file placement. Budget time for it.
Post-PDB AutoConfig + UTL_FILE_DIR: a symlinked /usr/tmp will fail it. Drive the directory list explicitly with txkCfgUtlfileDir.pl using real paths.
Looking back, the actual upgrade steps were straightforward. The real effort was understanding and repairing the assumptions the upgrade process makes about the existing environment. Missing bootstrap files, partially deconfigured Oracle Restart components, ASM registration gaps, password-file placement, and UTL_FILE_DIR migration issues consumed far more time than the upgrade binaries themselves. Those lessons are often absent from official documentation — which is exactly why I wanted to document the journey end to end.
References
Oracle E-Business Suite Release 12.2 — Interoperability Notes for Oracle Database 19c (My Oracle Support)
Oracle Database 19c Upgrade Guide
Using UTL_FILE_DIR or Database Directories for PL/SQL File I/O in Oracle E-Business Suite (My Oracle Support)
Oracle E-Business Suite Multitenant / Pluggable Database (PDB) conversion documentation
Oracle Grid Infrastructure Installation and Upgrade Guide (Oracle Restart)
(Look up the current My Oracle Support note IDs for your exact code and database levels — they are revised regularly.)
If this write-up helped you during your own Oracle E-Business Suite upgrade, you’re welcome to follow the blog for more real-world Oracle Database, EBS, ADOP, cloning, upgrade, and troubleshooting guides drawn from production experience.
Disclaimer: This post reflects my own hands-on experience and is shared for educational purposes only. All hostnames, database names, diskgroup names, and paths have been genericized. Always test thoroughly in a non-production environment and follow Oracle’s official documentation and your organization’s change-control process before applying any of these steps. The views expressed here are my own and do not represent those of any employer or client.
In this article, we are going to demonstrate the steps to apply the Oracle April 2026 Critical Patch Update (CPU) on a Single Instance Oracle 19c Container Database (CDB). Here we will apply combo patch 39062931 which contains 39034528 (Database Apr 2026 Release Update 19.31.0.0.260421) and 38906621 (OJVM Component Release Update 19.31.0.0.260421). We also cover an important real-world scenario where a previously applied overlay patch 34672698 (ORA-00800 fix) conflicted with the Apr 2026 RU — confirming that the fix is now natively included in the RU itself.
Official References: This walkthrough is based on MOS Note CPU58 — Critical Patch Update (CPU) Apr 2026 for Oracle Database Products — and the Oracle April 2026 CPU Advisory (481 new security patches across all Oracle product families, released 21-Apr-2026).
Patch Availability Note: The Linux x86-64 DB RU 39034528 was delayed post the standard Apr 21 release. Per MOS Note CPU58, the Linux x86-64 ETA was 15-May-2026. Always check CPU58 for the latest availability before scheduling your patching window.
Downtime Notice: Applying a DB Release Update on a Single Instance (non-RAC) database requires a full database shutdown. Plan for a maintenance window accordingly and notify application teams in advance.
Terminology Note: Oracle’s quarterly security bundle is commonly referred to as the Critical Patch Update (CPU). However, Oracle now delivers database fixes as Release Updates (RU). The term CPU refers to the overall security advisory, while RU is the actual patch artifact you apply. Both terms are used interchangeably in the field — this post uses CPU to align with the official advisory title.
Environment Details
Parameter
Value
Database Type
Single Instance Oracle 19c CDB
OS
Oracle Linux x86-64
Starting Version
19.30.0.0.0 (Jan 2026 RU)
Target Version
19.31.0.0.0 (Apr 2026 RU)
Combo Patch
39062931
DB RU Patch
39034528
OJVM RU Patch
38906621
Overlay Patch (conflict)
34672698 (ORA-00800 fix — rolled back, natively included in 19.31)
OPatch Version
12.2.0.1.51
ORACLE_HOME
/u03/app/oracle/product/19.0.0.0/db_1
Section 1: Pre-Patch Checks
1.1 Verify current database version
sqlplus / as sysdba
SQL> select version_full from v$instance;
VERSION_FULL
-----------------
19.30.0.0.0
1.2 Check existing SQL patch history (dba_registry_sqlpatch)
SQL> set lines 200
SQL> col description format a50
SQL> col status format a10
SQL> col action_time format a30
SQL> select patch_id, action, status, action_time, description
2 from dba_registry_sqlpatch
3 order by action_time;
PATCH_ID
ACTION
STATUS
ACTION_TIME
DESCRIPTION
29517242
APPLY
SUCCESS
2022-10-18 02:14:37
Database Release Update 19.17
38632161
APPLY
SUCCESS
2026-01-21 04:45:06
Database Release Update 19.30.0.0.260120
38523609
APPLY
SUCCESS
2026-01-21 04:49:43
OJVM Release Update 19.30.0.0.260120
1.3 Check component registry (dba_registry)
SQL> col comp_name format a45
SQL> select comp_id, comp_name, version, status from dba_registry order by comp_id;
COMP_ID
COMP_NAME
VERSION
STATUS
APS
OLAP Analytic Workspace
19.0.0.0.0
VALID
CATALOG
Oracle Database Catalog Views
19.0.0.0.0
VALID
CATJAVA
Oracle Database Java Packages
19.0.0.0.0
VALID
CATPROC
Oracle Database Packages and Types
19.0.0.0.0
VALID
CONTEXT
Oracle Text
19.0.0.0.0
VALID
DV
Oracle Database Vault
19.0.0.0.0
VALID
JAVAVM
JServer JAVA Virtual Machine
19.0.0.0.0
VALID
OLS
Oracle Label Security
19.0.0.0.0
VALID
ORDIM
Oracle Multimedia
19.0.0.0.0
VALID
OWM
Oracle Workspace Manager
19.0.0.0.0
VALID
SDO
Spatial
19.0.0.0.0
VALID
XDB
Oracle XML Database
19.0.0.0.0
VALID
XML
Oracle XDK
19.0.0.0.0
VALID
XOQ
Oracle OLAP API
19.0.0.0.0
VALID
1.4 Check invalid objects (baseline)
SQL> select count(*) from dba_objects where status = 'INVALID';
COUNT(*)
----------
8
Note: Record the baseline invalid object count. Any new invalids introduced by patching should be investigated. After datapatch, Oracle-supplied objects marked temporarily INVALID will recompile automatically. Only non-Oracle invalids or objects still INVALID after 24 hours need attention.
Note on OCW patch 29585399: This OCW (Oracle Clusterware) patch appears in the OPatch inventory even on Single Instance (non-RAC) environments. This is expected behaviour — it is included in the base Oracle Home installation and is not a sign of misconfiguration.
Section 2: Upgrade OPatch
Before applying any patch, always upgrade OPatch to the latest version. Download the latest OPatch (p6880880) from MOS and apply.
# As oracle user
cd $ORACLE_HOME
mv OPatch OPatch_backup_$(date +%Y%m%d)
unzip /<patch_staging_dir>/p6880880_190000_Linux-x86-64.zip -d $ORACLE_HOME
# Verify new version
$ORACLE_HOME/OPatch/opatch version
# Expected: OPatch Version: 12.2.0.1.51
Section 3: Stage the Patches
# Create staging directory
mkdir -p /<patch_staging_dir>/APR_2026
# Unzip combo patch (contains both DB RU and OJVM RU)
cd /<patch_staging_dir>/APR_2026
unzip p39062931_190000_Linux-x86-64.zip
# Verify extraction
ls -la
# Should show: 38906621/ 39034528/
Section 4: Conflict Check
# Check DB RU for conflicts
cd /<patch_staging_dir>/APR_2026/39034528
$ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./
# Check OJVM RU for conflicts
cd /<patch_staging_dir>/APR_2026/38906621
$ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./
Real-World Conflict Scenario (ZOP-47): In this environment, the conflict check returned a conflict between overlay patch 34672698 and the new DB RU 39034528. This is because the ORA-00800 fix originally delivered by 34672698 is now natively included in the 19.31 RU. The resolution is to roll back the overlay patch first before applying the RU. This is a standard ZOP-47 scenario — the overlay is superseded, not incompatible.
Section 5: System Space Check
# Check available space before patching
$ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace -ph /<patch_staging_dir>/APR_2026/39034528
$ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace -ph /<patch_staging_dir>/APR_2026/38906621
# Also verify OS-level space
df -h $ORACLE_HOME
df -h /tmp
Section 6: Take Oracle Home TAR Backup
# As oracle user — use relative path approach to avoid absolute path issues
cd $(dirname $ORACLE_HOME)
nohup tar -czf /<backup_dir>/ORACLE_HOME_backup_$(date +%Y%m%d).tar.gz \
$(basename $ORACLE_HOME) > /tmp/oh_tar_backup.log 2>&1 &
echo "Backup PID: $!"
tail -f /tmp/oh_tar_backup.log
Note: Using $(dirname $ORACLE_HOME) and $(basename $ORACLE_HOME) avoids embedding absolute paths in the tar archive, making restoration cleaner. The nohup & pattern allows the backup to run in background without being killed if the session disconnects.
Section 7: RMAN Backup (Verify)
# Verify a recent RMAN backup exists before proceeding
rman target /
RMAN> list backup summary;
RMAN> exit;
Important: Never apply a patch without confirming a valid RMAN backup. If the latest backup is stale, take a fresh full backup before proceeding. The Oracle Home TAR covers binaries — RMAN covers your data.
Because patch 34672698 conflicts with the Apr 2026 RU (its fix is now natively included), we must roll it back first.
export PATH=$ORACLE_HOME/OPatch:$PATH
cd $ORACLE_HOME/OPatch
opatch rollback -id 34672698
# Verify rollback
opatch lspatches
# 34672698 should no longer appear
Section 10: Apply DB RU 39034528
cd /<patch_staging_dir>/APR_2026/39034528
# Apply (interactive)
$ORACLE_HOME/OPatch/opatch apply
# OR apply silently (for automation/scripts)
$ORACLE_HOME/OPatch/opatch apply -silent
Note on oradism permissions: In some environments the DB RU apply (opatch apply) itself automatically changes oradism ownership from root:oinstall to oracle:oinstall with permissions 755. Always check after applying the DB RU before attempting any manual root steps:
ls -la $ORACLE_HOME/bin/oradism
If oradism is already showing oracle:755 post-apply, the manual chown/chmod steps are not required. Only apply root permission changes if oradism remains root-owned after the patch apply.
Section 11: Apply OJVM RU 38906621
cd /<patch_staging_dir>/APR_2026/38906621
$ORACLE_HOME/OPatch/opatch apply
11.1 Verify OPatch inventory after apply
$ORACLE_HOME/OPatch/opatch lspatches
Patch ID
Description
38906621
OJVM RELEASE UPDATE: 19.31.0.0.260421
39034528
Database Release Update: 19.31.0.0.260421
29585399
OCW RELEASE UPDATE 19.3.0.0.0
Note: Patch 34672698 no longer appears — successfully rolled back. Patch 34672698’s fix is now natively bundled into the 19.31 RU.
Section 12: Start Database and Listener
sqlplus / as sysdba
SQL> startup;
SQL> alter pluggable database all open;
SQL> show pdbs;
SQL> exit;
lsnrctl start LISTENER
lsnrctl status LISTENER
Section 13: Run Datapatch
Datapatch applies the SQL-level changes for the new patches into the CDB and all open PDBs. This step is mandatory — OPatch alone does not apply SQL changes.
cd $ORACLE_HOME/OPatch
# Run datapatch and capture output to log
./datapatch -verbose | tee /tmp/datapatch_19_31.log
Note on tee and exit codes: Using tee in a pipeline means the shell reports the exit code of tee, not datapatch. If you need to capture datapatch’s actual exit code for scripted validation, use: ./datapatch -verbose > /tmp/datapatch_19_31.log 2>&1; echo "Exit code: $?"
Section 14: Post-Patch Validation
14.1 Verify dba_registry_sqlpatch (post-patch)
SQL> select patch_id, action, status, action_time, description
2 from dba_registry_sqlpatch
3 order by action_time;
PATCH_ID
ACTION
STATUS
ACTION_TIME
DESCRIPTION
34672698
ROLLBACK
SUCCESS
17-MAY-26 09.12.43 AM
ORA-00800 fix (overlay)
38523609
ROLLBACK
SUCCESS
17-MAY-26 09.15.02 AM
OJVM Release Update 19.30
39034528
APPLY
SUCCESS
17-MAY-26 09.21.18 AM
Database Release Update 19.31.0.0.260421
38906621
APPLY
SUCCESS
17-MAY-26 09.25.47 AM
OJVM Release Update 19.31.0.0.260421
14.2 Verify dba_registry (post-patch)
SQL> select comp_id, comp_name, version, status from dba_registry order by comp_id;
COMP_ID
COMP_NAME
VERSION
STATUS
APS
OLAP Analytic Workspace
19.0.0.0.0
VALID
CATALOG
Oracle Database Catalog Views
19.0.0.0.0
VALID
CATJAVA
Oracle Database Java Packages
19.0.0.0.0
VALID
CATPROC
Oracle Database Packages and Types
19.0.0.0.0
VALID
CONTEXT
Oracle Text
19.0.0.0.0
VALID
DV
Oracle Database Vault
19.0.0.0.0
VALID
JAVAVM
JServer JAVA Virtual Machine
19.0.0.0.0
VALID
OLS
Oracle Label Security
19.0.0.0.0
VALID
ORDIM
Oracle Multimedia
19.0.0.0.0
VALID
OWM
Oracle Workspace Manager
19.0.0.0.0
VALID
SDO
Spatial
19.0.0.0.0
VALID
XDB
Oracle XML Database
19.0.0.0.0
VALID
XML
Oracle XDK
19.0.0.0.0
VALID
XOQ
Oracle OLAP API
19.0.0.0.0
VALID
14.3 Verify version
SQL> select version_full from v$instance;
VERSION_FULL
-----------------
19.31.0.0.0
14.4 Check invalid objects (post-patch)
SQL> select count(*) from dba_objects where status = 'INVALID';
COUNT(*)
----------
8
Invalid count unchanged from baseline — no new invalids introduced by patching.
14.5 Run utlrp.sql (conditional)
Note: Running utlrp.sql is conditional — only required if you see new invalid objects that were not in the baseline count, or if Oracle-supplied objects remain INVALID after 24 hours. It is not mandatory after every patch apply.
-- Only run if new invalids are found
sqlplus / as sysdba
SQL> @?/rdbms/admin/utlrp.sql
14.6 CDB-aware validation (cdb_registry_sqlpatch)
-- Run from CDB$ROOT to validate across all containers
SQL> col con_id format 99
SQL> col patch_id format 9999999999
SQL> col status format a10
SQL> col action format a10
SQL> select con_id, patch_id, action, status, description
2 from cdb_registry_sqlpatch
3 order by con_id, action_time;
Note:cdb_registry_sqlpatch shows the patch apply status for each container (CDB$ROOT and all PDBs). Use this view to confirm datapatch applied successfully in all PDBs, not just the root container. All rows should show SUCCESS.
Section 15: Rollback Procedure (If Required)
If the patch causes critical issues post-apply, roll back in reverse order: OJVM first, then DB RU. This requires full database downtime.
opatch lspatches
-- Expect to see 38632161 and 38523609 back as active
15.5 Start database, open PDBs, run datapatch
sqlplus / as sysdba
SQL> startup;
SQL> alter pluggable database all open;
SQL> show pdbs;
SQL> exit;
cd $ORACLE_HOME/OPatch
./datapatch -verbose
Important: If the Oracle Home is severely corrupted, restore from the Oracle Home TAR backup taken in Section 6, then run datapatch. If database files are also affected, restore and recover from the RMAN backup first, then run datapatch. Never skip datapatch after a rollback — the SQL layer must be consistent with the binary layer.
Section 16: Common Failures and Troubleshooting
ZOP-47: Conflict with existing overlay patch
Symptom:opatch prereq CheckConflictAgainstOHWithDetail reports a conflict between an installed overlay patch and the new RU.
Cause: The overlay patch fix has been natively absorbed into the new RU.
Resolution: Roll back the overlay patch using opatch rollback -id <patch_id>, then proceed with the RU apply. Confirm with Oracle Support SR if unsure whether the conflict is a ZOP-47 (self-resolving) or a genuine incompatibility.
ZOP-40: Patch already installed
Symptom: OPatch reports the patch is already present in the inventory.
Cause: Patch was applied in a previous cycle or the inventory is stale.
Resolution: Verify with opatch lspatches. If genuinely already installed, no action needed. If inventory is stale, run opatch lsinventory -detail to diagnose.
Datapatch skipped PDB
Symptom: Datapatch log shows one or more PDBs were skipped.
Cause: PDB was not open during datapatch execution.
Resolution: Open the skipped PDB (alter pluggable database <pdb_name> open;) and re-run datapatch. Confirm via cdb_registry_sqlpatch that all containers show SUCCESS.
OPatch heap space error
Symptom: OPatch fails with a Java heap space error during apply.
Cause: Default JVM heap is insufficient for large patch sets.
Resolution: Set JAVA_TOOL_OPTIONS="-Xmx512m" before running opatch. This is environment-specific and not universally required — only apply if the error is observed.
OPatch inventory lock
Symptom: OPatch fails reporting the inventory is locked by another process.
Cause: A previous OPatch run did not exit cleanly, leaving a lock file.
Resolution: Check for and remove the lock file: $ORACLE_HOME/.patch_storage/<lock_file>. Confirm no other OPatch process is running before removing.
Key MOS References
Document
Description
Doc ID CPU58
Critical Patch Update (CPU) Apr 2026 for Oracle Database Products — patch availability, known issues, delayed RU tracking
Doc ID KB106822
Primary Note for Database Quarterly Release Updates — recommended reading before any RU apply
Doc ID KB869205
Oracle Database 19c Apr 2026 RU Known Issues
Doc ID KB137197
OJVM Conditional Rolling Install Details
Doc ID 244241.1
OPatch Support for RAC Rolling Patches
Doc ID 293369.1
OPatch Documentation List
Disclaimer: All server names, hostnames, database names, and environment-specific paths in this post have been anonymized. Steps and outputs are based on real production experience adapted for general use. Always test in a non-production environment before applying patches to production systems.
If you found this useful, connect with me on LinkedIn or explore more Oracle DBA scripts on my GitHub. More patching walkthroughs at syedanwarahmedoracle.blog.
Share Your Experience
Have you applied Oracle 19c CPU patches and hit a similar overlay patch conflict? Or found that a fix you applied months ago is now natively bundled into the next RU? Your real-world experiences help fellow DBAs tackle the same challenges. Drop a comment below — questions, observations, and feedback are always welcome.
It started with an RMAN failure at 22:30. The backup script for DBPRO02 fired on schedule and died within 2 minutes. The RMAN log told the story clearly:
RMAN-03009: failure of backup command on c4 channel at 22:32:13
ORA-19502: write error on file "/dbbackup/DBPRC02/rman/DiffInc_DBPRC02_4u4mi8bf"
ORA-27072: File I/O error
Additional information: 4
Three more channels followed — c1, c2, c3 — all crashing at exactly 22:32:48. When multiple channels fail simultaneously at the same timestamp, it almost always means one thing: the destination filesystem just hit 100%.
What we did not know yet — digging into backup history would reveal that DBPRO01 had already failed at 12:30 that same day for the same reason, 10 hours earlier. Two databases unprotected on the same night.
2. The Investigation — Folder by Folder
The first step was understanding what was consuming the disk. One command gave us the top-level picture:
744G inside DBPRC01 alone — 73% of the entire disk. That was our primary suspect.
Drilling into DBPRC01
$ du -sh /dbbackup/DBPRC01/rman/* | sort -rh | head -10
7.5G DiffInc_DBPRC01_fl4lumij
7.5G DiffInc_DBPRC01_eg4lrnh5
7.5G DiffInc_DBPRC01_b44lc7uh
...
Every single file was a DiffInc_ or ArchivelogAll_ backup piece. No variety. No cleanup. Just backup after backup piling up.
$ ls /dbbackup/DBPRC01/rman/ | wc -l
1522
1,522 backup pieces. We checked the oldest and newest:
Oldest file on disk: 2022-05-07
Newest file on disk: 2026-04-25
Four years of backup files on disk — or so we thought.
3. The RMAN Investigation — Where Things Got Interesting
We connected RMAN to the database and ran the retention check:
RMAN> SHOW RETENTION POLICY;
CONFIGURE RETENTION POLICY TO REDUNDANCY 30;
REDUNDANCY 30. This tells RMAN to keep the last 30 complete backup copies of every datafile before considering anything obsolete.
Next logical step — check what RMAN considers obsolete:
RMAN> REPORT OBSOLETE;
no obsolete backups found
Nothing? With 1,522 files on disk?
We ran CROSSCHECK BACKUP — all 1,693 objects came back AVAILABLE. Then we checked the actual date range RMAN was tracking from the database control file:
SELECT TO_CHAR(MIN(completion_time),'DD-MON-YYYY') oldest,
TO_CHAR(MAX(completion_time),'DD-MON-YYYY') newest,
COUNT(*) total_pieces
FROM v$backup_piece_details
WHERE status = 'A';
OLDEST NEWEST TOTAL_PIECES
03-DEC-2025 25-APR-2026 1541
The control file only tracks pieces from December 2025 onwards — about 5 months. The 2022/2023 files seen on disk were old directories and scripts, not backup pieces. All 1,541 current pieces were legitimate and RMAN considered every one of them necessary under REDUNDANCY 30.
This was the key insight: RMAN was not broken. The retention policy itself was the problem.
4. Root Cause — The Architecture Trap
The deeper investigation revealed something unexpected. Looking at the actual RMAN backup script:
connect target rman/password@DBPRO01
...
format '/dbbackup/DBPRC01/rman/DiffInc_%d_%u'
(database);
...
delete obsolete;
DBPRO01 (the production database) was backing up INTO the DBPRC01 directory. The directory names suggested one database but contained another database’s backups entirely. The naming convention was PRO to PRC — production database backups stored in the production-copy directory.
This pattern existed for all three database pairs on the server. Each production database backed up into its corresponding copy directory.
The delete obsolete command was in the script — but with REDUNDANCY 30 and weekly Level 0 backups, obsolete only kicks in after 30 complete Level 0 cycles. That is 30 weeks = 7.5 months of retention. Since the current tracking window was only 5 months, delete obsolete ran every night and found absolutely nothing to delete.
The math:
Retention policy
REDUNDANCY 30
Level 0 frequency
Weekly (Sundays)
Effective retention period
~30 weeks / 7.5 months
Backup tracking since
December 2025 (~5 months)
Result
delete obsolete finds nothing — ever
Daily backup size
~7–7.5G per run
Total accumulated
744G
Adding fuel to the fire — the patching activity on April 18 triggered an extra Level 0 backup, followed by the regular Sunday Level 0 on April 19. Two large Level 0 runs (~27G each) within 24 hours wrote the final ~54G that pushed the disk over the edge.
5. Secondary Findings During Investigation
OCT2025 CPU Patch Artifacts (7.7G)
The October 2025 CPU patch files (zip archives + extracted directories) were still sitting in /dbbackup/OCT2025_CPU/. A quick OPatch check confirmed the database had since been patched to 19.30 (January 2026 RU) — the October 2025 patches were fully superseded and rolled back from inventory. Safe to delete immediately.
$ $ORACLE_HOME/OPatch/opatch lsinventory | grep -E "38291812|38194382"
# Empty — neither Oct 2025 patch in inventory anymore
5-Year-Old Pre-Migration Export Dumps
Three directories contained Oracle 11.2.0.4 export dumps from January–March 2021 — taken before the migration to 19c. With the database now running 19.30, these had zero recovery value but occupied ~14G collectively. Flagged for manager approval before deletion.
Recovery Catalog Version Mismatch
The original RMAN log flagged this warning:
PL/SQL package RMAN.DBMS_RCVCAT version 19.11.00.00 in RCVCAT database is not current
PL/SQL package RMAN.DBMS_RCVMAN version 19.11.00.00 in RCVCAT database is not current
The recovery catalog is running 19.11 packages while the RMAN client is now 19.30. Non-critical tonight but requires UPGRADE CATALOG in the next maintenance window.
6. The Fix — Emergency Space Recovery
With management approval obtained, we executed a time-based delete — keeping the last 30 days of backups and removing everything older:
RMAN> DELETE NOPROMPT BACKUP COMPLETED BEFORE 'SYSDATE-30';
This command does three things atomically:
Queries catalog/controlfile for all pieces completed before the cutoff date
Deletes the physical files from disk
Removes the records from RMAN catalog — no orphaned entries, no catalog drift
Both failed backups were re-submitted immediately and ran successfully in parallel:
$ nohup sh /opt/oracle/scripts/rman/rman_backup_DBPRO01.sh &
$ nohup sh /opt/oracle/scripts/rman/rman_backup_DBPRO02.sh &
$ jobs -l
[1] Running nohup sh ...rman_backup_DBPRO01.sh &
[2] Running nohup sh ...rman_backup_DBPRO02.sh &
DBPRO02 Level 0 backup fails — same errors, all 4 channels
23:08
Investigation begins — df -hP /dbbackup confirms 100% full
23:15
DBPRC01 directory identified as 744G consumer
23:25
RMAN connected — REDUNDANCY 30 discovered
23:35
Architecture confirmed — PRO databases backing up into PRC directories
23:45
Root cause confirmed — 7.5-month retention, delete obsolete finds nothing
23:50
DELETE BACKUP COMPLETED BEFORE SYSDATE-30 executed
23:51
1,072 pieces deleted — disk drops to 53%
23:55
OCT2025_CPU removed — disk at 52%, 495G free
00:00
Both backup jobs re-submitted and running successfully
8. Permanent Fix Recommendations
Fix 1 — Change Retention Policy to RECOVERY WINDOW
RMAN> CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 14 DAYS;
REDUNDANCY 30 with weekly Level 0s means 7.5 months of retention — far beyond what any production SLA requires. A 14-day recovery window keeps 2 weeks of backups regardless of backup frequency, and delete obsolete will actually find and remove old pieces going forward.
Fix 2 — Add Pre-Backup Space Check to Cron Script
#!/bin/bash
BACKUP_FS="/dbbackup"
THRESHOLD=20
AVAIL_PCT=$(df -hP $BACKUP_FS | awk 'NR==2 {gsub(/%/,""); print 100-$5}')
if [ "$AVAIL_PCT" -lt "$THRESHOLD" ]; then
echo "ABORT: $BACKUP_FS is ${AVAIL_PCT}% free — below ${THRESHOLD}% threshold" \
| mailx -s "BACKUP ABORTED: Low space on $BACKUP_FS" $MAILTO
exit 1
fi
A failing backup that writes 3G before dying is worse than a backup that never starts — it wastes the last 3G of free space and leaves partial pieces on disk.
Fix 3 — Upgrade the Recovery Catalog
RMAN> CONNECT TARGET /
RMAN> CONNECT CATALOG rman/password@rmancat
RMAN> UPGRADE CATALOG;
RMAN> UPGRADE CATALOG; -- run twice as prompted
The catalog is 2 major patch levels behind the RMAN client. Some catalog-dependent operations will start failing if left unaddressed.
Fix 4 — Filesystem Monitoring Alert
The FRA check scripts already email on FRA usage above 80%. The same pattern should exist for /dbbackup. A simple cron entry checking disk usage every hour with alert at 80% would have caught this days before the disk hit 100%.
9. Key Takeaways for Oracle DBAs
REDUNDANCY N is not always safer than RECOVERY WINDOW. REDUNDANCY 30 with weekly Level 0 backups means 7.5 months of retention — likely far beyond your RPO requirement and a silent space accumulator.
Always verify what delete obsolete actually deletes. If it finds nothing to delete every single night, that is a warning sign — not reassurance.
Check backup naming conventions carefully. When a directory named DBPRC01 contains DBPRO01 backups, retention policies applied to the wrong database RMAN configuration control the cleanup behavior.
Patching days generate oversized backups. A Level 0 taken manually on patch day plus the regular Sunday Level 0 the next day equals 2x the normal space consumption in 24 hours. Ensure extra headroom exists going into patch windows.
Use DELETE BACKUP COMPLETED BEFORE SYSDATE-N for emergency cleanup — not OS-level rm. RMAN deletes atomically update both the physical files and the catalog, preventing expired/orphaned piece confusion later.
Never use rm on RMAN backup pieces directly unless you follow up with CROSSCHECK BACKUP and DELETE EXPIRED BACKUP to sync the catalog.
10. Commands Reference — Quick Cheat Sheet
-- Check retention policy
RMAN> SHOW RETENTION POLICY;
-- Preview what would be deleted (dry run)
RMAN> REPORT OBSOLETE;
RMAN> LIST BACKUP COMPLETED BEFORE 'SYSDATE-30';
-- Emergency cleanup — delete pieces older than 30 days
RMAN> DELETE NOPROMPT BACKUP COMPLETED BEFORE 'SYSDATE-30';
-- Standard cleanup based on retention policy
RMAN> DELETE NOPROMPT OBSOLETE;
-- Sync catalog after any OS-level file operations
RMAN> CROSSCHECK BACKUP;
RMAN> DELETE NOPROMPT EXPIRED BACKUP;
-- Change to time-based retention (recommended)
RMAN> CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 14 DAYS;
-- Check backup piece date range in control file
SELECT TO_CHAR(MIN(completion_time),'DD-MON-YYYY') oldest,
TO_CHAR(MAX(completion_time),'DD-MON-YYYY') newest,
COUNT(*) total_pieces
FROM v$backup_piece_details
WHERE status = 'A';
-- Check backup history
SELECT session_key, input_type, status,
TO_CHAR(start_time,'YYYY-MM-DD HH24:MI:SS') start_time,
output_bytes_display, time_taken_display
FROM v$rman_backup_job_details
ORDER BY start_time DESC;
Conclusion
What appeared to be a simple disk full incident turned out to involve a multi-database backup architecture, a misconfigured retention policy, and a cleanup mechanism that was technically running correctly but never finding anything to clean. The fix itself — one RMAN command — took under 5 minutes. The real work was the systematic investigation to understand exactly what was safe to delete and why.
That is Oracle DBA work in a nutshell: the fix is often simple; understanding why it is safe to run is the real job.
If you found this useful, connect with me on LinkedIn or explore more Oracle DBA scripts on my GitHub. More incident walkthroughs at syedanwarahmedoracle.blog.
A critical alert was triggered in production showing UNDO tablespace usage at 97% with near-zero free space. At first glance, this looked like an immediate outage risk. However, deeper analysis revealed a completely different story.
Initial Observations
Standard checks showed Tablespace UNDOTBS1 with a total size of 16 GB and only ~2 MB free space. This strongly suggested the UNDO tablespace was nearly full. However, deeper analysis revealed a different story.
The Turning Point
Instead of relying on DBA_FREE_SPACE, we analyzed UNDO internals using extent status:
SELECT status,
ROUND(SUM(bytes)/1024/1024,2) MB
FROM dba_undo_extents
GROUP BY status;
EXPIRED UNDO extents are fully reusable by Oracle. This means ~55% of the tablespace was immediately reusable and no actual space pressure existed.
Real Usage Calculation
SELECT ROUND(
SUM(CASE WHEN status IN ('ACTIVE','UNEXPIRED') THEN bytes ELSE 0 END)
/ SUM(bytes) * 100, 2) actual_used_pct
FROM dba_undo_extents;
Actual usage was only ~45% — not 97%.
Root Cause of the False Alert
The monitoring system was using DBA_FREE_SPACE which does not include reusable EXPIRED extents. This is incorrect for UNDO tablespaces and leads to false critical alerts, unnecessary escalations, and wasted DBA effort.
The Correct Monitoring Approach
SELECT tablespace_name,
ROUND(SUM(CASE WHEN status IN ('ACTIVE','UNEXPIRED') THEN bytes ELSE 0 END)
/ SUM(bytes) * 100, 2) actual_used_pct
FROM dba_undo_extents
GROUP BY tablespace_name;
A production alert indicating UNDO tablespace usage at ~99% is typically treated as a critical issue. However, in Oracle Multitenant environments with local UNDO enabled, this metric can be misleading when evaluated using traditional tablespace monitoring techniques.
This article presents a real-world scenario involving the PDB-level UNDO tablespace APPS_UNDOTS1, where utilization reached ~99%, yet no actual resource contention existed.
This issue occurred in a Multitenant environment with Local UNDO enabled:
UNDO tablespace: APPS_UNDOTS1
Scope: PDB-level
Each PDB maintains its own UNDO
UNDO behavior must always be analyzed at the PDB level, not just at the CDB level.
Investigation Approach
Rather than relying on % used, a state-based analysis was performed.
Step 1: Active Transactions
SELECT COUNT(*) FROM v$transaction;
Observation:
Minimal active transactions
No ongoing workload pressure
Step 2: UNDO Extent Analysis
SELECT status, SUM(bytes)/1024/1024 MB FROM dba_undo_extents GROUP BY status;
Key Observation
STATUS MB PCT ----------- -------- ------- ACTIVE ~3 MB ~0.01% UNEXPIRED ~51 MB ~0.18% EXPIRED ~28 GB ~99.81%
💡 Critical Insight
~99.81% of UNDO (APPS_UNDOTS1) was EXPIRED and fully reusable
This means:
No real space pressure
No transaction risk
Tablespace is effectively available
Understanding UNDO Behavior
UNDO extents follow a lifecycle:
ACTIVE → UNEXPIRED → EXPIRED → REUSED
State
Description
Reusable
ACTIVE
Used by active transactions
❌
UNEXPIRED
Retained for consistency
⚠️
EXPIRED
No longer needed
✅
UNDO extents are not physically freed but logically reused by Oracle based on demand.
Why the Alert Was Misleading
The alert was based on:
SELECT * FROM dba_free_space;
Limitation
Shows only physically free space
Does not reflect reusable UNDO
Ignores Oracle’s internal reuse mechanism
Root Cause
A long-running concurrent request:
Ran for ~30 hours
Performed heavy DELETE operations
Generated significant UNDO
During execution:
UNDO reached ~99% → real pressure
After completion:
UNDO became EXPIRED
Space became reusable
Alert persisted → false positive
When UNDO Is Actually a Problem
ACTIVE > 20–30% UNEXPIRED very high EXPIRED very low
UNDO becomes a real issue only when both EXPIRED and UNEXPIRED extents are exhausted and no reusable space remains.
Recommended Monitoring Approach
WITH undo AS ( SELECT status, SUM(bytes)/1024/1024 mb FROM dba_undo_extents GROUP BY status ), total AS ( SELECT SUM(mb) total_mb FROM undo ) SELECT u.status, ROUND(u.mb,2) AS mb, ROUND((u.mb / t.total_mb) * 100, 2) AS pct FROM undo u, total t;
A tablespace showing 99% utilization can still be completely healthy if most of its extents are reusable.
Level Insight
“UNDO is not a storage problem — it is a lifecycle problem. The difference between false alarms and accurate diagnosis lies in understanding how Oracle transitions undo extents.”
During an Oracle E-Business Suite ADOP patching cycle in a multi-node environment, the apply phase failed on one node while completing successfully on others. Despite retries — including downtime mode — the issue persisted, pointing to a deeper inconsistency within the patching framework.
Symptoms Observed
ADOP session status: FAILED
Patch applied successfully on some nodes, failed on admin node
Repeated failures even with restart=no, abandon=yes, and downtime mode
No immediate actionable error from standard logs
Timeline of Events
T0 -- Patch execution initiated (ADOP apply phase)
T1 -- Failure observed on admin node
T2 -- Retry using downtime mode -- Failure persists
T3 -- ADOP session review shows inconsistent state
T4 -- Internal metadata tables analyzed
T5 -- Cleanup performed (tables + restart directory)
T6 -- Patch re-executed -- Success across all nodes
Investigation
Step 1: Check ADOP Session State
Query the ADOP session status to understand the current state across all nodes:
-- Check current ADOP session status
SELECT session_id, node_name, phase, status,
start_date, end_date
FROM applsys.ad_adop_sessions
ORDER BY start_date DESC;
-- Check apply phase status per node
SELECT s.session_id, n.node_name, p.phase_code,
p.status, p.start_date, p.end_date
FROM applsys.ad_adop_sessions s,
applsys.ad_adop_session_phases p,
applsys.fnd_nodes n
WHERE s.session_id = p.session_id
AND p.node_id = n.node_id
ORDER BY p.start_date DESC;
The existing session showed status FAILED with the apply phase partially completed — a clear indicator of inconsistent execution state across nodes.
Step 2: Check adalldefaults.txt
Reviewed the defaults file for any relevant configuration:
Modifying and retrying with this parameter had no impact, confirming the issue was not translation-related.
Step 3: Check Install Processes Table
-- Check for stale entries in FND_INSTALL_PROCESSES
SELECT COUNT(*) FROM applsys.fnd_install_processes;
-- View stale entries in detail
SELECT process_status, process_name, last_update_date
FROM applsys.fnd_install_processes
ORDER BY last_update_date DESC;
-- Check AD_DEFERRED_JOBS
SELECT COUNT(*) FROM applsys.ad_deferred_jobs;
SELECT * FROM applsys.ad_deferred_jobs;
Observation:FND_INSTALL_PROCESSES contained stale entries from the failed session. AD_DEFERRED_JOBS was empty.
Root Cause
The failure was caused by stale and inconsistent ADOP metadata tables — specifically APPLSYS.FND_INSTALL_PROCESSES and APPLSYS.AD_DEFERRED_JOBS. ADOP internally relies on these tables to track patch progress checkpoints, deferred job execution, and restart state management. When these tables retain entries from failed or incomplete sessions, ADOP assumes an incorrect execution state, leading to patch reconciliation failure, apply phase breakdown, and node-level inconsistencies.
Resolution Steps
Step 1: Backup Critical Tables
-- Always backup before any cleanup
CREATE TABLE applsys.fnd_install_processes_bak AS
SELECT * FROM applsys.fnd_install_processes;
CREATE TABLE applsys.ad_deferred_jobs_bak AS
SELECT * FROM applsys.ad_deferred_jobs;
-- Verify backups
SELECT COUNT(*) FROM applsys.fnd_install_processes_bak;
SELECT COUNT(*) FROM applsys.ad_deferred_jobs_bak;
Step 2: Drop Stale Metadata Tables
Dropping these tables forces ADOP to rebuild clean metadata during the next run:
DROP TABLE applsys.fnd_install_processes;
DROP TABLE applsys.ad_deferred_jobs;
Step 3: Reset the Restart Directory
The restart directory can silently preserve failure states. Back it up and create a fresh one:
The patch completed successfully across all nodes after the metadata cleanup.
Before vs After
Component
Before Fix
After Fix
ADOP Session
Failed
Successful
Node Consistency
Partial
Full
Restart Behavior
Stuck
Clean
Patch Execution
Incomplete
Completed
Key Takeaways
ADOP is state-driven — even when logs appear clean, internal metadata drives execution decisions
Partial success is a clue — if some nodes succeed and one fails, focus on local metadata, not the patch itself
The restart directory matters — it can silently preserve failure states and must be validated before retrying
Downtime mode is not a fix-all — even in downtime, ADOP still reads metadata tables; corruption persists unless cleaned
Always backup before cleanup — never drop tables without creating a backup first
When NOT to Use This Approach
Avoid applying this fix if the issue is caused by missing database patches (ETCC warnings), file system or permission issues, incorrect patch sequencing, or environment misconfiguration. Always validate the root cause before performing any metadata cleanup.
This scenario highlights a subtle but critical behavior in ADOP — sometimes patch failures are not caused by the patch itself, but by what the system remembers about past attempts. By resetting stale metadata, we allow ADOP to re-evaluate the environment cleanly, leading to successful execution.
In Oracle E-Business Suite (EBS) environments, performance issues are often attributed to high workload or system resource constraints. However, some of the most critical slowdowns originate from less obvious sources — inactive sessions holding uncommitted transactions. This post walks through a real-world production incident where an inactive Oracle Forms session caused cascading blocking across multiple users due to TX row-level locks.
Observed Symptoms
Oracle Forms screens becoming unresponsive in Order Management and Shipping modules
Analysis of v$session, v$transaction, and dba_wait_chains revealed a single inactive Oracle Forms session (frmweb) holding an active transaction with multiple downstream sessions waiting on TX row lock contention.
-- Identify blocking sessions
SELECT blocking_session, sid, wait_class, event
FROM v$session
WHERE blocking_session IS NOT NULL;
-- Detect inactive sessions with active transactions
SELECT s.sid, s.serial#, s.status, s.program,
s.username, t.start_time,
ROUND(s.last_call_et/3600,2) hrs_inactive
FROM v$session s, v$transaction t
WHERE s.saddr = t.ses_addr
AND s.status = 'INACTIVE'
ORDER BY hrs_inactive DESC;
-- Analyze full wait chain
SELECT * FROM dba_wait_chains;
The root blocking session showed STATUS = INACTIVE and EVENT = 'SQL*Net message from client' but had an active transaction in v$transaction — confirming it was idle at the application level but actively holding locks at the database level.
Root Cause
An Oracle Forms session executed a SELECT ... FOR UPDATE NOWAIT on WSH_DELIVERY_DETAILS, then became idle without committing or rolling back. This held exclusive row locks that blocked other sessions attempting to access the same rows, creating a cascading blocking chain.
-- The problematic SQL pattern
SELECT *
FROM WSH_DELIVERY_DETAILS
WHERE ROWID = :B1
FOR UPDATE NOWAIT;
Resolution
The root blocking session was identified, verified to have no active business transactions, approvals were obtained, and the session was terminated:
-- Kill blocking session (only after full validation and approval)
ALTER SYSTEM KILL SESSION 'SID,SERIAL#' IMMEDIATE;
Locks were released immediately, the blocking chain resolved, and application responsiveness was restored.
Preventive Measures
Implement idle session timeout policies
Educate users on proper transaction handling in Oracle Forms
Review custom code using FOR UPDATE — keep transactions short and commit promptly
Monitor long-running and idle transactions proactively
Key Takeaways
An inactive session can still hold active transactions and critical locks
Always identify the root blocker — intermediate sessions are symptoms, not the cause
Application-level inactivity does not mean database-level inactivity
In Oracle EBS, the most disruptive issues are often caused by inactive sessions holding uncommitted transactions
Written by Syed Anwar Ahmed — Oracle Apps DBA with 11 years of production experience. Connect: sdanwarahmed@gmail.com | LinkedIn
In a production Oracle database environment, a sudden spike in session count exceeding 1000+ sessions triggered alerts and concern. Interestingly, the system recovered automatically without any database-level intervention. At first glance this appeared to be a database issue — but deeper analysis revealed a different story.
The Incident
An automated alert reported session count exceeding threshold (1000+), with the majority in INACTIVE state from middleware connection pool accounts. Despite the spike there were no blocking sessions, no performance degradation, and no database errors.
-- Quick session count check
SELECT COUNT(*) FROM gv$session;
-- Result: 216 (already returning to normal)
-- Session breakdown by status
SELECT status, COUNT(*) cnt
FROM gv$session
GROUP BY status
ORDER BY cnt DESC;
Root Cause
Multiple production mid-tier servers simultaneously created new connection pools at the same time window. New pools created new database sessions while existing pools kept their sessions alive (INACTIVE) pending graceful termination — resulting in a temporary overlap:
Old Sessions (Inactive) + New Sessions (Active) = Session Surge
As older pools were cleaned up, inactive sessions terminated automatically and the count returned to baseline. This was not a database problem — it was connection pool lifecycle behavior in the mid-tier layer.
Recommendations
Stagger connection pool refresh across mid-tier servers to avoid simultaneous spikes
Monitor inactive session trends to detect abnormal accumulation early
Configure appropriate idle timeout, maximum pool size, and session reuse settings
Key Takeaways
Not all session spikes are database problems — check middleware behavior first
High session count does not necessarily indicate database stress
Transient issues still require analysis as they reveal architectural inefficiencies
Database alerts can originate from upstream connection management behavior
Written by Syed Anwar Ahmed — Oracle Apps DBA with 11 years of production experience. Connect: sdanwarahmed@gmail.com | LinkedIn
Production alert logs often contain messages that appear critical but are, in reality, indicators of normal database behavior under load. This article presents a real-world Oracle database investigation where repeated ORA-00031: session marked for kill messages and redo log allocation waits were observed. Using read-only analysis techniques, we demonstrate how to distinguish between expected behavior and actionable signals without performing any intrusive changes.
Observed Symptoms
ORA-00031: session marked for kill
Thread 1 cannot allocate new log
Private strand flush not complete
Phase 1: Interpreting ORA-00031 Correctly
ORA-00031 is generated when sessions are terminated using ALTER SYSTEM KILL SESSION. Oracle marks the session for cleanup and handles it asynchronously via background processes. This is not an error — it is a confirmation of successful session termination.
Phase 2: Identifying the True Performance Signal
The more critical messages were Thread 1 cannot allocate new log and Private strand flush not complete. These occur when LGWR attempts a redo log switch but active redo strands are still flushing. Oracle briefly delays the log switch until consistency is ensured — this is a redo allocation wait, typically seen under sustained transactional load.
Phase 3: Evidence-Based Analysis (Read-Only)
Redo switch frequency was analyzed to validate system behavior:
SELECT
TO_CHAR(TRUNC(first_time, 'HH24'), 'YYYY-MM-DD HH24:MI') AS switch_hour,
COUNT(*) AS switches
FROM v$log_history
WHERE first_time > SYSDATE - 1
GROUP BY TRUNC(first_time, 'HH24')
ORDER BY 1;
Findings
Metric
Observation
Average Switch Rate
5-7 per hour
Peak Rate
8-10 per hour during business hours
Off-Peak Rate
1-3 per hour
A direct correlation was observed between log switch spikes and high DML activity, confirming a cause-effect relationship rather than random errors.
Why No Changes Were Made
In this scenario, production environment restrictions were in place, no user impact was observed, and the behavior was transient and self-resolving. A monitoring-first approach was adopted instead of immediate tuning.
Recommendations
Continuously monitor redo switch frequency during peak windows
Use collected data to justify future redo log sizing via change management
Avoid unnecessary intervention when behavior is transient and non-impacting
Distinguish informational alert log messages from actionable errors
Key Takeaways
ORA-00031 is expected and harmless — it confirms session termination
Redo allocation waits are transient under sustained load
Proper analysis prevents unnecessary production intervention
Not all alert log warnings indicate failure — some are early signals of workload growth
The goal is not to eliminate every alert, but to understand which ones matter
Written by Syed Anwar Ahmed — Oracle Apps DBA with 11 years of production experience. Connect: sdanwarahmed@gmail.com | LinkedIn