DEV Community

Vahid Yousefzadeh
Vahid Yousefzadeh

Posted on

enq: TX — allocate ITL entry

To implement Data Concurrency and Read Consistency, Oracle stores transaction metadata in part of the data block header. This area of the block header is called the Interested Transaction List (ITL) and can contain multiple slots.

A transaction that wants to acquire a row must occupy an ITL slot in the block header of the block that contains that row. In the slot the database records information such as the Transaction ID (XID), the Undo Block Address (UBA), and the number of rows the transaction has locked in that block. Each slot belongs to a single transaction, and each transaction can occupy only one slot per block.

When a transaction ends (COMMIT or ROLLBACK), its ITL slot becomes reusable by other transactions.

The size of each transaction slot is about 24 bytes. Note that physical row addresses (ROWIDs) are not stored in this space:

SQL> select type_size from v$type_size where description=‘TRANSACTION VARIABLE HEADER’; TYPE_SIZE --------- 24 
Enter fullscreen mode Exit fullscreen mode

The initial number of transaction slots per block can be set using the INITRANS attribute at the object level. The default INITRANS is 1 for tables and 2 for indexes. If more ITL slots are needed, Oracle will automatically allocate additional slots unless either the number of ITL slots reaches the value of MAXTRANS or the block has no free space.

Example:

SQL> create table VAHID.MYTBL (id number, text varchar2(4000)); Table created SQL> select INI_TRANS, MAX_TRANS from user_tables p where TABLE_NAME=‘MYTBL’; INI_TRANS MAX_TRANS --------- --------- 1 255 
Enter fullscreen mode Exit fullscreen mode

As shown, MAXTRANS defaults to 255, so (space permitting) up to 255 concurrent:

transactions can be active on a single block. In practice this is seldom reached because the number of rows per block is usually much smaller.

Note 1: Since Oracle 9i, even when INITRANS is set to 1, Oracle reserves two ITL slots per data block by default.
Note 2: From Oracle 10gR1 onward, MAXTRANS is not user-configurable; Oracle effectively uses 255.

Understanding ITL SLOT structure
To investigate the internal structure of transaction slots we will dump blocks from a table and inspect the trace output.

First, create a table with INITRANS set to 2:

SQL> create table VAHID.MYTBL (id number, text varchar2(4000)) PCTFREE 0 INITRANS 2; Table created 
Enter fullscreen mode Exit fullscreen mode

Identify the block ranges for the table via DBA_EXTENTS:

SQL> select file_id, BLOCK_ID, blocks from dba_extents p where owner=‘USEF’ and segment_name=‘VAHID.MYTBL’; FILE_ID BLOCK_ID BLOCKS ------- -------- ------ 10 136 8 
Enter fullscreen mode Exit fullscreen mode

The table occupies 8 blocks (136 through 143). Dump those blocks to the session trace file with:

SQL> alter system dump datafile 10 block min 136 block max 143; System altered 
Enter fullscreen mode Exit fullscreen mode

Find the session trace file path:

SQL> SELECT p.tracefile FROM v$session s JOIN v$process p ON s.paddr = p.addr WHERE s.sid =(SELECT sid FROM v$mystat WHERE ROWNUM = 1); TRACEFILE ---------------------------------------------------------------- /18c/base/diag/rdbms/db18c/db18c/trace/db18c_ora_11978.trc 
Enter fullscreen mode Exit fullscreen mode

A portion of the trace showing ITL slots for one block looks like:

Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x0000.000.00000000 0x00000000.0000.00 --- 0 fsc 0x0000.00000000 0x02 0x0000.000.00000000 0x00000000.0000.00 --- 0 fsc 0x0000.00000000 
Enter fullscreen mode Exit fullscreen mode

Now we will run a transaction and watch how the ITL entries change.
Session 1:

SQL> insert into VAHID.MYTBL values(1,’v.u’); 1 row inserted SQL> SELECT dbms_rowid.rowid_block_number(rowid) block_number FROM VAHID.MYTBL; BLOCK_NUMBER ------------ 141 
Enter fullscreen mode Exit fullscreen mode

The inserted row resides in block 141. Dump that block:

SQL> alter system dump datafile 10 block 141; System altered 
Enter fullscreen mode Exit fullscreen mode

Trace:

Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x003e.00d.00000698 0x034000e3.03d7.1d --- 1 fsc 0x0000.00000000 0x02 0x0000.000.00000000 0x00000000.0000.00 --- 0 fsc 0x0000.00000000 
Enter fullscreen mode Exit fullscreen mode

As expected, the running transaction has occupied one ITL slot (0x01). Because it modified one row, LCK changed from 0 to 1 for that slot.

Without committing, issue another insert in the same session:

SQL> insert into VAHID.MYTBL values(2,’v.u’); 1 row inserted 
Enter fullscreen mode Exit fullscreen mode

Dump the same block again (from another session) and you will see:

Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x003e.00d.00000698 0x034000e3.03d7.1e --- 2 fsc 0x0000.00000000 0x02 0x0000.000.00000000 0x00000000.0000.00 --- 0 fsc 0x0000.00000000 
Enter fullscreen mode Exit fullscreen mode

LCK increased to 2, indicating two rows in block 141 are held by the same transaction. Further inserts continue to increment this value.

After several inserts (ids 3,4,5) the trace shows:

SQL> insert into mytbl values(3,’v.u’); 1 row inserted SQL> insert into mytbl values(4,’v.u’); 1 row inserted SQL> insert into mytbl values(5,’v.u’); 1 row inserted 
Enter fullscreen mode Exit fullscreen mode

Trace:

Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x003e.00d.00000698 0x034000e3.03d7.21 --- 5 fsc 0x0000.00000000 0x02 0x0000.000.00000000 0x00000000.0000.00 --- 0 fsc 0x0000.00000000 
Enter fullscreen mode Exit fullscreen mode

Commit the transaction:

SQL> commit; Commit complete 
Enter fullscreen mode Exit fullscreen mode

Trace after commit:

Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x003e.00d.00000698 0x034000e3.03d7.21 -U- 5 fsc 0x0000.a7d3633b 0x02 0x0000.000.00000000 0x00000000.0000.00 --- 0 fsc 0x0000.00000000 
Enter fullscreen mode Exit fullscreen mode

Now the XID remains present with Flag ‘-U-’ (committed) and the LCK value reflects the number of rows changed in that block.

We issue DELETE statements from different sessions to observe how Oracle allocates multiple ITL slots when several transactions concurrently modify rows within the same block.

Session 1:

SQL> delete VAHID.MYTBL where id=1; 1 row deleted 
Enter fullscreen mode Exit fullscreen mode

Session 2:

SQL> delete VAHID.MYTBL where id=2; 1 row deleted 
Enter fullscreen mode Exit fullscreen mode

Session 3:

SQL> delete VAHID.MYTBL where id=3; 1 row deleted 
Enter fullscreen mode Exit fullscreen mode

After these three concurrent deletes, the number of ITL slots increased from two to three. Each transaction touched one row in the block, and the trace shows three separate ITL entries:

Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x003b.001.0000052d 0x04000ddf.0292.1d --- 1 fsc 0x0008.00000000 0x02 0x003c.007.000006ba 0x0400039b.03ea.01 --- 1 fsc 0x0008.00000000 0x03 0x0042.001.0000066e 0x04000e52.03f3.01 --- 1 fsc 0x0008.00000000 
Enter fullscreen mode Exit fullscreen mode

When two more deletes are issued concurrently (ids 4 and 5), the number of ITL slots grows to five:

Session 4:

SQL> delete mytbl where id=4; 1 row deleted 
Enter fullscreen mode Exit fullscreen mode

Session 5:

SQL> delete mytbl where id=5; 1 row deleted 
Enter fullscreen mode Exit fullscreen mode
Itl Xid Uba Flag Lck Scn/Fsc 0x01 0x003b.001.0000052d 0x04000ddf.0292.1d --- 1 fsc 0x0008.00000000 0x02 0x003c.007.000006ba 0x0400039b.03ea.01 --- 1 fsc 0x0008.00000000 0x03 0x0042.001.0000066e 0x04000e52.03f3.01 --- 1 fsc 0x0008.00000000 0x04 0x003a.016.0000052c 0x04000142.0328.1b --- 1 fsc 0x0008.00000000 0x05 0x0039.01e.000004fc 0x0400031d.0377.0c --- 1 fsc 0x0008.00000000 
Enter fullscreen mode Exit fullscreen mode

Note: you can correlate XIDs in dump files with V$TRANSACTION by converting the raw XID bytes, for example using UTL_RAW.REVERSE on substrings of XID.

SQL> select utl_raw.reverse(substr(xid,1,4)) COL1,utl_raw.reverse(substr(xid,5,4)) COL2,utl_raw.reverse(substr(xid,9,8)) COL3 from v$transaction; 
Enter fullscreen mode Exit fullscreen mode

Investigating enq: TX — allocate ITL entry (the wait event)
When it is not possible to allocate a new ITL slot for a transaction within a block, the session executing that transaction will become blocked and exhibit the wait event named enq: TX — allocate ITL entry.

Two reasons can prevent allocation of a new ITL slot:
1) the number of ITL slots has reached the configured MAXTRANS, or
2) there is no free space left in the data block to store another ITL slot.

The following scenario demonstrates blocking by filling a data block so that no further ITL slots can be created.

Create a table where PCTFREE=0 and INITRANS=1 (no reserved free space in blocks):

SQL> create table VAHID.MYTBL (id number, text varchar2(4000)) PCTFREE 0 INITRANS 1; 
Enter fullscreen mode Exit fullscreen mode

Insert 6 rows with large row length so that all rows land in the same block and exhaust the block space:

SQL> insert into VAHID.MYTBL values(1,rpad(‘v’,2000,’v’)); SQL> insert into VAHID.MYTBL values(2,rpad(‘v’,2000,’v’)); SQL> insert into VAHID.MYTBL values(3,rpad(‘v’,2000,’v’)); SQL> insert into VAHID.MYTBL values(4,rpad(‘v’,2000,’v’)); SQL> insert into VAHID.MYTBL values(5,rpad(‘v’,2,’v’)); SQL> insert into VAHID.MYTBL values(6,rpad(‘v’,6,’v’)); SQL> commit; 
Enter fullscreen mode Exit fullscreen mode

Check the block numbers:

SQL> SELECT distinct dbms_rowid.rowid_block_number(rowid) block# FROM VAHID.MYTBL; BLOCK# ------ 228 
Enter fullscreen mode Exit fullscreen mode

All six rows are in block 228. The next inserted row goes into the next block because block 228 is full:

SQL> insert into VAHID.MYTBL values(7,’’); 1 row inserted SQL> SELECT distinct dbms_rowid.rowid_block_number(rowid) block# FROM VAHID.MYTBL; BLOCK# ------ 228 229 SQL> rollback; Rollback complete 
Enter fullscreen mode Exit fullscreen mode

Thus block 228 cannot accommodate more rows. Given this situation, more than two concurrent transactions cannot obtain ITL slots in block 228 (remember: Oracle reserves two ITL slots per block by default).

Next we run three concurrent DELETE statements from three different sessions:

Session 1 (transaction 1):

SQL> delete VAHID.MYTBL where id=1; 1 row deleted 
Enter fullscreen mode Exit fullscreen mode

Session 2 (transaction 2):

SQL> delete VAHID.MYTBL where id=2; 1 row deleted 
Enter fullscreen mode Exit fullscreen mode

Session 3 (transaction 3):

SQL> select sid from v$mystat where rownum<=1; SID ---- 382 SQL> delete VAHID.MYTBL where id=3; Executing… (the session hangs) 
Enter fullscreen mode Exit fullscreen mode

Session 3 will be blocked. Querying V$SESSION_WAIT shows the wait event:

SQL> select event from v$session_wait where sid=382; EVENT -------------------------------------------------- enq: TX – allocate ITL entry 
Enter fullscreen mode Exit fullscreen mode

When either transaction 1 or 2 finishes (commits or rollbacks), session 3 will be released and continue.

Two mitigations for ITL-related waits
To reduce occurrences of this wait event you can:
— Increase PCT_FREE for the table so blocks retain free space for future updates/inserts, and/or
— Increase INITRANS for the table and indexes so more ITL slots are reserved initially.

Both changes require rebuilding the table/index.

Example:

SQL> alter table VAHID.MYTBL INITRANS 50; Table altered 
Enter fullscreen mode Exit fullscreen mode

Deadlock scenarios involving ITL waits

Consider the following deadlock scenario:

session 1 has occupied the last available ITL slot in block A of table VAHID.MYTBL1 and session 2 has occupied the last available ITL slot in block B of table VAHID.MYTBL2. Neither block can allocate a new ITL slot. If session 1 requests a slot in block B while session 2 requests a slot in block A, a circular wait occurs and Oracle detects a deadlock.

Example interaction that leads to a deadlock:

Session 1:

SQL> delete VAHID.MYTBL1 where id=3; 1 row deleted 
Enter fullscreen mode Exit fullscreen mode

Session 2:

SQL> delete VAHID.MYTBL2 where id=4; 1 row deleted 
Enter fullscreen mode Exit fullscreen mode

Session 2 now executes:

SQL> delete VAHID.MYTBL1 where id=5; waiting… 
Enter fullscreen mode Exit fullscreen mode

Session 1 executes:

SQL> delete VAHID.MYTBL2 where id=6; ORA-00060: deadlock detected while waiting for resource 
Enter fullscreen mode Exit fullscreen mode

The alert log references a trace file with detailed deadlock information. An excerpt from such a trace file is shown below :

Deadlock graph: ————Blocker(s)———– ————Waiter(s)———— Resource Name process session holds waits serial process session holds waits serial TX-0042001D-00000710-00000000-00000000 89 137 X 31007 92 510 S 27570 TX-003E0011-0000075C-00000000-00000000 92 510 X 27570 89 137 S 31007 —– VKTM Time Drifts Circular Buffer —– session 137: DID 0001-0059-00000030 session 510: DID 0001-005C-00000006 session 510: DID 0001-005C-00000006 session 137: DID 0001-0059-00000030 —– Information for waiting sessions —– Session 137: sid: 137 ser: 31007 audsid: 570616 user: 104/USEF flags: (0x100041) USR/- flags2: (0x40009) -/-/INC flags_idl: (0x1) status: BSY/-/-/- kill: -/-/-/- current SQL: delete mytbl2 where id=6 Session 510: sid: 510 ser: 27570 audsid: 570619 user: 104/USEF flags: (0x100041) USR/- flags2: (0x40009) -/-/INC flags_idl: (0x1) status: BSY/-/-/- kill: -/-/-/- pid: 92 O/S info: user: oracle, term: UNKNOWN, ospid: 30324 application name: PL/SQL Developer, hash value=1190136663 action name: Command Window – New, hash value=254318129 current SQL: delete mytbl1 where id=5 —– End of information for waiting sessions —– *** 2020-02-16T13:38:06.735658+03:30 dbkedDefDump(): Starting a non-incident diagnostic dump (flags=0x0, level=1, mask=0x0) —– Error Stack Dump —– —– Current SQL Statement for this session (sql_id=91q18bnn0zm8b) —– delete mytbl2 where id=6 The history is displayed in reverse chronological order. Current Wait Stack: 0: waiting for ‘enq: TX – allocate ITL entry’ name|mode=0x54580004, usn<<16 | slot=0x3e0011, sequence=0x75c wait_id=119 seq_num=120 snap_id=1 wait times: snap=10.214547 sec, exc=10.214547 sec, total=10.214547 sec wait times: max=32.000000 sec, heur=10.214547 sec wait counts: calls=3 os=3 in_wait=1 iflags=0x15a0 There is at least one session blocking this session. Dumping 1 direct blocker(s): inst: 1, sid: 510, ser: 27570 Dumping final blocker: inst: 1, sid: 510, ser: 27570 There are 1 sessions blocked by this session. Dumping one waiter: inst: 1, sid: 510, ser: 27570 wait event: ‘enq: TX – allocate ITL entry’ p1: ‘name|mode’=0x54580004 p2: ‘usn<<16 | slot’=0x42001d p3: ‘sequence’=0x710 row_wait_obj#: 124758, block#: 175, row#: 0, file# 10 min_blocked_time: 0 secs, waiter_cache_ver: 35252 Wait State: fixed_waits=0 flags=0x22 boundary=(nil)/-1 Session Wait History: elapsed time of 0.000066 sec since current wait 0: waited for ‘enq: TX – allocate ITL entry’ name|mode=0x54580004, usn<<16 | slot=0x41001a, sequence=0x777 wait_id=118 seq_num=119 snap_id=1 wait times: snap=5.000921 sec, exc=5.000921 sec, total=5.000921 sec wait times: max=5.000000 sec wait counts: calls=2 os=2 occurred after 0.000075 sec of elapsed time 1: waited for ‘enq: TX – allocate ITL entry’ name|mode=0x54580004, usn<<16 | slot=0x3e0011, sequence=0x75c wait_id=117 seq_num=118 snap_id=1 wait times: snap=15.999972 sec, exc=15.999972 sec, total=15.999972 sec wait times: max=16.000000 sec wait counts: calls=6 os=6 occurred after 0.000045 sec of elapsed time 2: waited for ‘enq: TX – allocate ITL entry’ name|mode=0x54580004, usn<<16 | slot=0x41001a, sequence=0x777 wait_id=116 seq_num=117 snap_id=1 wait times: snap=4.999940 sec, exc=4.999940 sec, total=4.999940 sec wait times: max=5.000000 sec wait counts: calls=2 os=2 occurred after 0.000072 sec of elapsed time 
Enter fullscreen mode Exit fullscreen mode

Final note:
— — — — — — -
ITL waits occur only for UPDATE and DELETE operations — they do not occur for INSERT. For INSERT, if a block has no free ITL slot or is full, Oracle simply moves to a different block for the new row.

Top comments (0)