Locking behavior of compound triggers
Date: Wed, 12 Sep 2012 16:19:20 -0500
Message-ID: <5050FC58.80001_at_multiservice.com>
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> _at_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> _at_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.
-- http://www.freelists.org/webpage/oracle-lReceived on Wed Sep 12 2012 - 16:19:20 CDT