Locking behavior of compound triggers

  • From: Ryan January <rjjanuary@xxxxxxxxxxxxxxxx>
  • To: oracle-l <oracle-l@xxxxxxxxxxxxx>
  • Date: Wed, 12 Sep 2012 16:19:20 -0500

Hello all,
We recently completed an application upgrade which has caused 
significant enq: TM contention during a number of batch processes. A 
part of this upgrade was a migration away from individual 
insert/update/delete triggers on key tables, moving to a common compound 
trigger.
I've isolated the cause and duplicated this behavior in the code at the 
bottom of this email, however I don't fully understand why it's 
occurring. All tests were performed on a single instance of a 3 node RAC 
11.2.0.3.3 DB on RHEL5.
 >>I have two tables:

parent
     parent_id: INT, PK
     name:      VARCHAR

child
     child_id:    INT, PK
     parent_id:   INT, FK TO PARENT.PARENT_ID

 >>with the following values:

SQL> select * from parent;

  PARENT_ID NAME
---------- ------------------------------
        100 hello
        200 world

SQL> select * from child;

   CHILD_ID  PARENT_ID
---------- ----------
        101      100
        200      200

When a new parent record is created there is a high likelihood that the 
primary key value will be inserted null.  If the value is null, the 
trigger substitutes the next value from a sequence. The update and 
delete portions of the trigger I've found to be insignificant and are 
not included.


 >>as a test, I perform the following statements in separate sessions:

 >>SESSION1:

SQL> update child set child_id = 101 where child_id = 100;

1 row updated.

SQL> @mylocks;

        SID OBJECT_NAME OBJECT_TYPE STATUS    MODE_HELD
---------- ----------- ----------- ------------ ---------------
        267 CHILD       TABLE       Global    Row-X (SX)


 >>SESSION2:

SQL> update parent set name = 'testme' where 1 = 2; -- note that this is 
an UPDATE statement, and should always update no rows

(AT THIS POINT SESSION2 HANGS)

 >>SESSION1:

SQL> @mylocks;

        SID OBJECT_NAME OBJECT_TYPE STATUS    MODE_HELD
---------- ----------- ----------- ------------ ---------------
         52 CHILD       TABLE       Blocking    None
         52 CHILD       TABLE       Not Blocking None
         52 PARENT      TABLE       Global    Row-X (SX)
        267 CHILD       TABLE       Blocking    Row-X (SX)
        267 CHILD       TABLE       Not Blocking Row-X (SX)


 From what I've determined,  session2 is blocked due to an attempt to 
obtain a lock on the child table. This lock will cause a wait due to 
session1's uncommitted change.
Using a little "guess and check" I've determined this behavior is 
triggered by the existence of a "new.primary_key" reference in *any* 
portion of the compound trigger.  This includes one that is not 
executed. If we're requiring a lock I can understand the wait 
(considering the FK'd column in the child table is unindexed in this test.)
What I can't even begin to comprehend is why the lock needs to be 
obtained to begin with. In my example I am executing the code path for 
an update, and the reference requiring the lock is only executed on 
insert. This code also appeared to work well as individual triggers.  
This behavior only appears to manifest itself when using compound triggers.

Short term, we have added additional indexes to FK'd columns to reduce 
contention for some immediate relief.  The reason they were unindexed to 
begin with was that the PK of the parent table is almost never updated.  
As such we would likely never benefit from the index overhead.

Long term, I would like to understand why this lock occurs at all.  I 
don't feel that the index is a full solution since we're pushing our 
locks to the row level, rather than eliminating them completely.  If we 
run into an issue with a highly skewed parent_id value the contention 
could surface again.
If anyone could help explain why the locks are occurring, if it is to be 
expected, or if it's a bug I would greatly appreciate it. So far I've 
had 2 SR's open with Oracle and neither have helped us answer these 
questions.

Thank you,
Ryan


--------------------------------------------------------
--  test tables
--------------------------------------------------------
   CREATE TABLE PARENT(
     PARENT_ID NUMBER,
     NAME VARCHAR2(30),
   constraint "PARENT_PK" PRIMARY KEY (PARENT_ID)
    );

   CREATE TABLE CHILD(
     CHILD_ID NUMBER,
     PARENT_ID NUMBER,
   CONSTRAINT CHILD_PK PRIMARY KEY (CHILD_ID),
   CONSTRAINT CHILD_PARENT_FK FOREIGN KEY (PARENT_ID) REFERENCES 
PARENT(PARENT_ID)
    );

--------------------------------------------------------
--  supporting seq for parent table
--------------------------------------------------------
   CREATE SEQUENCE PARENT_SEQ START WITH 1 INCREMENT BY 1;
/
--------------------------------------------------------
--  create offending trigger
--------------------------------------------------------
create or replace TRIGGER PARENT_CT
   FOR INSERT OR UPDATE OR DELETE ON "PARENT" COMPOUND TRIGGER
   BEFORE EACH ROW IS
   BEGIN
       IF  INSERTING THEN
           IF  :new.parent_id IS NULL THEN
               :new.parent_id := PARENT_SEQ.NEXTVAL;
           END IF;
       ELSIF   UPDATING THEN
           IF  :new.name = 'ryan' then
               :new.name := 'nayr';
           END IF;
       END IF;
   END BEFORE EACH ROW;
END PARENT_CT;
/

--------------------------------------------------------
--  insert dummy values
--------------------------------------------------------
insert into parent (parent_id, name) values (100, 'hello');
insert into parent (parent_id, name) values (200, 'world');
insert into child (child_id, parent_id) values (100, 100);
insert into child (child_id, parent_id) values (200, 200);


---------------------------------------------------------------------------------------
This email is intended solely for the use of the addressee and may
contain information that is confidential, proprietary, or both.
If you receive this email in error please immediately notify the
sender and delete the email.
---------------------------------------------------------------------------------------


--
//www.freelists.org/webpage/oracle-l


Other related posts:

  • » Locking behavior of compound triggers - Ryan January