ORA-24761: Transaction Rolled Back - Handle Distributed Transaction Failures
ORA-24761: Transaction Rolled Back
Section titled “ORA-24761: Transaction Rolled Back”Error Overview
Section titled “Error Overview”Error Text: ORA-24761: transaction rolled back
The ORA-24761 error is raised when Oracle has forcibly rolled back a transaction, most commonly in the context of distributed transactions, serialization conflicts, or deadlock-adjacent scenarios. Unlike ORA-00060 (deadlock), ORA-24761 frequently appears in conjunction with ORA-02049 (distributed transaction timeout) or as a result of Oracle’s internal transaction management choosing to sacrifice one transaction to resolve a conflict. The error signals that the application must handle a rollback that it did not explicitly request and, in most cases, should retry the failed operation.
Common Causes
Section titled “Common Causes”1. Distributed Transaction Failures (ORA-02049 Relationship)
Section titled “1. Distributed Transaction Failures (ORA-02049 Relationship)”- A distributed transaction (involving database links) exceeded the
DISTRIBUTED_LOCK_TIMEOUTparameter - The remote database became unavailable mid-transaction, leaving the transaction in a prepared but uncommitted state
- Oracle chose to roll back the local participant to resolve a distributed deadlock
- A two-phase commit coordinator timed out, forcing the subordinate to roll back
2. Serialization Failures with Serializable Isolation
Section titled “2. Serialization Failures with Serializable Isolation”- A transaction using
SET TRANSACTION ISOLATION LEVEL SERIALIZABLEencountered a conflicting change made after its read snapshot - The application expected strict serializability but the database detected a write-write conflict
- Long-running serializable transactions in high-concurrency OLTP environments
3. Transaction Table Overflow and Internal Conflicts
Section titled “3. Transaction Table Overflow and Internal Conflicts”- Internal Oracle resource contention causing a transaction slot to be reused
- Shared server configuration issues causing transaction state loss
- RAC cross-instance transaction conflicts under extreme concurrency
4. Application-Level Misuse of Savepoints and Rollbacks
Section titled “4. Application-Level Misuse of Savepoints and Rollbacks”- Application calling
ROLLBACK TO SAVEPOINTincorrectly, causing the entire transaction to be rolled back by Oracle - Implicit rollbacks triggered by DDL statements executed inside a transaction
- Connection pool returning a dirty connection with a pending transaction that Oracle rolls back on reuse
5. ORA-24761 from Autonomous Transactions
Section titled “5. ORA-24761 from Autonomous Transactions”- An autonomous transaction block raising an unhandled exception, causing Oracle to roll back the autonomous transaction
- The calling transaction receiving ORA-24761 as a side effect of the autonomous block’s failure
Diagnostic Queries
Section titled “Diagnostic Queries”Identify Active Distributed Transactions
Section titled “Identify Active Distributed Transactions”-- View in-doubt distributed transactionsSELECT local_tran_id, global_tran_id, state, mixed, host, db_user, os_user, commit#FROM dba_2pc_pendingORDER BY local_tran_id;
-- Pending distributed transaction participantsSELECT local_tran_id, interface, dbuser_owner, linkFROM dba_2pc_neighborsORDER BY local_tran_id;
-- Distributed lock timeout parameterSHOW PARAMETER distributed_lock_timeout;Check for Serialization Conflicts
Section titled “Check for Serialization Conflicts”-- Identify serializable transactions currently activeSELECT s.sid, s.serial#, s.username, s.status, s.sql_id, t.isolation_level, t.start_scn, t.start_timeFROM v$session sJOIN v$transaction t ON s.taddr = t.addrWHERE t.isolation_level = 'SERIALIZABLE'ORDER BY t.start_time;
-- Historical serialization failures from AWRSELECT TO_CHAR(sample_time, 'DD-MON-YYYY HH24:MI:SS') AS sample_time, session_id, sql_id, event, wait_classFROM dba_hist_active_sess_historyWHERE sql_id IN ( SELECT sql_id FROM v$sql WHERE sql_text LIKE '%ORA-24761%') AND sample_time > SYSDATE - 7ORDER BY sample_time DESC;Examine Recent Transaction Errors
Section titled “Examine Recent Transaction Errors”-- Check alert log for ORA-24761 occurrencesSELECT originating_timestamp, message_textFROM v$diag_alert_extWHERE message_text LIKE '%24761%' OR message_text LIKE '%ORA-02049%'ORDER BY originating_timestamp DESCFETCH FIRST 50 ROWS ONLY;
-- Active transactions with database link activitySELECT s.sid, s.serial#, s.username, s.status, s.sql_id, s.machine, s.program, t.used_urec, t.used_ublkFROM v$session sJOIN v$transaction t ON s.taddr = t.addrWHERE s.sql_id IN ( SELECT sql_id FROM v$sql WHERE sql_text LIKE '%@%')ORDER BY t.start_time;Check Database Link Configuration
Section titled “Check Database Link Configuration”-- View database links accessible to the current userSELECT owner, db_link, username, host, createdFROM dba_db_linksORDER BY owner, db_link;
-- Check for open database link sessionsSELECT s.sid, s.serial#, s.username, s.machine, s.program, s.sql_idFROM v$session sWHERE s.type = 'USER' AND s.username IS NOT NULLORDER BY s.logon_time DESC;Investigate ORA-00060 Deadlock Relationship
Section titled “Investigate ORA-00060 Deadlock Relationship”-- Deadlock history from AWR (ORA-24761 often follows a deadlock resolution)SELECT TO_CHAR(snap_time, 'DD-MON-YYYY HH24:MI') AS snap_time, executions_delta, rows_processed_delta, optimizer_costFROM dba_hist_sqlstat sJOIN dba_hist_snapshot sn USING (snap_id, dbid, instance_number)WHERE s.sql_id IN ( SELECT sql_id FROM v$sql WHERE last_oracle_error IN (24761, 2049, 60)) AND sn.begin_interval_time > SYSDATE - 1ORDER BY snap_time DESC;Step-by-Step Resolution
Section titled “Step-by-Step Resolution”1. Resolve In-Doubt Distributed Transactions
Section titled “1. Resolve In-Doubt Distributed Transactions”-- List all in-doubt transactionsSELECT local_tran_id, global_tran_id, state, hostFROM dba_2pc_pending;
-- If the transaction is confirmed committed on all nodes, force commit:EXECUTE DBMS_TRANSACTION.COMMIT_FORCED('local_tran_id_here');
-- If the transaction should be rolled back:EXECUTE DBMS_TRANSACTION.ROLLBACK_FORCED('local_tran_id_here');
-- After forcing, verify it is cleared from dba_2pc_pendingSELECT COUNT(*) FROM dba_2pc_pending;
-- Purge the two-phase commit log if all pending transactions are resolvedEXECUTE DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY('local_tran_id_here');2. Adjust DISTRIBUTED_LOCK_TIMEOUT
Section titled “2. Adjust DISTRIBUTED_LOCK_TIMEOUT”-- Increase timeout to reduce spurious ORA-24761 on slow network links-- Default is 60 seconds; increase to 120 or 300 for WAN-connected databasesALTER SYSTEM SET distributed_lock_timeout = 120 SCOPE=BOTH;
-- Verify the new settingSELECT name, value, descriptionFROM v$parameterWHERE name = 'distributed_lock_timeout';3. Implement Application Retry Logic
Section titled “3. Implement Application Retry Logic”For applications receiving ORA-24761, the correct response is to retry the transaction. The following PL/SQL pattern demonstrates a robust retry loop:
-- Retry pattern for transactions susceptible to ORA-24761CREATE OR REPLACE PROCEDURE process_with_retry( p_max_retries IN NUMBER DEFAULT 3, p_retry_wait IN NUMBER DEFAULT 2 -- seconds between retries) AS v_retry_count NUMBER := 0; e_rollback EXCEPTION; PRAGMA EXCEPTION_INIT(e_rollback, -24761); e_dist_lock EXCEPTION; PRAGMA EXCEPTION_INIT(e_dist_lock, -2049);BEGIN LOOP BEGIN -- Your transactional work here INSERT INTO target_table SELECT * FROM source_table@remote_db WHERE status = 'PENDING'; UPDATE target_table SET status = 'PROCESSED' WHERE status = 'PENDING'; COMMIT; EXIT; -- Success: exit retry loop
EXCEPTION WHEN e_rollback OR e_dist_lock THEN v_retry_count := v_retry_count + 1; ROLLBACK; IF v_retry_count >= p_max_retries THEN RAISE; -- Exhausted retries: propagate the error END IF; DBMS_SESSION.SLEEP(p_retry_wait); -- Wait before retrying END; END LOOP;EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE;END;/4. Switch From Serializable to Read Committed Where Appropriate
Section titled “4. Switch From Serializable to Read Committed Where Appropriate”-- Check if serializable isolation is actually required-- Most OLTP applications work correctly with the default READ COMMITTED isolation
-- Change a session from serializable to read committedSET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- Or change the application's JDBC connection:-- conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
-- If serializable is genuinely required, reduce transaction duration-- by selecting only the rows actually needed rather than scanning large sets5. Fix Autonomous Transaction Error Handling
Section titled “5. Fix Autonomous Transaction Error Handling”-- Ensure autonomous transactions handle exceptions gracefullyCREATE OR REPLACE PROCEDURE log_audit_event( p_event VARCHAR2, p_user VARCHAR2) AS PRAGMA AUTONOMOUS_TRANSACTION;BEGIN INSERT INTO audit_log (event_text, username, event_time) VALUES (p_event, p_user, SYSTIMESTAMP); COMMIT;EXCEPTION WHEN OTHERS THEN -- Always commit or rollback in autonomous transactions -- to avoid leaving the calling transaction in an indeterminate state ROLLBACK; -- Optionally re-raise or log to a fallback mechanismEND;/Prevention Strategies
Section titled “Prevention Strategies”1. Minimise Distributed Transaction Duration
Section titled “1. Minimise Distributed Transaction Duration”-- Keep distributed transactions as short as possible-- BAD: Long-running distributed transactionBEGIN -- Expensive local processing FOR i IN 1..100000 LOOP process_local_data(i); END LOOP; -- Then touch the remote DB (now the transaction is long) INSERT INTO remote_table@db_link VALUES (...); COMMIT;END;
-- GOOD: Touch remote DB at the end, keep the window minimalBEGIN -- Do all local work first FOR i IN 1..100000 LOOP process_local_data(i); END LOOP; COMMIT; -- Commit local work separately
-- New short transaction for distributed work INSERT INTO remote_table@db_link VALUES (...); COMMIT;END;2. Monitor In-Doubt Transaction Accumulation
Section titled “2. Monitor In-Doubt Transaction Accumulation”-- Schedule a check for accumulating in-doubt transactionsCREATE OR REPLACE PROCEDURE check_2pc_pending AS v_count NUMBER;BEGIN SELECT COUNT(*) INTO v_count FROM dba_2pc_pending; IF v_count > 0 THEN DBMS_OUTPUT.PUT_LINE('WARNING: ' || v_count || ' in-doubt distributed transactions pending'); FOR rec IN (SELECT local_tran_id, state, host FROM dba_2pc_pending) LOOP DBMS_OUTPUT.PUT_LINE(' Tran: ' || rec.local_tran_id || ' State: ' || rec.state || ' Host: ' || rec.host); END LOOP; END IF;END;/3. Configure Appropriate Isolation Levels
Section titled “3. Configure Appropriate Isolation Levels”-- Document and enforce isolation level requirements per application module-- Use v$session to confirm applications are using expected isolation levelsSELECT username, machine, program, COUNT(*) AS session_countFROM v$sessionWHERE type = 'USER' AND username IS NOT NULLGROUP BY username, machine, programORDER BY session_count DESC;
-- Review transactions currently using serializable isolationSELECT COUNT(*)FROM v$transactionWHERE isolation_level = 'SERIALIZABLE';4. Connection Pool Configuration
Section titled “4. Connection Pool Configuration”- Configure connection pools to roll back and reset connections before returning them to the pool
- Use Oracle’s
DRCP(Database Resident Connection Pooling) which handles session reset automatically - Set pool validation queries to detect dirty connection state before use
Diagnostic Scripts
Section titled “Diagnostic Scripts”These Oracle Day by Day scripts can help identify transaction and session issues:
- gvsess.sql — Active session analysis with transaction state
- health.sql — Database health including lock and transaction status
Related Errors
Section titled “Related Errors”- ORA-00060 - Deadlock detected while waiting for resource
- ORA-02291 - Integrity constraint violated (parent key not found)
- ORA-01555 - Snapshot too old
Emergency Response
Section titled “Emergency Response”Quick Fixes
Section titled “Quick Fixes”-
Force resolve a stuck in-doubt distributed transaction
-- Commit if the transaction should have committedEXECUTE DBMS_TRANSACTION.COMMIT_FORCED('4.3.123456');-- Rollback if it should have rolled backEXECUTE DBMS_TRANSACTION.ROLLBACK_FORCED('4.3.123456'); -
Identify and kill sessions holding distributed locks
SELECT sid, serial#, username, machine, programFROM v$sessionWHERE taddr IN (SELECT addr FROM v$transaction);-- Kill the blocking sessionALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE; -
Temporarily increase distributed lock timeout
ALTER SYSTEM SET distributed_lock_timeout = 300 SCOPE=MEMORY;
Post-Resolution Cleanup
Section titled “Post-Resolution Cleanup”-- Confirm all in-doubt transactions are clearedSELECT COUNT(*) AS pending_count FROM dba_2pc_pending;
-- Review the alert log for repeated ORA-24761 / ORA-02049 patternsSELECT originating_timestamp, message_textFROM v$diag_alert_extWHERE message_text LIKE '%24761%' OR message_text LIKE '%02049%'ORDER BY originating_timestamp DESCFETCH FIRST 20 ROWS ONLY;
-- Run AWR report to assess transaction throughput and conflict rate@?/rdbms/admin/awrrpt.sql