-- Set transaction isolation levelsBEGINTRANSACTIONISOLATIONLEVELREADCOMMITTED;-- Your transaction code hereCOMMIT;-- Different isolation levels for different use casesBEGINTRANSACTIONISOLATIONLEVELREADUNCOMMITTED;-- Lowest isolationBEGINTRANSACTIONISOLATIONLEVELREADCOMMITTED;-- Default levelBEGINTRANSACTIONISOLATIONLEVELREPEATABLEREAD;-- Higher isolationBEGINTRANSACTIONISOLATIONLEVELSERIALIZABLE;-- Highest isolation-- Check current isolation levelSELECTcurrent_setting('transaction_isolation');
-- Create test tables for isolation level demonstrationCREATETABLEaccounts(idSERIALPRIMARYKEY,account_numberVARCHAR(20)UNIQUENOTNULL,balanceNUMERIC(10,2)NOTNULLDEFAULT0,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);CREATETABLEtransactions(idSERIALPRIMARYKEY,from_account_idINTEGERREFERENCESaccounts(id),to_account_idINTEGERREFERENCESaccounts(id),amountNUMERIC(10,2)NOTNULL,transaction_typeVARCHAR(20)NOTNULL,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Insert sample dataINSERTINTOaccounts(account_number,balance)VALUES('ACC001',1000.00),('ACC002',500.00),('ACC003',2000.00);-- READ COMMITTED exampleBEGINTRANSACTIONISOLATIONLEVELREADCOMMITTED;SELECTbalanceFROMaccountsWHEREaccount_number='ACC001';-- Another transaction can modify this data hereSELECTbalanceFROMaccountsWHEREaccount_number='ACC001';COMMIT;-- REPEATABLE READ exampleBEGINTRANSACTIONISOLATIONLEVELREPEATABLEREAD;SELECTbalanceFROMaccountsWHEREaccount_number='ACC001';-- This will see the same data even if another transaction modifies itSELECTbalanceFROMaccountsWHEREaccount_number='ACC001';COMMIT;-- SERIALIZABLE exampleBEGINTRANSACTIONISOLATIONLEVELSERIALIZABLE;SELECTbalanceFROMaccountsWHEREaccount_number='ACC001';-- This will fail if another transaction modifies the dataUPDATEaccountsSETbalance=balance-100WHEREaccount_number='ACC001';COMMIT;
-- Create tables for lock demonstrationCREATETABLEinventory(idSERIALPRIMARYKEY,product_idINTEGERNOTNULL,quantityINTEGERNOTNULL,reserved_quantityINTEGERDEFAULT0,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);CREATETABLEorders(idSERIALPRIMARYKEY,customer_idINTEGERNOTNULL,statusVARCHAR(20)DEFAULT'pending',total_amountNUMERIC(10,2)NOTNULL,created_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);CREATETABLEorder_items(idSERIALPRIMARYKEY,order_idINTEGERNOTNULLREFERENCESorders(id),product_idINTEGERNOTNULL,quantityINTEGERNOTNULL,unit_priceNUMERIC(10,2)NOTNULL);-- Insert sample dataINSERTINTOinventory(product_id,quantity)VALUES(1,100),(2,50),(3,200);-- Row-level locking examplesBEGIN;-- Lock specific rows for updateSELECT*FROMinventoryWHEREproduct_id=1FORUPDATE;-- Perform operations on locked rowsUPDATEinventorySETreserved_quantity=reserved_quantity+10WHEREproduct_id=1;COMMIT;-- Lock for share (read lock)BEGIN;SELECT*FROMinventoryWHEREproduct_id=1FORSHARE;-- Other transactions can read but not modifyCOMMIT;
-- Table-level locking examplesBEGIN;-- Lock table for exclusive accessLOCKTABLEinventoryINEXCLUSIVEMODE;-- Perform bulk operationsUPDATEinventorySETquantity=quantity-10;COMMIT;-- Lock table for share accessBEGIN;LOCKTABLEinventoryINSHAREMODE;-- Allow other transactions to read but not modifySELECT*FROMinventory;COMMIT;
-- Create function to prevent deadlocksCREATEORREPLACEFUNCTIONtransfer_inventory(from_product_idINTEGER,to_product_idINTEGER,quantityINTEGER)RETURNSBOOLEANAS$$DECLAREfrom_quantityINTEGER;to_quantityINTEGER;BEGIN-- Always lock in the same order to prevent deadlocksIFfrom_product_id<to_product_idTHEN-- Lock from_product firstSELECTquantityINTOfrom_quantityFROMinventoryWHEREproduct_id=from_product_idFORUPDATE;SELECTquantityINTOto_quantityFROMinventoryWHEREproduct_id=to_product_idFORUPDATE;ELSE-- Lock to_product firstSELECTquantityINTOto_quantityFROMinventoryWHEREproduct_id=to_product_idFORUPDATE;SELECTquantityINTOfrom_quantityFROMinventoryWHEREproduct_id=from_product_idFORUPDATE;ENDIF;-- Check if sufficient inventoryIFfrom_quantity<quantityTHENRAISEEXCEPTION'Insufficient inventory: % available, % requested',from_quantity,quantity;ENDIF;-- Perform transferUPDATEinventorySETquantity=quantity-quantityWHEREproduct_id=from_product_id;UPDATEinventorySETquantity=quantity+quantityWHEREproduct_id=to_product_id;RETURNTRUE;END;$$LANGUAGEplpgsql;
-- Create function with savepointsCREATEORREPLACEFUNCTIONprocess_order(customer_idINTEGER,order_itemsJSONB)RETURNSINTEGERAS$$DECLAREorder_idINTEGER;itemJSONB;product_idINTEGER;quantityINTEGER;unit_priceNUMERIC(10,2);total_amountNUMERIC(10,2):=0;BEGIN-- Start transactionBEGIN-- Create orderINSERTINTOorders(customer_id,total_amount)VALUES(customer_id,0)RETURNINGidINTOorder_id;-- Process each order itemFORitemINSELECT*FROMjsonb_array_elements(order_items)LOOPproduct_id:=(item->>'product_id')::INTEGER;quantity:=(item->>'quantity')::INTEGER;unit_price:=(item->>'unit_price')::NUMERIC(10,2);-- Use savepoint for each itemSAVEPOINTitem_processing;BEGIN-- Check inventoryIFNOTcheck_inventory_availability(product_id,quantity)THENRAISEEXCEPTION'Insufficient inventory for product %',product_id;ENDIF;-- Reserve inventoryUPDATEinventorySETreserved_quantity=reserved_quantity+quantityWHEREproduct_id=product_id;-- Add order itemINSERTINTOorder_items(order_id,product_id,quantity,unit_price)VALUES(order_id,product_id,quantity,unit_price);total_amount:=total_amount+(quantity*unit_price);EXCEPTIONWHENOTHERSTHEN-- Rollback to savepointROLLBACKTOSAVEPOINTitem_processing;RAISE;END;ENDLOOP;-- Update order totalUPDATEordersSETtotal_amount=total_amountWHEREid=order_id;RETURNorder_id;EXCEPTIONWHENOTHERSTHEN-- Rollback entire transactionROLLBACK;RAISE;END;END;$$LANGUAGEplpgsql;
-- Create table with version column for optimistic lockingCREATETABLEproducts(idSERIALPRIMARYKEY,nameVARCHAR(200)NOTNULL,priceNUMERIC(10,2)NOTNULL,versionINTEGERDEFAULT1,updated_atTIMESTAMPTZDEFAULTCURRENT_TIMESTAMP);-- Create function for optimistic locking updateCREATEORREPLACEFUNCTIONupdate_product_optimistic(product_idINTEGER,new_nameVARCHAR(200),new_priceNUMERIC(10,2),expected_versionINTEGER)RETURNSBOOLEANAS$$DECLAREcurrent_versionINTEGER;rows_affectedINTEGER;BEGIN-- Update with version checkUPDATEproductsSETname=new_name,price=new_price,version=version+1,updated_at=CURRENT_TIMESTAMPWHEREid=product_idANDversion=expected_version;GETDIAGNOSTICSrows_affected=ROW_COUNT;IFrows_affected=0THENRAISEEXCEPTION'Product was modified by another transaction. Expected version: %, current version: %',expected_version,(SELECTversionFROMproductsWHEREid=product_id);ENDIF;RETURNTRUE;END;$$LANGUAGEplpgsql;
-- Create function for pessimistic lockingCREATEORREPLACEFUNCTIONupdate_product_pessimistic(product_idINTEGER,new_nameVARCHAR(200),new_priceNUMERIC(10,2))RETURNSBOOLEANAS$$DECLAREcurrent_productRECORD;BEGIN-- Lock the row for updateSELECT*INTOcurrent_productFROMproductsWHEREid=product_idFORUPDATE;IFNOTFOUNDTHENRAISEEXCEPTION'Product not found: %',product_id;ENDIF;-- Update the productUPDATEproductsSETname=new_name,price=new_price,updated_at=CURRENT_TIMESTAMPWHEREid=product_id;RETURNTRUE;END;$$LANGUAGEplpgsql;
-- Create function to demonstrate snapshot isolationCREATEORREPLACEFUNCTIONget_user_balance_snapshot(user_idINTEGER)RETURNSNUMERIC(10,2)AS$$DECLAREbalanceNUMERIC(10,2);BEGIN-- This will see a consistent snapshot of the dataSELECTCOALESCE(SUM(amount),0)INTObalanceFROMtransactionsWHEREuser_id=user_id;RETURNbalance;END;$$LANGUAGEplpgsql;
-- Create function for transaction recoveryCREATEORREPLACEFUNCTIONrecover_failed_transactions()RETURNSINTEGERAS$$DECLAREfailed_transactionRECORD;recovery_countINTEGER:=0;BEGIN-- Find failed transactionsFORfailed_transactionINSELECT*FROMtransactionsWHEREstatus='failed'ANDcreated_at>CURRENT_TIMESTAMP-INTERVAL'1 hour'LOOPBEGIN-- Attempt to recover the transactionUPDATEtransactionsSETstatus='recovered',updated_at=CURRENT_TIMESTAMPWHEREid=failed_transaction.id;recovery_count:=recovery_count+1;EXCEPTIONWHENOTHERSTHEN-- Log recovery failureINSERTINTOerror_logs(transaction_id,error_type,error_message,timestamp)VALUES(failed_transaction.id,'recovery_failed',SQLERRM,CURRENT_TIMESTAMP);END;ENDLOOP;RETURNrecovery_count;END;$$LANGUAGEplpgsql;
-- 1. Set appropriate isolation levelsBEGINTRANSACTIONISOLATIONLEVELREADCOMMITTED;-- Your transaction code hereCOMMIT;-- 2. Use row-level lockingSELECT*FROMtable_nameWHEREid=1FORUPDATE;-- 3. Implement savepoints for nested transactionsSAVEPOINTmy_savepoint;-- Your code hereROLLBACKTOSAVEPOINTmy_savepoint;-- 4. Handle errors in transactionsBEGIN;-- Your code hereEXCEPTIONWHENOTHERSTHENROLLBACK;RAISE;END;