Thursday, February 24, 2022

Oracle Kill Session Crashes DB Instance

This blog demonstrates DB Instance crash caused by execution of statement:
    ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
and alert.log error message:
    ORA-00700: soft internal error, arguments: [ksepop:1 ksepop recursion ]
    ORA-00601: cleanup lock conflict
    PMON: terminating the instance due to ORA error 12752
    Cause - 'Instance is being terminated due to fatal process death (CL01)'
Note: Tested on Oracle 19.13 in Linux.

Update (2023-02-26): By this Blog, Oracle fixed the described and reproduced problem with Bug 33060212 (from 19.18 in January 2023):
  Bug 33060212 - ORA-07445 [kgldmp0] and Instance Termination Due to Background Process CLMN (Doc ID 33060212.8)
    The fix for 33060212 is first included in 19.18.0.0.230117 (January 2023) DB Release Update (DB RU)


1. Test Setup


Open 3 Sqlplus sessions (SID-1, SID-2, SID-3). SID-1 and SID-2 are two test sessions. SID-3 is a monitor session.
In the test, SID-1 is "blocked session", and SID-2 is "blocker session".

At first, in each session, we run a query to get its session info (SID, SERIAL#, PID, SPID).

In SID-1, we also run two queries on dbms_standard to warm up (avoid hard parsing), which will be used in later test.

---================== SID-1 (blocked session, SID=100, SERIAL#=1001, SPID=10011) ==================---
-- grant execute on sys.dbms_support to k;
 
15:25:41 SID-1 > select s.program, s.sid, s.serial#, pid, spid, last_call_et
                 from v$session s, v$process p where s.paddr=p.addr and s.sid = sys.dbms_support.mysid;
 
  PROGRAM       SID SERIAL#  PID SPID     LAST_CALL_ET
  ------------ ---- ------- ---- -------- ------------
  sqlplus.exe   100    1001   51 10011               0
 
15:25:46 SID-1 > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
 
  LOCALTIMESTAMP       ORA_MAX_NAME_LEN_SUPPORTED
  -------------------- --------------------------
  15:25:47                                    128
 
15:25:48 SID-1 > select dbms_standard.database_name from dual;
 
  DATABASE_NAME
  ----------------------------------------
  TESTDB
 
---================== SID-2 (blocker session, SID=200, SERIAL#=2002, SPID=20022) ==================---
 
15:26:01 SID-2 > select s.program, s.sid, s.serial#, pid, spid, last_call_et
                 from v$session s, v$process p where s.paddr=p.addr and s.sid = sys.dbms_support.mysid;
 
  PROGRAM       SID SERIAL#  PID SPID     LAST_CALL_ET
  ------------ ---- ------- ---- -------- ------------
  sqlplus.exe   200    2002   52 20022               0
 
---================== SID-3 (monitor session, SID=300, SERIAL#=3003, SPID=30033) ==================---
 
15:26:08 SID-3 > select s.program, s.sid, s.serial#, pid, spid, last_call_et
                 from v$session s, v$process p where s.paddr=p.addr and s.sid = sys.dbms_support.mysid;
 
  PROGRAM       SID SERIAL#  PID SPID     LAST_CALL_ET
  ------------ ---- ------- ---- -------- ------------
  sqlplus.exe   300    3003   53 30033               0


2. Test Run


We start a gdb on SID-2 process, set breakpoint on "kgxRelease". In each step, we print out "step_count", and display actual time by "shell date".

Then in Sqlplus SID-2, we run:

   SID-2 > select dbms_standard.database_name from dual;
It is waiting for gdb instruction.

Now we run gdb continue command. It stops on "kgxRelease", which indicates that SID-2 is holding a mutex (by kglGetMutex -> kgxExclusive), but not yet released by "kgxRelease".

We go to Sqlplus SID-1, run:

   SID-1 (Run-1) > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
It returns and not blocked.

Repeat the same actions ("gdb kgxRelease" and "SID-1 run") till SID-1 is hanging.

In our test, it occurs in Step-8 (see following test action flow diagram). At this moment, it signifies that SID-1 is blocked by a mutex held by SID-2. SID-1 is waiting on event "library cache: mutex X" (see later Section 3).

Then, from SID-3, we run "kill session immediate" on SID-1:

   15:32:41 SID-3 > alter system kill session '100,1001' immediate;
 
   (Note* instead of killing "blocker session" SID-2, we kill "blocked session" SID-1)
Now SID-1 shows "ORA-03114: not connected to ORACLE".

Immediately from v$session, we saw both SID-1 and one CLMN/CL01 are waiting on event "library cache: mutex X" (see later Section 3). In order to to cleanup killed processes (Marked process newly dead), CL01 from PMON group (PMON, CLMN, CLnn) is called, but also blocked (see later Section 3).

After about 5 minutes, DB instance crashed.

========= gdb Session-2 process ========= | ================================ SQL Session-1 ===================================
oracle@testdb > gdb -p 20022              |
                                          |
(gdb) break kgxRelease                    |
Breakpoint 1 at 0x12f90180                |
(gdb) set $step_count = 0                 |
(gdb) display $step_count++               |
1: $step_count++ = 0                      |
(gdb) shell date                          |
15:28:02                                  |
                                          |
-------------------------- 15:28:30 SID-2 > select dbms_standard.database_name from dual; ---------------------------------
                                          |                                         
------------------------------------- Step-1 --------------------------------------------------------------------------------
(gdb) c                                   |
Continuing.                               |
Breakpoint 1, 0x12f90180 in kgxRelease () |
1: $step_count++ = 1                      |
(gdb) shell date                          |
15:28:58                                  |
                                       --->>>
                                          | SID-1 (Run-1) > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
                                          |
                                          |   LOCALTIMESTAMP       ORA_MAX_NAME_LEN_SUPPORTED
                                          |   -------------------- --------------------------
                                          |   15:29:01                                    128
------------------------------------- Step-2 --------------------------------------------------------------------------------
(gdb) c                                   |
Continuing.                               |
Breakpoint 1, 0x12f90180 in kgxRelease () |
1: $step_count++ = 2                      |
(gdb) shell date                          |
15:29:17                                  |
                                       --->>>
                                          | SID-1 (Run-2) > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
                                          |
                                          |   LOCALTIMESTAMP       ORA_MAX_NAME_LEN_SUPPORTED
                                          |   -------------------- --------------------------
                                          |   15:29:27                                    128
------------------------------------- Step-3 --------------------------------------------------------------------------------
(gdb) c                                   |
Continuing.                               |
Breakpoint 1, 0x12f90180 in kgxRelease () |
1: $step_count++ = 3                      |
(gdb) shell date                          |
15:29:32                                  |
                                       --->>>
                                          | SID-1 (Run-3) > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
                                          |
                                          |   LOCALTIMESTAMP       ORA_MAX_NAME_LEN_SUPPORTED
                                          |   -------------------- --------------------------
                                          |   15:29:37                                    128
------------------------------------- Step-4 --------------------------------------------------------------------------------
(gdb) c                                   |
Continuing.                               |
Breakpoint 1, 0x12f90180 in kgxRelease () |
1: $step_count++ = 4                      |
(gdb) shell date                          |
15:29:45                                  |
                                       --->>>
                                          | SID-1 (Run-4) > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
                                          |
                                          |   LOCALTIMESTAMP       ORA_MAX_NAME_LEN_SUPPORTED
                                          |   -------------------- --------------------------
                                          |   15:29:50                                    128
------------------------------------- Step-5 --------------------------------------------------------------------------------
(gdb) c                                   |
Continuing.                               |
Breakpoint 1, 0x12f90180 in kgxRelease () |
1: $step_count++ = 5                      |
(gdb) shell date                          |
15:29:57                                  |
                                       --->>>
                                          | SID-1 (Run-5) > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
                                          |
                                          |   LOCALTIMESTAMP       ORA_MAX_NAME_LEN_SUPPORTED
                                          |   -------------------- --------------------------
                                          |   15:30:06                                    128
------------------------------------- Step-6 --------------------------------------------------------------------------------
(gdb) c                                   |
Continuing.                               |
Breakpoint 1, 0x12f90180 in kgxRelease () |
1: $step_count++ = 6                      |
(gdb) shell date                          |
15:30:11                                  |
                                       --->>>
                                          | SID-1 (Run-6) > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
                                          |
                                          |   LOCALTIMESTAMP       ORA_MAX_NAME_LEN_SUPPORTED
                                          |   -------------------- --------------------------
                                          |   15:30:38                                    128
------------------------------------- Step-7 --------------------------------------------------------------------------------
(gdb) c                                   |
Continuing.                               |
Breakpoint 1, 0x12f90180 in kgxRelease () |
1: $step_count++ = 7                      |
(gdb) shell date                          |
15:30:46                                  |
                                       --->>>
                                          | SID-1 (Run-7) > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
                                          |
                                          |   LOCALTIMESTAMP       ORA_MAX_NAME_LEN_SUPPORTED
                                          |   -------------------- --------------------------
                                          |   15:30:50                                    128
------------------------------------- Step-8 --------------------------------------------------------------------------------
(gdb) c                                   |
Continuing.                               |
Breakpoint 1, 0x12f90180 in kgxRelease () |
1: $step_count++ = 8                      |
(gdb) shell date                          |
15:31:05                                  |
                                       --->>>
                                          | SID-1 (Run-8) > select localtimestamp, dbms_standard.ora_max_name_len_supported from dual;
                                          | select localtimestamp, dbms_standard.ora_max_name_len_supported from dual
                                          |
                                          | *** SID-1 is blocked ***
                                          |                                                                                     
-------------------------- 15:32:41 SID-3 > alter system kill session '100,1001' immediate; ---------------------------------
                                          |
                                          | ERROR at line 1:
                                          | ORA-03113: end-of-file on communication channel
                                          | Process ID: 10011
                                          | Session ID: 100 Serial number: 1001
                                          | ERROR:
                                          | ORA-03114: not connected to ORACLE
                                          |
-----------------------------------------------------------------------------------------------------------------------------
From gdb, we can also show call stack and register info of of SID-2.

(gdb) display $r8d                       
2: $r8d = 4                              
 
(gdb) bt
#0  0x12f90180 in kgxRelease ()
#1  0x12f6a588 in kglReleaseMutex ()
#2  0x12f635b7 in kglLoadOnLock ()
#3  0x12f62255 in kgllkal ()
#4  0x12f5d90e in kglLock ()
#5  0x12f587d5 in kglget ()
#6  0x034d1e68 in kksaxs ()
#7  0x034d016d in kksauc ()
#8  0x034e3db9 in kkscscid_auc_eval ()
#9  0x12c3d85a in kkscsCheckCriteria ()
#10 0x034e1d17 in kkscsCheckCursor ()
#11 0x034e0eec in kkscsSearchChildList ()
#12 0x12c2e5c6 in kksfbc ()
#13 0x12c27e37 in kkspsc0 ()
#14 0x12c27552 in kksParseCursor ()
#15 0x12e139c6 in opiosq0 ()
#16 0x12b7d71f in kpooprx ()
#17 0x12b7b115 in kpoal8 ()
#18 0x12b08902 in opiodr ()
#19 0x12ed2fbe in ttcpip ()
#20 0x028afa8c in opitsk ()
#21 0x028b43e8 in opiino ()
#22 0x12b08902 in opiodr ()
#23 0x028ab806 in opidrv ()
#24 0x03425585 in sou2o ()
#25 0x00dcb266 in opimai_real ()
#26 0x03430fb1 in ssthrdmain ()
#27 0x00dcb090 in main ()
 
(gdb) info r
rax            0x0      0
rbx            0x7fecd71ec9c0   140655198128576
rcx            0x7fecd70f4738   140655197112120
rdx            0x0      0
rsi            0x9b71f4b0       2607936688
rdi            0x7fecd71ec9c0   140655198128576
rbp            0x7ffebb73bbe0   0x7ffebb73bbe0
rsp            0x7ffebb73bb78   0x7ffebb73bb78
r8             0x4      4
r9             0x80002801       2147493889
r10            0x0      0
r11            0x2      2
r12            0x9b71f4b0       2607936688
r13            0xa27c0820       2726037536
r14            0x2      2
r15            0x7ffebb73c9a0   140732043348384
rip            0x12f90180       0x12f90180 
eflags         0x246    [ PF ZF IF ]
cs             0x33     51
ss             0x2b     43
Once SID-1 is blocked in Step-8, if we make an errorstack dump of SID-1 before SID-1 kill session, the Call Stack shows that SID-1 is blocked in "kglGetMutex -> kgxExclusive -> kgxWait":

oradebug dump errorstack 3
 
  ----- Current SQL Statement for this session (sql_id=27t9knmt7u5pq) -----
  select localtimestamp, dbms_standard.ora_max_name_len_supported from dual
 
  ----- Call Stack Trace -----
    FRAME [1] (ksedst1()+95 -> kgdsdst())
    FRAME [2] (ksedst()+58 -> ksedst1())
    FRAME [3] (dbkedDefDump()+23448 -> ksedst())
    FRAME [4] (ksedmp()+577 -> dbkedDefDump())
    FRAME [5] (ksdxdmp()+1425 -> ksedmp())
    FRAME [6] (ksdxfdmp()+152 -> ksdxdmp())
    FRAME [7] (ksdxcb()+872 -> ksdxfdmp())
    FRAME [8] (sspuser()+200 -> ksdxcb())
    FRAME [9] (__sighandler() -> sspuser())
    FRAME [10] (semtimedop()+10 -> __sighandler())
    FRAME [11] (sskgpwwait()+245 -> semtimedop())
    FRAME [12] (skgpwwait()+187 -> sskgpwwait())
    FRAME [13] (ksliwat()+2293 -> skgpwwait())
    FRAME [14] (kslwaitctx()+200 -> ksliwat())
    FRAME [15] (kgxWait()+1291 -> kslwaitctx())
    FRAME [16] (kgxExclusive()+712 -> kgxWait())
    FRAME [17] (kglGetMutex()+147 -> kgxExclusive())
    FRAME [18] (kglpin()+696 -> kglGetMutex())
    FRAME [19] (kglpnp()+439 -> kglpin())
    FRAME [20] (kgiina()+352 -> kglpnp())
    FRAME [21] (kgiinp()+39 -> kgiina())
    FRAME [22] (peiinspbn()+146 -> kgiinp())
    FRAME [23] (kkxpispbn()+212 -> peiinspbn())
    FRAME [24] (kgmexec()+688 -> kkxpispbn())
    FRAME [25] (evapls()+1270 -> kgmexec())
    FRAME [26] (evaopn2()+747 -> evapls())
    FRAME [27] (kpofcr()+7390 -> evaopn2())
    FRAME [28] (qerfiFetch()+143 -> kpofcr())
    FRAME [29] (opifch2()+3211 -> qerfiFetch())
    FRAME [30] (kpoal8()+4185 -> opifch2())
    FRAME [31] (opiodr()+1202 -> kpoal8())
    FRAME [32] (ttcpip()+1246 -> opiodr())


3. Session Event Monitoring During Test


From SID-3 (monitor session), we can see the Session Event change in three steps:
    (1). No Blocking
    (2). User Session Blocked on "library cache: mutex X" when mutex not released (kgxRelease) before Kill Session
    (3). Both User Session and CL01 Blocked on "library cache: mutex X" when mutex not released (kgxRelease) after Kill Session

----------================== SID-3 (monitor session, SID=300, SERIAL#=3003, SPID=30033) ===========================================================
-----****** Before blocking
 
15:30:30 SID-3 > select s.program, event, s.sid, s.serial#, pid, spid, p1raw, p2raw, p3raw, last_call_et, p1text, p2text, p3text
                 from v$session s, v$process p
                 where s.paddr=p.addr and (lower(s.program) like '%sql%' or lower(s.program) like '%pmon%'
                                        or lower(s.program) like '%(cl%' or lower(s.program) like '%(dia%')
                 order by s.program desc;
 
PROGRAM               EVENT                         SID SERIAL#  PID SPID  P1RAW    P2RAW            P3RAW            LAST_CALL_ET P1TEXT     P2TEXT  P3TEXT
--------------------- ---------------------------- ---- ------- ---- ----- -------- ---------------- ---------------- ------------ ---------  ------- --------------------
sqlplus.exe           SQL*Net message from client   100    1001   51 10011 54435000 0000000000000001 00                         24 driver id  #bytes
sqlplus.exe           SQL*Net message from client   200    2002   52 20022 54435000 0000000000000001 00                         61 driver id  #bytes
sqlplus.exe           SQL*Net message to client     300    3003   53 30033 54435000 0000000000000001 00                          0 driver id  #bytes
oracle@testdb (PMON)  pmon timer                    359   33127    2 31362 0000012C 00               00                        562 duration  
oracle@testdb (DIAG)  DIAG idle wait                897   49978   11 31394 00000003 0000000000000001 00                        560 component  where   wait time(millisec)
oracle@testdb (DIA0)  DIAG idle wait                  3   31438   18 31411 00000003 0000000000000001 00                        560 component  where   wait time(millisec)
oracle@testdb (CLMN)  pmon timer                    539   36539    3 31366 0000012C 00               00                        562 duration  
oracle@testdb (CL01)  pmon timer                    186   46807   49 31939 0000012C 00               00                        465 duration  
oracle@testdb (CL00)  pmon timer                    722   15363   34 31937 0000012C 00               00                        465 duration  
 
 
-----****** During "library cache: mutex X" blocking of User Session SID-1 (SID=100) before Kill Session
 
15:31:26 SID-3 > select s.program, event, s.sid, s.serial#, pid, spid, p1raw, p2raw, p3raw, last_call_et, p1text, p2text, p3text
                 from v$session s, v$process p
                 where s.paddr=p.addr and (lower(s.program) like '%sql%' or lower(s.program) like '%pmon%'
                                        or lower(s.program) like '%(cl%' or lower(s.program) like '%(dia%')
                 order by s.program desc;
 
PROGRAM               EVENT                         SID SERIAL#  PID SPID  P1RAW    P2RAW            P3RAW            LAST_CALL_ET P1TEXT    P2TEXT  P3TEXT
--------------------- ---------------------------- ---- ------- ---- ----- -------- ---------------- ---------------- ------------ --------- ------  --------------------
sqlplus.exe           library cache: mutex X        100    1001   51 10011 7CE2FAC4 00000C8000000000 000004C500010004           18 idn       value   where
sqlplus.exe           SQL*Net message from client   200    2002   52 20022 54435000 0000000000000001 00                        117 driver id #bytes
sqlplus.exe           SQL*Net message to client     300    3003   53 30033 54435000 0000000000000001 00                          0 driver id #bytes
oracle@testdb (PMON)  pmon timer                    359   33127    2 31362 0000012C 00               00                        618 duration        
oracle@testdb (DIAG)  DIAG idle wait                897   49978   11 31394 00000003 0000000000000001 00                        616 component where   wait time(millisec)
oracle@testdb (DIA0)  DIAG idle wait                  3   31438   18 31411 00000003 0000000000000001 00                        616 component where   wait time(millisec)
oracle@testdb (CLMN)  pmon timer                    539   36539    3 31366 0000012C 00               00                        618 duration        
oracle@testdb (CL01)  pmon timer                    186   46807   49 31939 0000012C 00               00                        521 duration        
oracle@testdb (CL00)  pmon timer                    722   15363   34 31937 0000012C 00               00                        521 duration        
 
 
-----****** "kill session immediate" on SID-1
 
15:32:41 SID-3 > alter system kill session '100,1001' immediate;
 
   System altered.
 
 
-----****** During "library cache: mutex X" blocking of both User Session SID-1 (SID=100), and CLMN/CLnn (CL01 SID=186) after Kill Session
 
15:33:05 SID-3 > select s.program, event, s.sid, s.serial#, pid, spid, p1raw, p2raw, p3raw, last_call_et, p1text, p2text, p3text
                 from v$session s, v$process p
                 where s.paddr=p.addr and (lower(s.program) like '%sql%' or lower(s.program) like '%pmon%'
                                        or lower(s.program) like '%(cl%' or lower(s.program) like '%(dia%')
                 order by s.program desc;
 
PROGRAM               EVENT                         SID SERIAL#  PID SPID  P1RAW    P2RAW            P3RAW            LAST_CALL_ET P1TEXT     P2TEXT  P3TEXT
--------------------- ---------------------------- ---- ------- ---- ----- -------- ---------------- ---------------- ------------ ---------- ------- --------------------
sqlplus.exe           library cache: mutex X        100    1001   51 10011 7CE2FAC4 00000C8000000000 000004C500010004          117 idn        value   where
sqlplus.exe           SQL*Net message from client   200    2002   52 20022 54435000 0000000000000001 00                        216 driver id  #bytes
sqlplus.exe           SQL*Net message to client     300    3003   53 30033 54435000 0000000000000001 00                          0 driver id  #bytes
oracle@testdb (PMON)  pmon timer                    359   33127    2 31362 0000012C 00               00                        717 duration
oracle@testdb (DIAG)  DIAG idle wait                897   49978   11 31394 00000003 0000000000000001 00                        715 component  where   wait time(millisec)
oracle@testdb (DIA0)  DIAG idle wait                  3   31438   18 31411 00000003 0000000000000001 00                        715 component  where   wait time(millisec)
oracle@testdb (CLMN)  pmon timer                    539   36539    3 31366 0000012C 00               00                        717 duration
oracle@testdb (CL01)  library cache: mutex X        186   46807   49 31939 7CE2FAC4 00000C8000000000 000004C50001004F          620 idn        value   where
oracle@testdb (CL00)  pmon timer                    722   15363   34 31937 0000012C 00               00                        620 duration
In some tests, instead of CLnn, we also saw CLMN is waiting for "library cache: mutex X".

P2RAW in Event "library cache: mutex X" is mutex "value", which contains mutex holder (blocker) SID. In this test, it is 200 = 0xC8.

P3RAW in Event "library cache: mutex X" is mutex "where". Its last 8-bit byte indicates the blocking subroutine location.
In this test, it is 79 = 0x4F (kglrfcl1).

We can print out those locations from kglMutexLocations[] array:

#Define Command to PrintkglMutexLocations
define PrintkglMutexLocations
  set $i = 0
  while $i < $arg0 + $arg0
    x /s *(&kglMutexLocations + $i)
    set $i = $i + 2
  end
end
 
(gdb) PrintkglMutexLocations 174
 
  kglpin1    4 (0x04)
  kglhdgn1  62 (0x3E)
  kglrfcl1  79 (0x4F)
  kgllkdl1  85 (0x55)
  kglhdgn2 106 (0x6A)
In a few tests, we saw CLnn having "P3RAW=000004C500010055" on "library cache: mutex X". After about two minutes (see Section DB alert.log), DIAG (DIA0) was active to perform certain actions on Hung Sessions, and CLMN took over "library cache: mutex X" with "P3RAW=000004C50001004F" on "library cache: mutex X". (changed from kgllkdl1 (0x55: library cache lock delete) to kglrfcl1 (0x4F: library cache reference clear))


4. DB alert.log, Dumps and Incident Files


For instance crash, we can also have a look of alert.log, dumps from user and system sessions (CLMN/CLnn, PMON, DIAG/DIAn, MMON/MMON_SLAVE).


4.1 DB alert.log


alert.log showed that KILL SESSION at 15:32:41, DIA0 is active at 15:35:12 (151 seconds later), and finally at 15:37:59 (167 seconds later), errors are signalled and Instance termination was alerted:

    ORA-00700: soft internal error, arguments: [ksepop:1 ksepop recursion ]
    ORA-00601: cleanup lock conflict
   'Instance is being terminated due to fatal process death (pid: 49, ospid: 31939, CL01)'

----------================== alert.log ==================----------
15:32:41.540893
Process termination requested for pid 10011 [source = rdbms], [info = 2] [request issued by pid: 30033, uid: 100]
15:32:41.590770
KILL SESSION for sid=(100, 1001):
  Reason = alter system kill session
  Mode = KILL HARD SAFE -/-/-
  Requestor = USER (orapid = 53, ospid = 30033, inst = 1)
  Owner = Process: USER (orapid = 51, ospid = 10011)
  Result = ORA-0
15:35:12.167943
DIA0 Critical Database Process Blocked: Hang ID 1 blocks 2 sessions
     Final blocker is session ID 200 serial# 2002 OSPID 20022 on Instance 1
     No resolution will be attempted by Hang Manager
15:37:55.685771
Errors in file /orabin/app/oracle/admin/testdb/diag/rdbms/testdb/testdb/trace/testdb_cl01_31939.trc  (incident=89993):
ORA-00700: soft internal error, arguments: [ksepop:1 ksepop recursion ], [], [], [], [], [], [], [], [], [], [], []
ORA-00601: cleanup lock conflict
ORA-00601: cleanup lock conflict
Incident details in: /orabin/app/oracle/admin/testdb/diag/rdbms/testdb/testdb/incident/incdir_89993/testdb_cl01_31939_i89993.trc
15:37:57.240744
Dumping diagnostic data in directory=[cdmp_20220222153757], requested by (instance=1, osid=31939 (CL01)), summary=[incident=89993].
15:37:59.637484
PMON (ospid: 31362): terminating the instance due to ORA error 12752
15:37:59.637658
Cause - 'Instance is being terminated due to fatal process death (pid: 49, ospid: 31939, CL01)'
15:37:59.638404
System state dump requested by (instance=1, osid=31362 (PMON)), summary=[abnormal instance termination].
At 15:35:12.167943, DIA0 detected that 2 sessions were blocked by SID-2 (session ID 200 serial# 2002 OSPID 20022 ). The 2 blocked sessions are SID-1 (orapid = 51, ospid = 10011) and CL01.


4.2 CL01 Dump


cl01.trc looks like:

----------================== testdb_cl01_31939.trc ==================----------
Unix process pid: 31939, image: oracle@testdb (CL01)
*** 15:32:41.593097
*** SESSION ID:(186.46807) 15:32:41.593132
 
KGX cleanup...
KGX Atomic Operation Log 0x9b77d058
Mutex 0xa27c0820(727, 0) idn 7ce2fac4 oper GET_EXCL(5)
Library Cache uid 548 efd 7 whr 4 slp 9271
oper=0 pt1=0xa27c06d0 pt2=(nil) pt3=(nil)
pt4=(nil) pt5=(nil) ub4=0 flg=0x0 uw1=0 uw2=0
msk=0000-0000-0000-0000-0000
 
*** 15:35:11.727606
KGL UOL cleanup...
KGX Atomic Operation Log 0x9c44cf58
Mutex (nil)(0, 0) idn 0 oper NONE(0)
Library Cache uid 186 efd 15 whr 77 slp 0
oper=259 pt1=0x9b6a3938 pt2=0x9b6a2800 pt3=(nil)
pt4=0x9b6a3f58 pt5=(nil) ub4=0 flg=0x0 uw1=0 uw2=0
msk=0000-0000-0000-0000-0000
 
LibraryHandle:  Address=0x9b6a3938 Hash=0 LockMode=0 PinMode=0 LoadLockMode=0 Status=INVL Subpool=1
  Name:  Namespace=SQL AREA(00) Type=CURSOR(00) ContainerId=0
  Statistics:  InvalidationCount=1 ExecutionCount=9 LoadCount=1 ActiveLocks=0 TotalLockCount=3 TotalPinCount=10
  Counters:  BrokenCount=2 RevocablePointer=2 KeepDependency=0 Version=0 BucketInUse=0 HandleInUse=0 HandleReferenceCount=0
  Concurrency:  DependencyMutex=0x9b6a39e8(0, 0, 0, 0) Mutex=0x9b6a51e0(186, 34, 0, 6)
  Flags=RON/PIN/PN0/EXP/CHD/[10010111] Flags2=[0000]
  WaitersLists: 
    Lock=0x9b6a39c8[0x9b6a39c8,0x9b6a39c8]
    Pin=0x9b6a39a8[0x9b6a39a8,0x9b6a39a8]
    LoadLock=0x9b6a3a20[0x9b6a3a20,0x9b6a3a20]
  LibraryObject:  Address=0x9b6a2800 HeapMask=0000-0000-0000-0000
  NamespaceDump: 
    Child Cursor:  Heap0=0x9b6a28e0 Heap6=0x7f65526469c0 Heap0 Load Time=15:25:46 Heap6 Load Time=15:25:46 15:37:55.686246
Incident 89993 created, dump file: /orabin/app/oracle/admin/testdb/diag/rdbms/testdb/testdb/incident/incdir_89993/testdb_cl01_31939_i89993.trc
 
*** 15:37:55.686302
ORA-00700: soft internal error, arguments: [ksepop:1 ksepop recursion ], [], [], [], [], [], [], [], [], [], [], []
ORA-00601: cleanup lock conflict
It shows "KGX cleanup" on "idn 7ce2fac4" at 15:32:41, and "KGL UOL cleanup" at 15:35:11 (it is close to above DIA0 detection time of 15:35:12).

With following query, we can see "7CE2FAC4" (HASH_VALUE=2095250116) is SYS.DBMS_STANDARD Package Spec, which is also showed as P1RAW for the session event "library cache: mutex X".

select owner, name, namespace, type, hash_value, to_char(hash_value, 'XXXXXXXX') hash_value_HEX
from v$db_object_cache v where name like 'DBMS_STANDARD';
 
  OWNER    NAME                 NAMESPACE            TYPE       HASH_VALUE HASH_VALUE_HEX
  -------- -------------------- -------------------- ---------- ---------- --------------------
  K        DBMS_STANDARD        TABLE/PROCEDURE      CURSOR      599930705  23C23751
  PUBLIC   DBMS_STANDARD        TABLE/PROCEDURE      SYNONYM    2691725148  A070775C
 SYS      DBMS_STANDARD        TABLE/PROCEDURE      PACKAGE    2095250116  7CE2FAC4
  SYS      DBMS_STANDARD        BODY                 CURSOR     3231142607  C09752CF
Text "Mutex=0x9b6a51e0(186, 34, 0, 6)" shows cl01 (SESSION ID: 186) in Execlusuve lock (6).


4.3 CL01 Incident File



----------================== incident/incdir_89993/testdb_cl01_31939_i89993.trc ==================----------
*** SESSION ID:(186.46807) 15:37:55
ORA-00700: soft internal error, arguments: [ksepop:1 ksepop recursion ], [], [], [], [], [], [], [], [], [], [], []
 
  ----- Call Stack Trace -----
  FRAME [11] (kgesoftnmierr()+712 -> kgerinv_internal())
  FRAME [12] (ksepop()+1007 -> kgesoftnmierr())
  FRAME [13] (kgepop()+135 -> ksepop())
  FRAME [14] (kgesecl0()+145 -> kgepop())
  FRAME [15] (kgxWait()+1403 -> kgesecl0())
  FRAME [16] (kgxExclusive()+712 -> kgxWait())
  FRAME [17] (kglGetMutex()+147 -> kgxExclusive())
  FRAME [18] (kglrfcl()+354 -> kglGetMutex())
  FRAME [19] (kglobcl()+589 -> kglrfcl())
  FRAME [20] (kglobfr()+366 -> kglobcl())
  FRAME [21] (kgllccl()+695 -> kglobfr())
  FRAME [22] (kglMutexCleanupAll()+1499 -> kgllccl())
  FRAME [23] (kglOnErrorMutexCleanup()+76 -> kglMutexCleanupAll())
  FRAME [24] (ksepop()+351 -> kglOnErrorMutexCleanup())
  FRAME [25] (kgepop()+135 -> ksepop())
  FRAME [26] (kgepop()+472 -> kgepop())
  FRAME [27] (ksesecl0()+189 -> kgepop())
  FRAME [28] (ksucln_bump_cleanup_timer()+1053 -> ksesecl0())
  FRAME [29] (ksucln_check_wait_tasks()+137 -> ksucln_bump_cleanup_timer())
  FRAME [30] (ksliwat()+3923 -> ksucln_check_wait_tasks())
  FRAME [31] (kslwaitctx()+200 -> ksliwat())
  FRAME [32] (kgxWait()+1291 -> kslwaitctx())
  FRAME [33] (kgxExclusive()+712 -> kgxWait())
  FRAME [34] (kglGetMutex()+147 -> kgxExclusive())
  FRAME [35] (kglrfcl()+354 -> kglGetMutex())
  FRAME [36] (kglobcl()+589 -> kglrfcl())
  FRAME [37] (kglobfr()+366 -> kglobcl())
  FRAME [38] (kglobf0()+327 -> kglobfr())
  FRAME [39] (kgllkdl()+1532 -> kglobf0())
  FRAME [40] (kss_del_cb()+811 -> kgllkdl())
 
    Current Wait Stack:
     1: waiting for 'library cache: mutex X'
        idn=0x7ce2fac4, value=0xc8000000000, where=0x4c50001004f
        wait_id=202 seq_num=203 snap_id=1
        wait times: snap=2 min 44 sec, exc=2 min 44 sec, total=2 min 44 sec
        wait times: max=infinite, heur=2 min 44 sec
        wait counts: calls=16383 os=16383
        in_wait=1 iflags=0x1532
     0: waiting for 'library cache: mutex X'
        idn=0x7ce2fac4, value=0xc8000000000, where=0x4c50001004f
        wait_id=201 seq_num=202 snap_id=1
        wait times: snap=5 min 14 sec, exc=2 min 30 sec, total=5 min 14 sec
        wait times: max=infinite, heur=5 min 14 sec
        wait counts: calls=15000 os=15000
        in_wait=1 iflags=0x15b2
    There is at least one session blocking this session.
      Dumping 1 direct blocker(s):
        inst: 1, sid: 200, ser: 2002
      Dumping final blocker:
        inst: 1, sid: 200, ser: 2002
The error message said:

   "ORA-00700: soft internal error, arguments: [ksepop:1 ksepop recursion ]"
In the Call Stack, we can see two same calls in two different frames:

  FRAME [13] (kgepop()+135 -> ksepop())
  FRAME [25] (kgepop()+135 -> ksepop())
Probably that is interpreted as "recursion" in "[ksepop:1 ksepop recursion ]".

In "Current Wait Stack" section, we can see 'library cache: mutex X' with "idn=0x7ce2fac4", and blocker is SID-1 (sid: 200, ser: 2002).


4.4 SID-1 (blocked session) Dump


SID-1 is waiting for 'library cache: mutex X' with "idn=0x7ce2fac4" and "where=0x4c500010004" (kglpin1).

----------================== testdb_ora_10011_bucket.trc ==================----------
Unix process pid: 32246, image: oracle@testdb
*** SESSION ID:(100.1001) 15:37:57.302316
 
*** 15:37:57.302326
Process diagnostic dump for oracle@testdb, OS id=10011,
-------------------------------------------------------------------------------
current sql: 
client details:
  machine: SYS\M7080 program: sqlplus.exe
Current Wait Stack:
0: waiting for 'library cache: mutex X'
    idn=0x7ce2fac4, value=0xc8000000000, where=0x4c500010004
    wait_id=357 seq_num=359 snap_id=1
    wait times: snap=6 min 48 sec, exc=6 min 48 sec, total=6 min 48 sec
    wait times: max=infinite, heur=6 min 48 sec
    wait counts: calls=9271 os=9271
    in_wait=1 iflags=0x55b2


4.5 SID-2 (blocker session) Dump


There are 2 sessions blocked by this session (SID-2). One waiter is CL01 (sid: 186, ser: 46807), waiting for 'library cache: mutex X' with "idn=0x7ce2fac4" and "where=0x4c50001004f" (kglrfcl1).

----------================== testdb_ora_20022_bucket.trc ==================----------
Unix process pid: 20022, image: oracle@testdb
*** SESSION ID:(200.2002) 15:37:57.303902
*** 15:37:57.303913
Process diagnostic dump for oracle@testdb, OS id=20022,
-------------------------------------------------------------------------------
current sql: select dbms_standard.database_name from dual
client details:
  machine: SYS\M7080 program: sqlplus.exe
Current Wait Stack:
  Not in wait; last wait ended 9 min 3 sec ago
There are 2 sessions blocked by this session.
Dumping one waiter:
  inst: 1, sid: 186, ser: 46807
  wait event: 'library cache: mutex X'
    p1: 'idn'=0x7ce2fac4
    p2: 'value'=0xc8000000000
    p3: 'where'=0x4c50001004f
  row_wait_obj#: 4294967295, block#: 0, row#: 0, file# 0
  min_blocked_time: 310 secs, waiter_cache_ver: 327
Blog: DBMS_SCHEDULER Job Not Running and Used Slaves has some further discussions on Oracle Kill Statement and UNIX Kill Command.

Wednesday, February 23, 2022

How to determine session's CURRVAL of an oracle sequence without error and without modify ?

Any reference to CURRVAL always returns the last reference to NEXTVAL in the current session. Each session caches its own previous call of NEXTVAL. CURRVAL is session private, whereas NEXTVAL is DB wide global and unique.

If you use CURRVAL without previous call of NEXTVAL, you will get error:
  ORA-08002: sequence SEQ.CURRVAL is not yet defined in this session
If you call NEXTVAL, it will modify the sequence.

So how can we determine session's CURRVAL of a sequence without error and without modify ?

The similar question is asked in:
How to retrieve the current value of an oracle sequence without increment it?

Note: Tested on Oracle 19c in Linux


First we create a sequence:

create sequence test_seq_1;
Then open a new Sqlplus session, make a session heapdump at level 29 (PGA, UGA, CGA, top call heaps, call heaps, session heap):

alter session set tracefile_identifier = 'without_currval';
alter session set events 'immediate trace name heapdump level 29';
In the dump, searching "SEQ CACHE", we found:

  7f0b13d3db18 sz=      504    cprm      "SEQ CACHE      "
Pick this address and make a heapdump_addr:

alter session set tracefile_identifier = 'seq_1';
alter session set events 'immediate trace name heapdump_addr level 2, addr 0x7f0b13d3db18';  
Then search around "addr 0x7f0b13d3db18", we can see the cached sequence: SYS.AUDSES$. There is no "SEQ CACHE" for test_seq_1.

7F0B13D3DB10 00000000 00000000 000001F9 00B38F00  [................]
7F0B13D3DB20 147F8A9C 00000000 1450F9A0 00007F0B  [..........P.....]
7F0B13D3DB30 1450F9A0 00007F0B 55410007 53455344  [..P.......AUDSES]
7F0B13D3DB40 00000024 00000000 00000000 00000000  [$...............]
7F0B13D3DB50 00000000 00000000 00000000 00000000  [................]
        Repeat 5 times
7F0B13D3DBB0 00000000 00000000 00070000 53445541  [............AUDS]
7F0B13D3DBC0 00245345 00000000 00000000 00000000  [ES$.............]
7F0B13D3DBD0 00000000 00000000 00000000 00000000  [................]
        Repeat 5 times
7F0B13D3DC30 00000000 00000000 00000000 59530003  [..............SY]
7F0B13D3DC40 00000053 00000000 00000000 00000000  [S...............]
7F0B13D3DC50 00000000 00000000 00000000 00000000  [................]
        Repeat 7 times
7F0B13D3DCD0 00000169 4EC4000C 00542706 00000000  [i......N.'T.....]
For SYS.AUDSES$, a session connected to an Oracle database may obtain one of its session identifiers, the Auditing Session ID (V$SESSION.AUDSID), by use of the built-in USERENV SQL function
(MOS Docu: How Sessions get Their AUDSID Identifier (Doc ID 122230.1)).

select AUDSID, dump(AUDSID, 16) AUDSID_dump, userenv('SESSIONID'), s.sid from v$session s where sid = sys.dbms_support.mysid;
 
    AUDSID  AUDSID_DUMP                   USERENV('SESSIONID')   SID
  --------  ----------------------------  -------------------- -----
  77053883  Typ=2 Len=5: c4,4e,6,27,54                77053883   189
In address line: 7F0B13D3DCD0, we can see AUDSID=77053883 in internal form (little endian): "4EC4000C 00542706".

Now we call test_seq_1.nextval:

select test_seq_1.nextval, dump(1, 16) dump from dual;
 
   NEXTVAL   DUMP
  -------   ------------------
        1   Typ=2 Len=2: c1,2
Make again a session heapdump:

alter session set tracefile_identifier = 'with_currval';
alter session set events 'immediate trace name heapdump level 29';
There is one entry about "SEQ CACHE" (SYS.AUDSES$ is no more cached, and its cache address is re-used).
The "SEQ CACHE" is located in "session heap".

HEAP DUMP heap name="session heap"  desc=0x7f0b14509568
  7f0b13d3db18 sz=      504    cprm      "SEQ CACHE      "
HEAP DUMP heap name="pga heap"  desc=0x7f0b192fd220
HEAP DUMP heap name="top call heap"  desc=0x7f0b192ffb40
HEAP DUMP heap name="top uga heap"  desc=0x7f0b192ffe00
HEAP DUMP heap name="callheap"  desc=0x7f0b192fe160
HEAP DUMP heap name="callheap"  desc=0x7f0b192fe160
Pick address: 7f0b13d3db18 and make a heapdump_addr:

alter session set tracefile_identifier = 'seq_2';
alter session set events 'immediate trace name heapdump_addr level 2, addr 0x7f0b13d3db18';
 
7F0B13D3DB10 00000000 00000000 000001F9 00B38F00  [................]
7F0B13D3DB20 147F8A9C 00000000 1450F9B0 00007F0B  [..........P.....]
7F0B13D3DB30 1450F9B0 00007F0B 4554000A 535F5453  [..P.......TEST_S]
7F0B13D3DB40 315F5145 00000000 00000000 00000000  [EQ_1............]
7F0B13D3DB50 00000000 00000000 00000000 00000000  [................]
        Repeat 5 times
7F0B13D3DBB0 00000000 00000000 000A0000 54534554  [............TEST]
7F0B13D3DBC0 5145535F 0000315F 00000000 00000000  [_SEQ_1..........]
7F0B13D3DBD0 00000000 00000000 00000000 00000000  [................]
        Repeat 5 times
7F0B13D3DC30 00000000 00000000 00000000 004B0001  [..............K.]
7F0B13D3DC40 00000000 00000000 00000000 00000000  [................]
        Repeat 7 times
7F0B13D3DCC0 96BA7940 00000000 906F3D38 00000000  [@y......8=o.....]
7F0B13D3DCD0 00472B44 02C10009 00542706 00000000  [D+G......'T.....]
In address line: 7F0B13D3DCD0, we can see test_seq_1.nextval=1 in internal form (little endian): "02C1".

From above dumps, we can see that each sequence used in the session has one "SEQ CACHE" for its currval with sz= 504.
The cached currval is stored from offset 444:

select to_number('7F0B13D3DCD0', 'XXXXXXXXXXXX') + 4 - to_number('7F0B13D3DB18', 'XXXXXXXXXXXX') offset from dual;
 
   OFFSET
  -------
      444

kdnnxt and kdncur Subroutines


The call stack for nextval and kdncur shows that internal function kdnnxt and kdncur are called.

--==== select test_seq_1.nextval from dual;
 
#0   kdnnxt ()
#1   qersqPopulate ()
#2   qersqRowProcedure ()
#3   qerfiFetch ()
#4   qersqFetch ()
#5   opifch2 ()
#6   kpoal8 ()
#7   opiodr ()
#8   ttcpip ()
#9   opitsk ()
#10  opiino ()
#11  opiodr ()
#12  opidrv ()
#13  sou2o ()
#14  opimai_real ()
#15  ssthrdmain ()
#16  main ()
 
 
--==== select test_seq_1.currval from dual;                          
                                
#0   kdncur ()
#1   qersqStart ()
#2   selexe0 ()
#3   opiexe ()
#4   kpoal8 ()
#5   opiodr ()
#6   ttcpip ()
#7   opitsk ()
#8   opiino ()
#9   opiodr ()
#10  opidrv ()
#11  sou2o ()
#12  opimai_real ()
#13  ssthrdmain ()
#14  main ()

Monday, January 31, 2022

Oracle SQL Monitoring Shared Pool "keomg: entry list " Memory Leak

This Blog will try to demonstrate shared pool "keomg: entry list " memory leak when SQL is monitored.
Note 1: Tested in Oracle 19.11 and 19.13 with following settings:
  -- set 3 subpools in shared_pool
  alter system set "_kghdsidx_count"=3 scope=spfile;
  
  -- disable autosga to generate ORA-04031
  alter system set "_memory_imm_mode_without_autosga"=false;
Note 2: The behaviour was first observed by other people in Oracle applications.


1. "keomg: entry list " Memory Leak Test


"keomg: entry list " is used to store SQL Monitoring data. With following tests, we can observe the continuous increase of memory usage only when SQL is monitored.

--================ Test Setup ================--             
drop table keomg_tab1 purge;
create table keomg_tab1 as select level x, rpad('ABC', 20, 'X') y from dual connect by level < 1e3;
create index keomg_tab1#1 on keomg_tab1(x, y);
 
--================ Test 1. WithOut Hint, DELTA=0 ================--
col bytes new_value bytes_before
 
select name, bytes from v$sgastat s where name like 'keomg: entry list%';
select count(*) from keomg_tab1 where y is not null;
select name, bytes, (bytes - &bytes_before) delta from v$sgastat s where name like 'keomg: entry list%';
 
  NAME                    BYTES      DELTA
  ------------------ ---------- ----------
  keomg: entry list     1044480          0
       
--================ Test 2. With MONITOR Hint, DELTA=3072 ================--
select name, bytes from v$sgastat s where name like 'keomg: entry list%';
select /*+ MONITOR */ count(*) from keomg_tab1 where y is not null;
select name, bytes, (bytes - &bytes_before) delta from v$sgastat s where name like 'keomg: entry list%';
 
  NAME                    BYTES      DELTA
  ------------------ ---------- ----------
  keomg: entry list     1047552       3072
      
--================ Test 3. With Parallel 2 Hint, DELTA=9216 ================--
select name, bytes from v$sgastat s where name like 'keomg: entry list%';
select /*+ parallel(2) */ count(*) from keomg_tab1 where y is not null;
select name, bytes, (bytes - &bytes_before) delta from v$sgastat s where name like 'keomg: entry list%';
 
  NAME                    BYTES      DELTA
  ------------------ ---------- ----------
  keomg: entry list     1056768       9216
      
--================ Test 4. With Parallel 16 Hint, DELTA=52224 ================--
select name, bytes from v$sgastat s where name like 'keomg: entry list%';
select /*+ parallel(16) */ count(*) from keomg_tab1 where y is not null;
select name, bytes, (bytes - &bytes_before) delta from v$sgastat s where name like 'keomg: entry list%';
 
  NAME                    BYTES      DELTA
  ------------------ ---------- ----------
  keomg: entry list     1108992      52224
      
--================ Test 5. Index Rebuild Without Parallel Degree, DELTA=0 ================--
select name, bytes from v$sgastat s where name like 'keomg: entry list%';
alter index keomg_tab1#1 rebuild;
select name, bytes, (bytes - &bytes_before) delta from v$sgastat s where name like 'keomg: entry list%';
 
  NAME                    BYTES      DELTA
  ------------------ ---------- ----------
  keomg: entry list     1108992          0
      
--================ Test 6. Index Rebuild With Parallel 2, DELTA=15360 ================--
select name, bytes from v$sgastat s where name like 'keomg: entry list%';
alter index keomg_tab1#1 rebuild parallel 2;
select name, bytes, (bytes - &bytes_before) delta from v$sgastat s where name like 'keomg: entry list%';
alter index keomg_tab1#1 noparallel;
 
  NAME                    BYTES      DELTA
  ------------------ ---------- ----------
  keomg: entry list     1124352      15360
        
--================ Test 7. Index Rebuild With Parallel 16, DELTA=101376 ================--
select name, bytes from v$sgastat s where name like 'keomg: entry list%';
alter index keomg_tab1#1 rebuild parallel 16;
select name, bytes, (bytes - &bytes_before) delta from v$sgastat s where name like 'keomg: entry list%';
alter index keomg_tab1#1 noparallel;
 
  NAME                    BYTES      DELTA
  ------------------ ---------- ----------
  keomg: entry list     1225728     101376
From above tests, we can see memory increase when using MONITOR hint, Parallel hint, and index rebuild with Parallel Degree. The magnitude of increase is proportional to Parallel Degree.

With following queries, we can look SQL statements whose execution have been (or are being) monitored.

-- X$KESWXMON
select * from v$sql_monitor order by last_refresh_time desc;
select * from v$sql_monitor_statname 

-- X$ALL_KESWXMON
select * from gv$all_sql_monitor order by last_refresh_time desc;
select * from gv$sql_plan_monitor order by last_refresh_time desc;
select * from gv$all_sql_plan_monitor order by last_refresh_time desc;
select * from gv$sql_monitor_statname;


2. Oracle Patches


In Oracle 19.13, two Patches are included to fix the problem:
  Bug 32465193 - ORA-4031 Due to high SQL Monitoring allocations in Shared Pool
  Bug 32645139 - ORA-4031 Top 10 Memory Uses for SGA Heap Shows Keomg Allocations
      (Patch 33523677: MERGE ON DATABASE RU 19.12.0.0.0 OF 32465193 32645139)
However, the test on Oracle 19.13 with both patches installed shows the same result as Oracle 19.11. Both above patches have no effect on "keomg: entry list " memory leak.


3. Workaround


Oracle has a few hidden parameters to control the SQL Monitoring:

  Name                         Description                                                                   Default
  ---------------------------  ----------------------------------------------------------------------------  -------
  _dbop_enabled                Any positive number enables automatic DBOP monitoring. 0 is disabled          1     
  _sqlmon_threshold            CPU/IO time threshold before a statement is monitored. 0 is disabled          5     
  _sqlmon_max_plan             Maximum number of plans entry that can be monitored. Defaults to 20 per CPU   120   
  _sqlmon_max_planlines        Number of plan lines beyond which a plan cannot be monitored                  300   
  _sqlmon_binds_xml_format     format of column binds_xml in [G]V$SQL_MONITOR                                default
  _px_load_monitor_threshold   threshold for pushing information to load slave workload monitor              10000 
  _merge_monitor_threshold     threshold for pushing information to MERGE monitoring                         10000 
  _monitor_workload_interval   workload monitoring interval in hours                                         24    
  _sqlmon_recycle_time        Minimum time (in s) to wait before a plan entry can be recycled                      5
One quick workaround is to disable SQL Monitoring (disable/enable are immediate, not need DB restart):

  -- disable
  alter system set "_sqlmon_threshold"=0 scope=both sid='*';
               
  -- enable to default
  alter system set "_sqlmon_threshold"=5 scope=both sid='*';
               
  -- reset also disable
  alter system reset "_sqlmon_threshold" scope=both;
For further information about SQL Monitoring, see Oracle MOS: Monitoring SQL Statements with Real-Time SQL Monitoring (Doc ID 1380492.1)


4. "keomg: entry list " Component Single Subpool Allocation in Shared Pool


In our test DB, we set "_kghdsidx_count"=3 to configure 3 subpools in shared_pool. With following query, we can see "keomg: entry list " is only allocated in one single subpool: Subpool 3. Eventually we will experience the ORA-04031 error due to single subpool memory pressure, similar to "SO private sga" discussed in Blog: Oracle 19c new shared pool "SO private sga" and "SO private so latch" Performance Impacts

-- x$ksmsp lists each memomy chunk (ksmchptr, minimum unit) in each area (ksmchpar) for each component (ksmchcom) in subpool (ksmchidx)
-- x$ksmsp does not contain reserved extents
select ksmchidx subpool, ksmchcom, count(*) row_cnt, sum(ksmchsiz) siz
  from x$ksmsp
 where ksmchcom like 'keomg: entry%'
 group by ksmchidx, ksmchcom;
 
  SUBPOOL  KSMCHCOM         ROW_CNT     SIZ
  -------  ---------------- -------  ------
        3  keomg: entry li      274  848304
       
        
-- x$ksmss (v$sgastat) is about stats of SGA component (ksmssnam) in each subpool (ksmdsidx)
-- ksmdsidx = 0 is for reserved extents
 
select ksmdsidx subpool, ksmssnam, count(*) row_cnt, sum(ksmsslen) siz
  from x$ksmss
 where ksmssnam like 'keomg: entry%'
  group by ksmdsidx, ksmssnam;
 
  SUBPOOL  KSMSSNAM           ROW_CNT     SIZ
  -------  -----------------  -------  ------
        3  keomg: entry list        1  841728


5. ORA-04031 Dump


Following dump is from an Oracle 19.11 hitting ORA-04031 due to "keomg: entry list " single subpool allocation

In this case, "keomg: entry list " is only allocated into "SUB POOL 3", and occupies 50% of "SUB POOL 3" with 2304 MB, hence positioned as TOP consumer.

By the way, we can also see ""SO private sga" (711 MB 14%) is only allocated into "SUB POOL 4".

Subroutine "keswxCurEndPlanMonitoringCb" in ORA-4031 Error Stack points out that xplan SQL Monitor signals no space available in the heap (kghnospc) during memory allocation (kghalo).
       
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.11.0.0.0

Begin 4031 Diagnostic Information
 
Allocation request for: keomg: entry list
Requested from sga heap(3,0), Heap: 0x700000000169c48, size: 3096
******************************************************
HEAP DUMP heap name="sga heap(3,0)" desc=700000000169c48
 
==============================================
TOP 10 MEMORY USES FOR SGA HEAP SUB POOL 3
----------------------------------------------
"keomg: entry list             "  2304 MB 50%
"free memory                   "   752 MB 16%
"db_block_hash_buckets         "   295 MB  6%
"KTC latch subh                "   251 MB  5%
"file queue buckets            "   145 MB  3%
"object queue header free      "   109 MB  2%
"KGLH0                         "   107 MB  2%
"object queue hash buckets     "    74 MB  2%
"KKSSP                         "    66 MB  1%
"kglsim object batch           "    54 MB  1%
     -----------------------------------------
free memory                        750 MB
memory alloc.                     3858 MB
Sub total                         4609 MB
==============================================
 
==============================================
TOP 10 MEMORY USES FOR SGA HEAP SUB POOL 4
----------------------------------------------
"KGLH0                         "   923 MB 18%
"SQLA                          "   902 MB 18%
"SO private sga                "   711 MB 14%
"free memory                   "   570 MB 11%
"db_block_hash_buckets         "   289 MB  6%
"KTC latch subh                "   261 MB  5%
"SQLP                          "   254 MB  5%
"file queue buckets            "   147 MB  3%
"KGLHD                         "   125 MB  2%
"object queue header free      "   108 MB  2%
     -----------------------------------------
free memory                        572 MB
memory alloc.                     4548 MB
Sub total                         5122 MB
==============================================
 
Error Stack: ORA-4031
ksm_ 4031_dump <- ksmasg <- kghallocpdb_swcb <- kgh_invoke_alloc_cb <- kghnospc
<- kghalo <- kghxal <- keomgVarAddBytes <- keswxWriteEndinfoToStream
<- keswxCurEndPlanMonitoringCb <- kxsffir <- kxsFreeWorkArea
<- kxsFreeExecutionHeap <- kksumc <- opiexe <- kpoal8 <- opiodr <- kpoodr <- upirtrc <- kpurcsc
<- kpuexec <- OCIStmtExecute <- jslvec_execcb <- jslvswu <- jslvCDBSwitchUsr
<- jslve_execute0 <- jslve_execute <- jslve_cdb_execute <- rpiswu2 <- kkjexle_cdb
<- kkjsexe <- kkjrdp <- opirip <- opidrv <- sou2o <- opimai_real <- ssthrdmain <- main

Friday, December 17, 2021

How to count the inserts and updates from merge ?

This blog will make two attempts to count the inserts and updates from merge without changing merge statement. One is by programming, another by Oracle stats. There are some discussions on this topic in AskTom: How to count the inserts and updates from merge.

Note: Tested in Oracle 19c


1. Test Setup



drop table source_tab;
drop table target_tab;
 
-- 7000 rows, x from 1 to 7000
create table target_tab as select level x, rpad('TTT', 2000, 'X') y from dual connect by level <= 7000;
 
-- 5000 rows, x from 3001 to 8000
create table source_tab as select 3000+level x, rpad('SSS', 2000, 'X') y from dual connect by level <= 5000;
 
alter table target_tab add (constraint target_tab#p primary key (x));
alter table source_tab add (constraint source_tab#p primary key (x));
 
exec dbms_stats.gather_table_stats(user, 'TARGET_TAB', cascade=> true);
exec dbms_stats.gather_table_stats(user, 'SOURCE_TAB', cascade=> true);


2. Test Run



declare
  l_time          date;
  l_merge_cnt     number;
  l_before_cnt    number;
  l_after_cnt     number;
  l_before_change number;
  l_before_insert number;
  l_after_change  number;
  l_after_insert  number;
  l_upd_oracle    number;
begin
  l_time := sysdate;
  select count(*) into l_before_cnt from target_tab;  -- assume no other DML on target_tab
 
  select b.value into l_before_change from v$statname a, v$mystat b where a.statistic# = b.statistic# and name = 'HSC Heap Segment Block Changes';
  select b.value into l_before_insert from v$statname a, v$mystat b where a.statistic# = b.statistic# and name =  'Heap Segment Array Inserts';
 
  merge /*+ index(a target_tab#p) */ into target_tab a
        --using source_tab b
        using (select /*+ index(t source_tab#p) */ * from source_tab t where x > 4000)  b
    on (a.x = b.x)
    when matched then
      update set a.y       = b.y
    when not matched then
      insert (x, y) values (b.x, b.y);
     
  l_merge_cnt := sql%rowcount;
 
  select b.value into l_after_change from v$statname a, v$mystat b where a.statistic# = b.statistic# and name = 'HSC Heap Segment Block Changes';
  select b.value into l_after_insert from v$statname a, v$mystat b where a.statistic# = b.statistic# and name =  'Heap Segment Array Inserts';
 
  --select count(*) into l_before_cnt from target_tab as of timestamp l_time;  -- sys.smon_scn_time.TIME_DP is DATE type
  select count(*) into l_after_cnt from target_tab;
 
  dbms_output.put_line('Merge sql%rowcount = '||l_merge_cnt);
  dbms_output.put_line('Table Rows before  = '||l_before_cnt);
  dbms_output.put_line('Table Rows after   = '||l_after_cnt);
 
  dbms_output.put_line('============= inserts and updates by Programmer =============');
  dbms_output.put_line('Insert Rows = '|| (l_after_cnt - l_before_cnt));
  dbms_output.put_line('Update Rows = '|| (l_merge_cnt - (l_after_cnt - l_before_cnt)));
 
  dbms_output.put_line('============= inserts and updates by Oracle =============');
  l_upd_oracle := (l_after_change  - l_before_change) - (l_after_insert - l_before_insert);
  dbms_output.put_line('Insert Rows = '|| (l_merge_cnt - l_upd_oracle));
  dbms_output.put_line('Update Rows = '|| l_upd_oracle);
 
  rollback;
end;
/


3. Test Output



Merge sql%rowcount = 4000
Table Rows before  = 7000
Table Rows after   = 8000
 
============= inserts and updates by Programmer =============
Insert Rows = 1000
Update Rows = 3000
 
============= inserts and updates by Oracle =============
Insert Rows = 1000
Update Rows = 3000

Sunday, November 28, 2021

Oracle dbms_aq.dequeue_array Shared Pool "KTC Latch Subh" Memory Leak

This Blog will try to demonstrate shared pool "KTC Latch Subh" memory leak when using AQ array dequeue: dbms_aq.dequeue_array (enqueue_array and dequeue_array introduced in 10g).

Note 1: Tested in Oracle 19c with following settings:
 
  -- disable autosga to generate ORA-04031
  alter system set "_memory_imm_mode_without_autosga"=false;
 
  -- set small shared_pool to quickly hit ORA-04031 due to "KTC latch subh"
  alter system set shared_pool_size=1008M scope=spfile;  
  
  -- set 3 subpools in shared_pool
  alter system set "_kghdsidx_count"=3 scope=spfile; 
 
Note 2: The behaviour was first observed by other people in Oracle applications.


1. "KTC latch subh" Memory Leak Test


At first, we run test_aq_loop (see section 4 "Test Code") to make 3 array_enqueue / array_dequeue calls with array_size: 1000, 2000, and 3000 one after another. Each time the same number of messages are first enqueued and then dequeued.

The output shows that 'KTC latch subh' memory is increased progressively from 0.5 MB to 6 MB. The increase only occurs in Dequeue_Array, but not Enqueue_Array.

SQL > select round(bytes/1024/1024, 2) KTC_MB from v$sgastat where name = 'KTC latch subh';
    KTC_MB
    ------
       .05
 
SQL > exec test_aq_loop('ArrayEnq',  'ArrayDeq',  3, 0, 1e3);
 
  RUN = 1 at 06:27:46
    Enqueue_Array.Size = 1000, KTC latch subh MB = .05
    Dequeue_Array.Size = 1000, KTC latch subh MB = 1.04
    Enqueue_Array.Size = 2000, KTC latch subh MB = 1.05
    Dequeue_Array.Size = 2000, KTC latch subh MB = 3.03
    Enqueue_Array.Size = 3000, KTC latch subh MB = 3.04
    Dequeue_Array.Size = 3000, KTC latch subh MB = 6.01
   
    Elapsed: 00:00:03.34
 
SQL > select round(bytes/1024/1024, 2) KTC_MB from v$sgastat where name = 'KTC latch subh';
    KTC_MB
    ------
      6.01
Then make 3 array_enqueue / array_dequeue calls with 10 times bigger array_size: 10000, 20000, and 30000 one after another.

The output shows that 'KTC latch subh' memory is increased progressively from 6 MB to 55 MB. Again the increase only occurs in Dequeue_Array, but not Enqueue_Array.

SQL > exec test_aq_loop('ArrayEnq',  'ArrayDeq',  3, 0, 1e4);
 
  RUN = 2 at 06:28:41
    Enqueue_Array.Size = 10000, KTC latch subh MB = 6.01
    Dequeue_Array.Size = 10000, KTC latch subh MB = 15.93
    Enqueue_Array.Size = 20000, KTC latch subh MB = 15.94
    Dequeue_Array.Size = 20000, KTC latch subh MB = 25.85
    Enqueue_Array.Size = 30000, KTC latch subh MB = 25.86
    Dequeue_Array.Size = 30000, KTC latch subh MB = 55.61
   
    Elapsed: 00:00:23.01
 
SQL > select round(bytes/1024/1024, 2) KTC_MB from v$sgastat where name = 'KTC latch subh';
 
    KTC_MB
    ------
     55.61
As a third test, make 3 array_enqueue / array_dequeue calls with 100 times bigger array_size: 100000, 200000, and 300000 one after another.

The output shows that 'KTC latch subh' memory reaches 445 MB from 55 MB and process terminated with "ORA-04031: unable to allocate 872 for KTC latch subh". Same as above two tests, the increase only occurs in Dequeue_Array, but not Enqueue_Array.

Only first two Dequeue_Array calls succeeded, the third Dequeue_Array call requires more than 445 MB "KTC latch subh" and no more free memory is available for 'KTC latch subh'.

SQL > exec test_aq_loop('ArrayEnq',  'ArrayDeq',  3, 0, 1e5);
 
  RUN = 3 at 06:30:14
  Enqueue_Array.Size = 100000, KTC latch subh MB = 55.61
  Dequeue_Array.Size = 100000, KTC latch subh MB = 154.79
  Enqueue_Array.Size = 200000, KTC latch subh MB = 154.8
  Dequeue_Array.Size = 200000, KTC latch subh MB = 350.19
  Enqueue_Array.Size = 300000, KTC latch subh MB = 350.19
  BEGIN test_aq_loop('ArrayEnq',  'ArrayDeq',  3, 0, 1e5); END;
 
  *
  ERROR at line 1:
  ORA-04031: unable to allocate 456 bytes of shared memory ("shared pool","select cols,audit$,textlengt...","SQLA^ca34c3df","opixpop:kctdef")
  ORA-06512: at "K.TEST_AQ_LOOP", line 15
  ORA-06512: at "K.TEST_AQ_LOOP", line 58
  ORA-04031: unable to allocate 872 bytes of shared memory ("shared pool","unknown object","KTC latch subh","KTCCC OBJECT")
  ORA-06512: at "SYS.DBMS_AQ", line 1107
  ORA-06512: at "K.TEST_DEQ_ARRAY", line 16
  ORA-06512: at "K.TEST_AQ_LOOP", line 49
  ORA-06512: at line 1
 
  Elapsed: 00:08:11.47
 
SQL > select round(bytes/1024/1024, 2) KTC_MB from v$sgastat where name = 'KTC latch subh';
    KTC_MB
    ------
     445.9
We can also list 'KTC latch subh' KSMCHPAR areas ("DS" or "desc"), each of which contains a set of Chunks. There are 11 such areas (more areas in bigger shared_pool). Average Chunk size is about 4 KB. (above ORA-04031 occurs when unable to allocate 872 bytes).

select ksmchpar, round(sum(ksmchsiz)/1024/1024, 2) mb, count(*), round(avg(ksmchsiz)) avg, min(ksmchsiz) min, max(ksmchsiz) max
  from  x$ksmsp
 where ksmchcom = 'KTC latch subh'
 group by ksmchpar order by mb desc;
 
  KSMCHPAR                 MB   COUNT(*)        AVG        MIN        MAX
  ---------------- ---------- ---------- ---------- ---------- ----------
  00000000934D5B50     199.52      50031       4182        920       4320
  00000000934D58A8     126.21      32822       4032        920       4320
  00000000934D5DF8      99.76      25016       4182       1000       4320
  00000000934D5BD8      19.96       5002       4184       2616       4320
  00000000934D5C60          2        502       4184       4184       4320
  00000000934D5A40          1        254       4148        920       4320
  00000000934D59B8        .01          2       4252       4184       4320
  00000000934D5930        .01          2       4252       4184       4320
  00000000934D5CE8        .01          2       4252       4184       4320
  00000000934D5D70        .01          3       1899        504       4320
  00000000934D5AC8        .01          2       4252       4184       4320
 
  11 rows selected.
To further identify memory usage, we can pick one KSMCHPAR and make a heapdump, for example:

  oradebug dump heapdump_addr 2 0x00000000934D5B50        
The dump file list all "KTC latch subh" Chunks, each of which is noted with "sz= 4144". The most noticeable text is "TEST_Q", which is exactly the queue_name used in our Test Code.

  ******************************************************
  HEAP DUMP heap name="KTC latch subh"  desc=0x934d5b50
   extent sz=0x1040 alt=32767 het=32767 rec=9 flg=0x3 opc=0
   parent=0x601476e0 owner=(nil) nex=(nil) xsz=0x1040 heap=(nil)
   fl2=0x24, fl3=0x0, nex=(nil), idx=1, dur=1, dsxvers=1, dsxflg=0x0
  dsx first ext=0x849428e0
   dsx empty ext bytes=207207648  subheap rc link=0x84942960,0x84942960
   dsx heap size=207211904, dsx heap depth=1
   pdb id=0, src pdb id=0
  EXTENT 0 addr=0x667a9180
    Chunk        0667a9190 sz=     4144    free      "               "
  Dump of memory from 0x00000000667A9190 to 0x00000000667AA1C0
  0667A9190 00001031 D0B38F00 00000000 00000000  [1...............]
  0667A91A0 667AA1F8 00000000 84942930 00000000  [..zf....0)......]
  0667A91B0 01010101 00000000 00000000 00000000  [................]
  0667A91C0 00000000 00000000 00000000 00000000  [................]
  ...
  0667A9EB0 00000000 00000000 00000000 00000000  [................]
          Repeat 6 times
  0667A9F20 00000001 54534554 0000515F 00000000  [....TEST_Q......]
  0667A9F30 00000000 00000000 00000000 00000000  [................]
By the way, above "SQLA^ca34c3df" can be found by:

select hash_value, sql_id, last_active_time, executions, rows_processed, sql_text  --, v.*
  from v$sqlarea v where hash_value = to_number('ca34c3df', 'xxxxxxxxx');
 
  HASH_VALUE SQL_ID        LAST_ACTIVE_TIME     EXECUTIONS ROWS_PROCESSED SQL_TEXT
  ---------- ------------- ---------------- ---------- -------------- ------------------------------------------------------------------------------------------
  3392455647 c1aqra3539hyz         07:25:03          2              2 select cols,audit$,textlength,intcols,nvl(property,0),flags,rowid from view$ where obj#=:1
During the test, we also record the "KTC latch subh" and PGA allocation in table test_aq_array_runs. Here the details about each STEP of every RUN.

We can see KTC_SUBH_MB increase in each ArrayDeq (KTC_DELTA > 0), but not ArrayEnq (KTC_DELTA = 0). Therefore it seems memory leak in dbms_aq.dequeue_array. The memory increased size is almost linear to dequeue array_size.

select d.*, ktc_subh_mb - lag(ktc_subh_mb) over (partition by run order by step) ktc_delta
  from test_aq_array_runs d order by 1, 2, 3;
 
  RUN STEP TIME      ACTION                    ARRAY_SIZE KTC_SUBH_MB PGA_ALLOC_MB SUBH_DETAILS                                              KTC_DELTA
  --- ---- --------  ------------------------- ---------- ----------- ------------ -------------------------------------------------------- ----------
    1    0 06:27:47  ArrayEnq-ArrayDeq Start                        0           11 ksmchcls: (freeabl=0MB, CNT=3); (recr=0MB, CNT=9)
    1    1 06:27:48  ArrayEnq                        1000           0           23 ksmchcls: (freeabl=0MB, CNT=4); (recr=0MB, CNT=9)                 0
    1    1 06:27:48  ArrayDeq                        1000           1           40 ksmchcls: (freeabl=1MB, CNT=256); (recr=0MB, CNT=9)               1
    1    2 06:27:48  ArrayEnq                        2000           1           44 ksmchcls: (freeabl=1MB, CNT=257); (recr=0MB, CNT=9)               0
    1    2 06:27:49  ArrayDeq                        2000           3           58 ksmchcls: (freeabl=3MB, CNT=757); (recr=0MB, CNT=9)               2
    1    3 06:27:49  ArrayEnq                        3000           3           58 ksmchcls: (freeabl=3MB, CNT=757); (recr=0MB, CNT=10)              0
    1    3 06:27:50  ArrayDeq                        3000           6           76 ksmchcls: (freeabl=6MB, CNT=1510); (recr=0MB, CNT=10)             3
 
    2    0 06:28:42  ArrayEnq-ArrayDeq Start                        6           10 ksmchcls: (freeabl=6MB, CNT=1510); (recr=0MB, CNT=10)
    2    1 06:28:44  ArrayEnq                       10000           6          120 ksmchcls: (freeabl=6MB, CNT=1511); (recr=0MB, CNT=10)             0
    2    1 06:28:45  ArrayDeq                       10000          16          202 ksmchcls: (freeabl=16MB, CNT=4011); (recr=0MB, CNT=10)           10
    2    2 06:28:50  ArrayEnq                       20000          16          218 ksmchcls: (freeabl=16MB, CNT=4011); (recr=0MB, CNT=11)            0
    2    2 06:28:53  ArrayDeq                       20000          26          380 ksmchcls: (freeabl=26MB, CNT=6511); (recr=0MB, CNT=11)           10
    2    3 06:29:00  ArrayEnq                       30000          26          380 ksmchcls: (freeabl=26MB, CNT=6512); (recr=0MB, CNT=11)            0
    2    3 06:29:04  ArrayDeq                       30000          56          562 ksmchcls: (freeabl=56MB, CNT=14013); (recr=0MB, CNT=11)          30
 
    3    0 06:30:14  ArrayEnq-ArrayDeq Start                       56           10 ksmchcls: (freeabl=56MB, CNT=14013); (recr=0MB, CNT=11)
    3    1 06:30:43  ArrayEnq                      100000          56          981 ksmchcls: (freeabl=56MB, CNT=14013); (recr=0MB, CNT=11)           0
    3    1 06:31:09  ArrayDeq                      100000         155         1817 ksmchcls: (freeabl=156MB, CNT=39027); (recr=0MB, CNT=11)         99
    3    2 06:32:19  ArrayEnq                      200000         155         1961 ksmchcls: (freeabl=156MB, CNT=39028); (recr=0MB, CNT=11)          0
    3    2 06:33:52  ArrayDeq                      200000         350         3595 ksmchcls: (freeabl=352MB, CNT=88304); (recr=0MB, CNT=11)        195
    3    3 06:36:00  ArrayEnq                      300000         350         2893 ksmchcls: (freeabl=352MB, CNT=88305); (recr=0MB, CNT=11)          0
Above output shows that all allocated memory are with Chunk type: "freeabl" (Freeable), no Chunk type "recr" (Recreatable). But they cannot be evicted even with flushing shared_pool ("alter system flush shared_pool"). The last resort is DB restart.

Chunk types are documented in Oracle MOS: "Troubleshooting and Diagnosing ORA-4031 Error [Video] (Doc ID 396940.1)"

  Chunk types:
 
    Normal (freeable) chunks - These chunks are allocated in such a way that the user can explicitly free
    the chunk once they have finished with the memory.
   
    Free chunks - These chunks are free and available for reuse should a request come into the pool for
    this chunk size or smaller.
   
    Recreatable chunks - This is a special form of "freeable" memory.  These chunks are placed on an
    LRU list when they are unpinned.   If memory is needed, we go to the LRU list and free "recreatable"
    memory that hasn't been used for a while.
    
    Permanent chunks - These chunks can be allocated in different ways.   Some chunks are allocated
    and will remain in use for the "life" of the instance.   Some "permanent" chunks are allocated but can
    be used over and over again internally as they are available.


2. ORA-04031 incident file


In ORA-04031 incident file, Call Stack shows that ORA-04031 is raised in "ktccAddCbkObj" calling to "kghalo", which hit no space "kghnospc".

----- Call Stack -----                                                     
 FRAME [1]  (ksedst1()+95 -> kgdsdst())                                                 
 FRAME [2]  (ksedst()+58 -> ksedst1())                                                  
 FRAME [3]  (dbkedDefDump()+23448 -> ksedst())                                          
 FRAME [4]  (ksedmp()+577 -> dbkedDefDump())                                            
 FRAME [5]  (dbgexPhaseII()+2092 -> ksedmp())                                           
 FRAME [6]  (dbgexExplicitEndInc()+285 -> dbgexPhaseII())                               
 FRAME [7]  (dbgeEndDDEInvocationImpl()+314 -> dbgexExplicitEndInc())  
                  
 FRAME [8]  (kghnospc()+4787 -> dbgeEndDDEInvocationImpl())                              
 FRAME [9]  (kghalo()+4255 -> kghnospc())                                               
 FRAME [10] (ktccAddCbkObj()+487 -> kghalo())                                           
 FRAME [11] (kwqidracbk()+255 -> ktccAddCbkObj())                                      
 FRAME [12] (kwqidcpmc()+2716 -> kwqidracbk())                                         
 FRAME [13] (kwqidafm0()+6345 -> kwqidcpmc())                                           
 FRAME [14] (kwqididqx()+2368 -> kwqidafm0())                                          
 FRAME [15] (kwqideqarr()+2295 -> kwqididqx())                                         
 FRAME [16] (kwqideqarr0()+49 -> kwqideqarr())                                         
 FRAME [17] (spefcifa()+1286 -> kwqideqarr0())                                         
 FRAME [18] (spefmccallstd()+436 -> spefcifa())                                        
 FRAME [19] (peftrusted()+139 -> spefmccallstd())                                      
 FRAME [20] (psdexsp()+280 -> peftrusted())                                            
 FRAME [21] (rpiswu2()+2077 -> psdexsp())                                              
 FRAME [22] (kxe_push_env_internal_pp_()+362 -> rpiswu2())                             
 FRAME [23] (kkx_push_env_for_ICD_for_new_session()+149 -> kxe_push_env_internal_pp_())
 FRAME [24] (psdextp()+387 -> kkx_push_env_for_ICD_for_new_session())                  
 FRAME [25] (pefccal()+663 -> psdextp())                                               
 FRAME [26] (pefcal()+223 -> pefccal())                                                
 FRAME [27] (pevm_FCAL()+90 -> pefcal())                                               
 FRAME [28] (pfrinstr_FCAL()+62 -> pevm_FCAL())                                        
 FRAME [29] (pfrrun_no_tool()+52 -> pfrinstr_FCAL())                                    
 FRAME [30] (pfrrun()+902 -> pfrrun_no_tool())                                         
 FRAME [31] (plsql_run()+1498 -> pfrrun())                                             
In fact, kghalo first calls kghfnd_in_free_lists, but no space found, thus jump to kghnospc (no space found).

  #0  0x0000000004e19e5d in kghfnd_in_free_lists ()
  #1  0x0000000004e177ad in kghalo ()
  #2  0x0000000001d1ccf7 in ktccAddCbkObj ()
ORA-04031 incident file also shows the detail memory allocation in each subpools (we set "_kghdsidx_count"=3 in our test), where "KTC latch subh" are ranked as the highest.

By the way, three occurrences of "SO private sga" in each of three subpools showed that the bug revealed in Blog: Oracle 19c new shared pool "SO private sga" and "SO private so latch" Performance Impacts is fixed in this test DB.

  ==============================================
  TOP 10 MEMORY USES FOR SGA HEAP SUB POOL 1
  ----------------------------------------------
  "KTC latch subh                 "   198 MB 48%
  "free memory                    "    69 MB 17%
  "FileIdentificatonBlock         "    13 MB  3%
  "ksunfy_meta 1                  "  9324 KB  2%
  "SQLA                           "  8702 KB  2%
  "SO private sga                 "  8223 KB  2%
  "KGLH0                          "  6878 KB  2%
  "db_block_hash_buckets          "  5440 KB  1%
  "private strands                "  5187 KB  1%
  "KGLS                           "  4221 KB  1%
      
  ==============================================
  TOP 10 MEMORY USES FOR SGA HEAP SUB POOL 2
  ----------------------------------------------
  "KTC latch subh                 "   146 MB 46%
  "free memory                    "    51 MB 16%
  "ksunfy_meta 1                  "  9324 KB  3%
  "SO private sga                 "  8777 KB  3%
  "ksipc state object             "  7602 KB  2%
  "KSRMA State Object             "  5705 KB  2%
  "db_block_hash_buckets          "  5504 KB  2%
  "private strands                "  5320 KB  2%
  "ASH buffers                    "  4096 KB  1%
  "KSFD SGA I/O b                 "  4092 KB  1%
      
  ==============================================
  TOP 10 MEMORY USES FOR SGA HEAP SUB POOL 3
  ----------------------------------------------
  "KTC latch subh                 "   101 MB 37%
  "free memory                    "    45 MB 17%
  "ksunfy_meta 1                  "  9324 KB  3%
  "SO private sga                 "  8925 KB  3%
  "SQLA                           "  7807 KB  3%
  "KGLH0                          "  6938 KB  2%
  "db_block_hash_buckets          "  5444 KB  2%
  "private strands                "  5187 KB  2%
  "PLMCD                          "  4619 KB  2%
  "ASH buffers                    "  4096 KB  1%
In incident file, we can also find above heapdump KSMCHPAR: desc=0x934d5b50, which is marked as "ds=0x934d5b50".

For "KSMCHPAR = 00000000934D5B50" in above x$ksmsp query, size (199.52 MB) and chunk count (50031) is noted as "sz=209213168 ct= 50031" in incident file.

  Chunk        0667a9168 sz=     4184    freeable  "KTC latch subh "  ds=0x934d5b50
  Chunk        0667aa1c0 sz=     4184    freeable  "KTC latch subh "  ds=0x934d5b50
  ...
  Chunk        084677d58 sz=     4184    freeable  "KTC latch subh "  ds=0x934d5b50
       ds        0934d5b50 sz=209213168 ct=    50031
Pick one Chunk addr, for example, 0667a9168, with following query, it can be found by "lower(ksmchptr) like '%0667a9168'" in x$ksmsp.

select * from x$ksmsp
 where ksmchcom = 'KTC latch subh' and ksmchpar = '00000000934D5B50' and lower(ksmchptr) like '%0667a9168';   
 
  ADDR               INDX INST_ID CON_ID   KSMCHIDX KSMCHDUR KSMCHCOM       KSMCHPTR         KSMCHSIZ KSMCHCLS KSMCHTYP KSMCHPAR
  ---------------- ------ ------- ------ ---------- -------- -------------- ---------------- -------- -------- -------- ----------------
  00007F05EBE80DF8 235272       1      0          1        1 KTC latch subh 00000000667A9168     4184 freeabl         0 00000000934D5B50


3. Other enqueue/dequeue Cases


If we make message "single enqueue/single dequeue" or "array enqueue/ single dequeue", there are no 'KTC latch subh' memory leak observed.

  exec test_aq_loop('SingleEnq', 'SingleDeq', 3, 0, 1e3);
  exec test_aq_loop('ArrayEnq',  'SingleDeq', 3, 0, 1e3);
But with "single enqueue/array dequeue", we can also observe the similar 'KTC latch subh' memory leak as "array enqueue/array dequeue".

  exec test_aq_loop('SingleEnq', 'ArrayDeq',  3, 0, 1e3);
In conclusion, this Blog demonstrated "KTC Latch Subh" memory leak in dbms_aq.dequeue_array.


4. Test Code


----==================== 1. AQ Setup====================----
 
begin sys.dbms_aqadm.drop_queue_table(queue_table => 'TEST_QTAB', force=> TRUE); end;
/
 
drop type payload_rec force;
 
create or replace noneditionable type payload_rec force as object(r1 number, r2 varchar2(10))
/
 
drop type test_payload force;
 
create or replace noneditionable type test_payload is object (id number, txt varchar2(1000), lob clob, rec payload_rec)
/                                                                             
 
drop type test_payload_array force;
 
create  or replace noneditionable type test_payload_array as table of test_payload;
/
 
begin
  sys.dbms_aqadm.create_queue_table
   (queue_table            => 'TEST_QTAB'
    ,queue_payload_type    => 'TEST_PAYLOAD'
    ,compatible            => '10.0.0'  --'8.1'
    ,sort_list             => 'PRIORITY,ENQ_TIME'
    ,multiple_consumers    =>  false
    ,message_grouping      =>  0
    ,comment               =>  'KS Test Queue Table'
    ,secure                =>  false);
end;
/
 
begin
  sys.dbms_aqadm.stop_queue ( queue_name => 'TEST_Q');
  sys.dbms_aqadm.drop_queue ( queue_name => 'TEST_Q');
end;
/
 
begin
  sys.dbms_aqadm.create_queue
   (queue_name          => 'TEST_Q'
   ,queue_table         => 'TEST_QTAB'
   ,queue_type          =>  sys.dbms_aqadm.normal_queue
   ,max_retries         =>  100
   ,retry_delay         =>  2
   ,retention_time      =>  604800
   ,comment             => 'KS Test Queue');
end;
/
 
begin sys.dbms_aqadm.start_queue(queue_name => 'TEST_Q', enqueue => true, dequeue => true); end;
/

----==================== 2. Single Enqueue / Dequeue ====================----
 
create or replace procedure test_enq_single(p_id number := 1) as
  l_enqueue_options          dbms_aq.enqueue_options_t;
  l_message_properties       dbms_aq.message_properties_t;
  l_payload_rec              payload_rec;
  l_payload                  test_payload;
  l_message_handle           raw(16);
 
  l_array_msg_properties     dbms_aq.message_properties_array_t := dbms_aq.message_properties_array_t();
  l_array_payloads           test_payload_array := new test_payload_array();
  l_array_msg_ids            dbms_aq.msgid_array_t;
  l_array_errors             dbms_aq.error_array_t;
  l_enq_cnt                  number;
begin
  l_payload_rec := payload_rec(p_id, 'XXXYYY');
  l_payload     := test_payload(p_id, rpad('ABC', 1000, 'X'), rpad('CBA', 7000, 'X'), l_payload_rec);
 
  dbms_aq.enqueue(queue_name         => 'TEST_Q',
                  enqueue_options    => l_enqueue_options,
                  message_properties => l_message_properties,
                  payload            => l_payload,
                  msgid              => l_message_handle);
  commit;
end;
/             
 
-- exec test_enq_single(1);
 
create or replace procedure test_deq_single(p_dur number := 1) as
  l_dequeue_options       dbms_aq.dequeue_options_t;
  l_message_properties    dbms_aq.message_properties_t;
  l_payload               test_payload;
                l_message_handle        raw(16);
begin
                l_dequeue_options.wait := p_dur;
               
  dbms_aq.dequeue(queue_name         => 'TEST_Q',
                  dequeue_options    => l_dequeue_options,
                  message_properties => l_message_properties,
                  payload            => l_payload,
                  msgid              => l_message_handle);
       
  --dbms_output.put_line ('MSG id : ' || l_payload.id);
  commit;
 
  exception when others then dbms_output.put_line ('Error: ' || SQLERRM);
end;
/
                                                                                                               
-- exec test_deq_single();
 
create or replace procedure test_enq_single_loop (p_cnt number, p_id number := 1) as
  l_subh_mb                  number;
begin
  for i in 1..p_cnt loop
    test_enq_single(i);
  end loop;
  select round(sum(bytes)/1024/1024, 2) into l_subh_mb from v$sgastat s where name = 'KTC latch subh';
  dbms_output.put_line ('SingleEnqueCNT = '||p_cnt||', KTC latch subh MB = '||l_subh_mb);
end;
/
 
-- exec test_enq_single_loop(10);
 
create or replace procedure test_deq_single_loop (p_cnt number, p_dur number := 1) as
  l_subh_mb                  number;
begin
  for i in 1..p_cnt loop
    test_deq_single(p_dur);
  end loop;
  select round(sum(bytes)/1024/1024, 2) into l_subh_mb from v$sgastat s where name = 'KTC latch subh';
  dbms_output.put_line ('SingleDequeCNT = '||p_cnt||', KTC latch subh MB = '||l_subh_mb);
end;
/
 
-- exec test_deq_single_loop(10);

----==================== 3. Array Enqueue / Dequeue ====================----
 
create or replace procedure test_enq_array(p_array_size number := 1) as
  l_enqueue_options          dbms_aq.enqueue_options_t;
  l_message_properties       dbms_aq.message_properties_t;
  l_array_msg_properties     dbms_aq.message_properties_array_t := dbms_aq.message_properties_array_t();
  l_payload_rec              payload_rec;
  l_array_payloads           test_payload_array := new test_payload_array();
  l_array_msg_ids            dbms_aq.msgid_array_t;
  l_array_errors             dbms_aq.error_array_t;
  l_enq_cnt                  number;
  l_subh_mb                  number;
begin
  for i in 1..p_array_size loop
    l_payload_rec :=  payload_rec(i, 'XXXYYY');
    l_array_payloads.extend;
    l_array_payloads(l_array_payloads.last) := test_payload(i, rpad('ABC', 1000, 'X'), rpad('CBA', 7000, 'X'), l_payload_rec);
    l_array_msg_properties.extend;
    l_array_msg_properties(l_array_msg_properties.last) := l_message_properties;
  end loop;
 
  l_enq_cnt := dbms_aq.enqueue_array(
                       queue_name               => 'TEST_Q',
                       enqueue_options          => l_enqueue_options,
                       array_size               => l_array_payloads.count,
                       message_properties_array => l_array_msg_properties,
                      payload_array            => l_array_payloads,
                       msgid_array              => l_array_msg_ids,
                       error_array              => l_array_errors);
  commit;
 
  select round(sum(bytes)/1024/1024, 2) into l_subh_mb from v$sgastat s where name = 'KTC latch subh';
  dbms_output.put_line('Enqueue_Array.Size = '||l_enq_cnt||', KTC latch subh MB = '||l_subh_mb);
end;
/                             
 
-- exec test_enq_array(3);
 
create or replace procedure test_deq_array(p_array_size number := 1, p_sleep number := 5) as
   l_dequeue_options       dbms_aq.dequeue_options_t;
   l_array_msg_properties  dbms_aq.message_properties_array_t;
   l_array_payloads        test_payload_array;
   l_array_msg_ids         dbms_aq.msgid_array_t;
   l_deq_cnt               number;
   l_subh_mb                  number;
begin
   l_array_payloads := test_payload_array();
   l_array_payloads.extend(p_array_size);
   l_array_msg_properties := dbms_aq.message_properties_array_t();
   l_array_msg_properties.extend(p_array_size);
   l_array_msg_ids := dbms_aq.msgid_array_t();
   l_dequeue_options.wait := p_sleep;
 
   l_deq_cnt := dbms_aq.dequeue_array(
                        queue_name               => 'TEST_Q',
                        dequeue_options          => l_dequeue_options,
                        array_size               => p_array_size,
                        message_properties_array => l_array_msg_properties,
                        payload_array            => l_array_payloads,
                        msgid_array              => l_array_msg_ids);
  
   select round(sum(bytes)/1024/1024, 2) into l_subh_mb from v$sgastat s where name = 'KTC latch subh';
   dbms_output.put_line('Dequeue_Array.Size = '||l_deq_cnt||', KTC latch subh MB = '||l_subh_mb);
   commit;
  
    --for i in 1..l_deq_cnt loop
    --  dbms_output.put_line ('Payload id: ' || l_array_payloads(i).id);
    --end loop;
end;
/
 
-- exec test_deq_array(3);

----==================== 4. Loop Test Setup and Test Outcome Recording ====================----
drop table test_aq_array_runs;
 
create table test_aq_array_runs(run number, step number, time date, action varchar2(30), array_size number,
                                ktc_subh_mb number, pga_alloc_mb number, subh_details varchar2(300));
 
 

----==================== 5. Array-Single Enqueue / Dequeue Loop ====================----
 
create or replace procedure test_aq_loop(p_enq_mode varchar2, p_deq_mode varchar2, p_steps number := 1, p_base_size number := 0, p_delta_size number := 100000) as
  l_array_size  number;
  l_run         number := 0;
  l_step        number := 0;
  l_purge_opt   dbms_aqadm.aq$_purge_options_t;
 
  procedure save_stats (p_name varchar2) as
    l_subh_mb         number;
    l_pga_mb          number;
    l_subh_details    varchar2(300);
  begin
    select round(sum(bytes)/1024/1024) into l_subh_mb
      from v$sgastat s where name = 'KTC latch subh';
     
    select round(pga_alloc_mem/1024/1024) into l_pga_mb
      from v$session s, v$process p where s.paddr=p.addr and s.sid in (select sid from v$mystat where rownum=1);
     
   select 'ksmchcls: '|| listagg(subh_areas, '; ') within group (order by subh_areas) into l_subh_details
     from (select '('||ksmchcls||'='||round(sum(ksmchsiz)/1024/1024)||'MB, CNT='||count(*)||')' subh_areas
             from sys.x_ksmsp where lower(ksmchcom) like 'ktc%subh' group by ksmchcls);
  
   insert into test_aq_array_runs values (l_run, l_step, sysdate, p_name, l_array_size, l_subh_mb, l_pga_mb, l_subh_details);
   commit;
  end;
begin
  --l_purge_opt.block := true;
  --dbms_aqadm.purge_queue_table(queue_table => 'TEST_QTAB', purge_condition => null, purge_options => l_purge_opt);
  --select state, count(*) from test_qtab where q_name = 'TEST_Q' group by state;  -- 0 READY, 1 WAITING, 2 RETAINED or PROCESSED, 3 EXPIRED
 
  select nvl(max(run), 0) + 1 into l_run from test_aq_array_runs;
  dbms_output.put_line ('RUN = '||l_run||' at '||sysdate);
  save_stats(p_enq_mode||'-'||p_deq_mode||' Start');
 
  for i in 1..p_steps loop
    l_step       := i;
    l_array_size := p_base_size + (l_step*p_delta_size);
   
    -- Enqueue Array or Single
    if p_enq_mode = 'ArrayEnq' then
      test_enq_array(l_array_size);
      save_stats('ArrayEnq');
    else
      test_enq_single_loop(l_array_size);
      save_stats('SingleEnq');
    end if;
   
    -- Dequeue Array or Single
    if p_deq_mode =  'ArrayDeq' then
      test_deq_array(l_array_size);
      save_stats('ArrayDeq');
    else
      test_deq_single_loop(l_array_size);
      save_stats('SingleDeq');     
    end if;
  end loop;
 
  exception when others then
    save_stats('Error');
    raise;
end;
/
 
 
-- exec test_aq_loop('SingleEnq', 'SingleDeq', 3, 0, 1e1);
-- exec test_aq_loop('ArrayEnq',  'SingleDeq', 3, 0, 1e1);
-- exec test_aq_loop('SingleEnq', 'ArrayDeq',  3, 0, 1e1);
-- exec test_aq_loop('ArrayEnq',  'ArrayDeq',  3, 0, 1e1);