We got few partitioned tables (range partitioned by month) and use pl/sql procedure to update the date column so that the data will move from default partitions to month specific partition.
When the application run 15+ concurrent instances of the pl/sql procedure randomly we get the dead lock reported. Attached are few lines from the deadlock trace
Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TX-00140029-0013c58f 39 218 X 36 216 S
TX-00180024-000a720c 36 216 X 39 218 S
session 218: DID 0001-0027-00003699 session 216: DID 0001-0024-00003A68
session 216: DID 0001-0024-00003A68 session 218: DID 0001-0027-00003699
Rows waited on:
Session 216: no row
Session 218: obj - rowid = 00000000 - D/////AAFAAAANpAAA
(dictionary objn - 0, file - 5, block - 873, slot - 0)
Information on the OTHER waiting sessions:
Session 216:
pid=36 serial=52171 audsid=3549600 user: 18/<none>
O/S info: user: jfedba, term: , ospid: 439194, machine: pqs1s
program: archive_doc_file@pqs1s (TNS V1-V3)
application name: archive_doc_file@pqs1s (TNS V1-V3), hash value=0
Current SQL Statement:
UPDATE MAILPIECE PARTITION(MAILPIECE_DEF) SET ARCHIVED_DT=:B2 WHERE DOC_ID=:B1
End of information on OTHER waiting sessions.
Current SQL statement for this session:
UPDATE MAILPIECE PARTITION(MAILPIECE_DEF) SET ARCHIVED_DT=:B2 WHERE DOC_ID=:B1
......
SO: 700000a0f317ae0, type: 4, owner: 700000a0c27dbb8, flag: INIT/-/-/0x00
(session) sid: 218 trans: 7000009ffaaf938, creator: 700000a0c27dbb8, flag: (80100041) USR/- BSY/-/-/-/-/-
DID: 0001-0027-00003699, short-term DID: 0000-0000-00000000
txn branch: 0
oct: 6, prv: 0, sql: 700000a0d3d58c0, psql: 700000a0ea20718, user: 18/<none>
O/S info: user: jfedba, term: , ospid: 262728, machine: pqs1s
program: archive_doc_file@pqs1s (TNS V1-V3)
application name: archive_doc_file@pqs1s (TNS V1-V3), hash value=0
last wait for 'enq: TX - allocate ITL entry' blocking sess=0x700000a0f316630 seq=2188 wait_time=2929726 seconds since wait start
ed=33
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
Dumping Session Wait History
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929726
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929728
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929725
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929730
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929809
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929764
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929725
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929726
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929727
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
for 'enq: TX - allocate ITL entry' count=1 wait_time=2929729
name|mode=54580004, usn<<16 | slot=180024, sequence=a720c
After reviewing this trace & related posts from asktom/oracle site we increased initrans value to 15 for the default partitions, considering we could have 15 concurrent sessions of pl/sql procedure. The other options suggested on your post is to increase pctfree of the block so we could have move space for ITL growth.
Also in past PL/SQL procedure is designed to commit every 100k records, currently i removed the intermittent commits and perform single commit before exit of procedure.
My question is
1) Currently application is not sure about the number of concurrent transaction, this being said do you suggest to increase initrans or increase the pctfree from default 10% to higher value?
2) Opportunity of improvement on the pl/sql procedure. Code as attached
CREATE OR REPLACE PROCEDURE MACS.PROC_ARCHIVE_DOC_FILE (
update_cnt OUT int, /* Number of doc records updated */
in_doc_file_id IN doc_file.doc_file_id%TYPE,
in_process_status_cd IN doc_file.current_status_cd%TYPE, /* Can change it to something besides ARC... */
in_archived_dt IN doc_file.archived_dt%TYPE /* Can change it to something besides sysdate... */
)
IS
null_input_exc EXCEPTION;
cursor mycursor is
select doc_id
from doc partition(doc_def)
where doc_file_id = in_doc_file_id;
my_doc_id doc.doc_id%TYPE;
BEGIN
IF in_doc_file_id IS NULL or in_process_status_cd IS NULL THEN
RAISE null_input_exc;
END IF;
update_cnt := 0;
/**********************************/
/* Looping through all docs */
/**********************************/
OPEN mycursor;
LOOP
FETCH mycursor INTO my_doc_id;
EXIT WHEN mycursor%NOTFOUND;
/**********************************/
/* Archiving all doc_cic with doc_id */
/**********************************/
IF in_process_status_cd = 'NOP'
THEN
update doc_cic partition(doc_cic_def)
set archived_dt=in_archived_dt,
suspended_reason_cd='NOP'
where doc_id=my_doc_id;
ELSE
update doc_cic partition(doc_cic_def)
set archived_dt=in_archived_dt
where doc_id=my_doc_id;
END IF;
/**********************************/
/* Archiving all doc_dtl with doc_id */
/**********************************/
update doc_dtl partition(doc_dtl_def)
set archived_dt=in_archived_dt
where doc_id=my_doc_id;
/**********************************/
/* Archiving all doc_addr with doc_id */
/**********************************/
update doc_addr partition(doc_addr_def)
set archived_dt=in_archived_dt
where doc_id=my_doc_id;
/**********************************/
/* Archiving each sub_doc */
/**********************************/
update sub_doc partition(sub_doc_def)
set archived_dt=in_archived_dt
where doc_id=my_doc_id;
/**********************************/
/* Archiving each mailpiece */
/**********************************/
if in_process_status_cd != 'ARC'
then
update mailpiece partition(mailpiece_def)
set archived_dt=in_archived_dt,
process_status_cd=in_process_status_cd
where doc_id=my_doc_id;
Else
update mailpiece partition(mailpiece_def)
set archived_dt=in_archived_dt
where doc_id=my_doc_id;
END IF;
/**********************************/
/* Archiving each doc */
/**********************************/
update doc partition(doc_def)
set current_status_cd=in_process_status_cd,
archived_dt=in_archived_dt
where doc_id=my_doc_id;
update_cnt := update_cnt + 1;
END LOOP;
CLOSE mycursor;
/**********************************/
/* Archiving doc_file with doc_file_id */
/* Doing this last... If done, then entire file is complete */
/**********************************/
update doc_file
set archived_dt=in_archived_dt,
current_status_cd=in_process_status_cd
where doc_file_id=in_doc_file_id;
commit;
EXCEPTION
WHEN null_input_exc
THEN
update_cnt := 0;
END PROC_ARCHIVE_DOC_FILE;
1) if you have 15 running, you have 15 transactions, having 15 be the Initrans size would more than cover you. You would use pctfree to reserve space for subsequent updates if necessary.
2) let us remove the slow by slow code.....
Instead of all of the updates in the loop, I would evaluate just doing:
update doc_dtl partition(doc_dtl_def)
set archived_dt=in_archived_dt
where doc_id IN (select doc_id
from doc partition(doc_def)
where doc_file_id = in_doc_file_id );
using "as of" on the IN subquery if doc is being modified as you process and you need a consistent read on it across all of the updates.