PRAGMA AUTONOMOUS_TRANSACTION performs an independent operation within a main transaction, ensuring its success or failure does not affect the main transaction. An autonomous transaction starts a completely separate transaction with its own commit and rollback logic, making it ideal for scenarios like audit logging or error logging. Even if the main transaction rolls back, logs recorded by the autonomous transaction persist, ensuring that all operations are traceable.
How it works
The core principle of an autonomous transaction is isolation. When you call a program containing PRAGMA AUTONOMOUS_TRANSACTION, the current parent transaction is suspended, and a new, independent sub-transaction starts.
Isolation: An autonomous transaction cannot see uncommitted changes from its parent transaction, and vice versa.
Independent commits and rollbacks: A
COMMITorROLLBACKinside an autonomous transaction affects only that transaction and is independent of the parent transaction's finalCOMMITorROLLBACK.Connection consumption: Each call to an autonomous transaction creates a separate database connection, which is released when the transaction ends.
Notes
You can use the PRAGMA AUTONOMOUS_TRANSACTION directive in the declaration section of the following SPL program units:
Standalone stored procedures and functions.
Anonymous blocks.
Stored procedures and functions declared in packages.
Triggers.
Object type methods.
To ensure system stability and transactional consistency, autonomous transactions in Triggers are not supported in Oracle syntax compatibility version 2.0.
The
PRAGMA AUTONOMOUS_TRANSACTIONdirective must be placed at the beginning of the declaration section.-- Correct syntax for using an autonomous transaction in a stored procedure CREATE OR REPLACE PROCEDURE procedure_name IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN -- Business logic COMMIT; END; -- Correct syntax for using an autonomous transaction in a trigger, for use with Oracle compatibility 1.0 only CREATE OR REPLACE TRIGGER trigger_name AFTER INSERT OR UPDATE OR DELETE ON table_name DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN -- Business logic COMMIT; END;
Use cases
Use an autonomous Anonymous Block to record audit logs
This scenario ensures that audit logs are saved, regardless of whether the main transaction is committed or rolled back.
Create the
emptable for employee data and theempauditlogtable for audit logs.CREATE TABLE emp ( emp_id INT, emp_name VARCHAR(50), job VARCHAR(50) ); CREATE TABLE empauditlog ( audit_date DATE, audit_user VARCHAR2(20), audit_desc VARCHAR2(100) );Start a transaction, insert data, and then roll back the transaction.
BEGIN; INSERT INTO emp (emp_id, emp_name, job) VALUES (101, 'John Doe', 'Engineer'); DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO empauditlog VALUES (SYSDATE, USER, 'Added employee(s)'); COMMIT; END; ROLLBACK;Because the parent transaction was rolled back, the data insertion into the
emptable is undone, and the data is not persisted. However, the autonomous transaction wrote and committed the audit log to theempauditlogtable independently and is not affected by the parent transaction's rollback.-- Query the emp table. It should be empty because the main transaction was rolled back. SELECT * FROM emp WHERE emp_id = 101; -- Query the empauditlog table. Verify that the autonomous transaction was committed. This should return one audit record. SELECT * FROM empauditlog;
Use an autonomous Stored Procedure to log failed attempts
In this scenario, the system creates an order if stock is sufficient. If stock is insufficient, the system rolls back the order creation but still records a log of the user's attempt.
Create three tables:
orders(for order data),inventory, andoperation_log(including failure logs). Initialize the stock to 5. Placing an order for 10 items triggers an insufficient stock exception and logs the failure.-- 1. Order table (main business data) CREATE TABLE orders ( order_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, user_id NUMBER, product_id NUMBER, qty NUMBER, order_time TIMESTAMP DEFAULT SYSDATE ); -- 2. Inventory table CREATE TABLE inventory ( product_id NUMBER PRIMARY KEY, stock NUMBER ); -- 3. Operation log table (for autonomous transaction writes) CREATE TABLE operation_log ( log_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, user_id NUMBER, product_id NUMBER, action VARCHAR2(50), -- such as 'ORDER_ATTEMPT' status VARCHAR2(20), -- 'SUCCESS' / 'FAILED' reason VARCHAR2(200), -- Failure reason log_time TIMESTAMP DEFAULT SYSDATE ); -- 4. Initialize data INSERT INTO inventory (product_id, stock) VALUES (1001, 5); -- Product 1001 has only 5 items in stockCreate a a Stored Procedure using an autonomous transaction to ensure the
operation_logis persisted even when the parent transaction is rolled back.CREATE OR REPLACE PROCEDURE log_order_attempt( p_user_id IN NUMBER, p_product_id IN NUMBER, p_reason IN VARCHAR2 ) IS PRAGMA AUTONOMOUS_TRANSACTION; -- Declaration section within the procedure BEGIN INSERT INTO operation_log ( user_id, product_id, action, status, reason, log_time ) VALUES ( p_user_id,p_product_id,'ORDER_ATTEMPT','FAILED',p_reason,SYSDATE ); COMMIT; -- Independent commit, not affected by the main transaction END;A user attempts to place an order. It fails due to insufficient stock, but the autonomous transaction ensures the log is committed independently.
DECLARE v_user_id NUMBER := 123; v_product_id NUMBER := 1001; v_order_qty NUMBER := 10; v_stock NUMBER; BEGIN SELECT stock INTO v_stock FROM inventory WHERE product_id = v_product_id FOR UPDATE; IF v_stock < v_order_qty THEN log_order_attempt( p_user_id => v_user_id, p_product_id => v_product_id, p_reason => 'Insufficient stock: need ' || v_order_qty || ', available ' || v_stock ); RAISE_APPLICATION_ERROR(-20001, 'Insufficient stock, order creation failed'); END IF; INSERT INTO orders (user_id, product_id, qty) VALUES (v_user_id, v_product_id, v_order_qty); UPDATE inventory SET stock = stock - v_order_qty WHERE product_id = v_product_id; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END;Verify the results: The
orderstable is empty and theinventoryis unchanged because the parent transaction was rolled back. However, theoperation_logtable contains a record, because the autonomous transaction was committed.SELECT * FROM orders; SELECT * FROM inventory; SELECT * FROM operation_log;
Best practices
Risk of connection exhaustion
Each autonomous transaction consumes a separate database connection. In high-concurrency scenarios, overusing autonomous transactions can quickly exhaust the database's max_connections limit, causing new connection requests to fail and leading to service disruptions.
Risk scenario: Using autonomous transactions inside a loop or in a frequently called function.
Nesting risk: Autonomous transactions can be nested. Each level of nesting consumes an additional connection. For example, a session with three levels of nested autonomous transactions temporarily uses four connections: one for the main session and three for the autonomous transactions.
Capacity planning: Before you use autonomous transactions, evaluate the extra demand they will place on database connections. Adjust the
max_connectionsparameter as needed. Perform stress tests.
Recommended practices
Explicitly commit or roll back: Use
COMMITorROLLBACKat the end of an autonomous transaction block to explicitly define its outcome.Keep them short: An autonomous transaction should be short and contain only fast-executing operations, such as inserting a single log entry. Avoid running complex, time-consuming queries.
Use for specific scenarios: Use them only for auxiliary tasks that are independent of the main transaction logic, such as auditing, logging, or updating statistics.
Compatibility notes
The implementation of autonomous transactions in PolarDB for PostgreSQL (Compatible with Oracle) has a key behavioral difference compared to Oracle Database. This difference can introduce potential risks during database migration.
Feature
| Oracle behavior | PolarDB for PostgreSQL (Compatible with Oracle) behavior | Risk description |
No explicit commit or rollback | Throws an | Implicit commit. If the code block finishes normally without a | High risk. This behavior can lead to data being committed unexpectedly, which violates the principle of atomicity for transactions. To ensure consistent code behavior across different environments, we strongly recommend always using |
FAQ
How to troubleshoot the "sorry, too many clients already" error?
This error indicates that the number of active database connections has reached the max_connections limit. If you suspect that autonomous transactions are the cause, follow these steps:
Check the current number of connections and the configured
max_connectionslimit.-- View the total number of connections SELECT count(*) FROM pg_stat_activity; -- View the current maximum number of connections SHOW max_connections;Identify long-running autonomous transactions. Use the following query to find queries related to autonomous transactions running for more than five minutes.
SELECT pid, query_start, now() - query_start AS duration, query FROM pg_stat_activity WHERE state = 'active' AND query ILIKE '%PRAGMA AUTONOMOUS_TRANSACTION%' AND now() - query_start > interval '5 minutes';Solutions:
Optimize the code: Check for and optimize any loops or long-running operations that call autonomous transactions.
Increase the number of connections: If usage is reasonable but connections are still insufficient, consider increasing
max_connectionsafter evaluating the system-wide impact.Set a timeout: Set
statement_timeoutfor the session to automatically terminate long-running queries and prevent connections from being held open indefinitely.