@@ -10,6 +10,17 @@ export interface TransactionOptions {
1010export interface TransactionContext {
1111 client : PoolClient | Pool ;
1212 isTransaction : boolean ;
13+ // Add query tracking for debugging
14+ queryHistory : QueryHistoryEntry [ ] ;
15+ addQuery : ( query : string , params ?: any [ ] , startTime ?: number ) => void ;
16+ }
17+
18+ export interface QueryHistoryEntry {
19+ query : string ;
20+ params ?: any [ ] ;
21+ timestamp : number ;
22+ duration ?: number ;
23+ error ?: any ;
1324}
1425
1526/**
@@ -22,41 +33,142 @@ export async function withTransaction<T>(
2233 options : TransactionOptions ,
2334 fn : ( context : TransactionContext ) => Promise < T >
2435) : Promise < T > {
36+ const queryHistory : QueryHistoryEntry [ ] = [ ] ;
37+
38+ const addQuery = ( query : string , params ?: any [ ] , startTime ?: number ) => {
39+ queryHistory . push ( {
40+ query,
41+ params,
42+ timestamp : Date . now ( ) ,
43+ duration : startTime ? Date . now ( ) - startTime : undefined
44+ } ) ;
45+ } ;
46+
2547 if ( ! options . useTransaction ) {
2648 // No transaction - use pool directly
2749 log . debug ( 'Executing without transaction' ) ;
28- return fn ( { client : pool , isTransaction : false } ) ;
50+ return fn ( { client : pool , isTransaction : false , queryHistory , addQuery } ) ;
2951 }
3052
3153 // Use transaction
3254 const client = await pool . connect ( ) ;
55+ const transactionStartTime = Date . now ( ) ;
3356 log . debug ( 'Starting transaction' ) ;
3457
3558 try {
59+ const beginTime = Date . now ( ) ;
3660 await client . query ( 'BEGIN' ) ;
61+ addQuery ( 'BEGIN' , [ ] , beginTime ) ;
3762
38- const result = await fn ( { client, isTransaction : true } ) ;
63+ const result = await fn ( { client, isTransaction : true , queryHistory , addQuery } ) ;
3964
65+ const commitTime = Date . now ( ) ;
4066 await client . query ( 'COMMIT' ) ;
41- log . debug ( 'Transaction committed successfully' ) ;
67+ addQuery ( 'COMMIT' , [ ] , commitTime ) ;
68+
69+ const transactionDuration = Date . now ( ) - transactionStartTime ;
70+ log . debug ( `Transaction committed successfully in ${ transactionDuration } ms` ) ;
4271
4372 return result ;
44- } catch ( error ) {
45- await client . query ( 'ROLLBACK' ) ;
46- log . error ( 'Transaction rolled back due to error:' , error ) ;
73+ } catch ( error : any ) {
74+ const rollbackTime = Date . now ( ) ;
75+
76+ try {
77+ await client . query ( 'ROLLBACK' ) ;
78+ addQuery ( 'ROLLBACK' , [ ] , rollbackTime ) ;
79+ } catch ( rollbackError ) {
80+ log . error ( 'Failed to rollback transaction:' , rollbackError ) ;
81+ }
82+
83+ const transactionDuration = Date . now ( ) - transactionStartTime ;
84+
85+ // Enhanced error logging with context
86+ const errorLines = [ ] ;
87+ errorLines . push ( `Transaction rolled back due to error after ${ transactionDuration } ms:` ) ;
88+ errorLines . push ( `Error Code: ${ error . code || 'N/A' } ` ) ;
89+ errorLines . push ( `Error Message: ${ error . message || 'N/A' } ` ) ;
90+
91+ // Log query history for debugging
92+ if ( queryHistory . length > 0 ) {
93+ errorLines . push ( 'Query history for this transaction:' ) ;
94+ queryHistory . forEach ( ( entry , index ) => {
95+ const duration = entry . duration ? ` (${ entry . duration } ms)` : '' ;
96+ const params = entry . params && entry . params . length > 0
97+ ? ` with params: ${ JSON . stringify ( entry . params . slice ( 0 , 2 ) ) } ${ entry . params . length > 2 ? '...' : '' } `
98+ : '' ;
99+ errorLines . push ( ` ${ index + 1 } . ${ entry . query . split ( '\n' ) [ 0 ] . trim ( ) } ${ params } ${ duration } ` ) ;
100+ } ) ;
101+ }
102+
103+ // For transaction aborted errors, provide additional context
104+ if ( error . code === '25P02' ) {
105+ errorLines . push ( '🔍 Debug Info: Transaction was aborted due to a previous error.' ) ;
106+ errorLines . push ( ' This usually means a previous command in the transaction failed.' ) ;
107+ errorLines . push ( ' Check the query history above to identify the failing command.' ) ;
108+ }
109+
110+ // Log the consolidated error message
111+ log . error ( errorLines . join ( '\n' ) ) ;
112+
47113 throw error ;
48114 } finally {
49115 client . release ( ) ;
50116 }
51117}
52118
53119/**
54- * Helper to execute a query within a transaction context
120+ * Helper to execute a query within a transaction context with enhanced logging
55121 */
56122export async function executeQuery (
57123 context : TransactionContext ,
58124 query : string ,
59125 params ?: any [ ]
60126) : Promise < any > {
61- return context . client . query ( query , params ) ;
127+ const startTime = Date . now ( ) ;
128+
129+ try {
130+ const result = await context . client . query ( query , params ) ;
131+ const duration = Date . now ( ) - startTime ;
132+
133+ // Add to query history
134+ context . addQuery ( query , params , startTime ) ;
135+
136+ // Log slow queries for debugging
137+ if ( duration > 1000 ) {
138+ log . warn ( `Slow query detected (${ duration } ms): ${ query . split ( '\n' ) [ 0 ] . trim ( ) } ` ) ;
139+ }
140+
141+ return result ;
142+ } catch ( error : any ) {
143+ const duration = Date . now ( ) - startTime ;
144+
145+ // Add failed query to history
146+ context . addQuery ( query , params , startTime ) ;
147+
148+ // Enhanced error logging
149+ const errorLines = [ ] ;
150+ errorLines . push ( `Query failed after ${ duration } ms:` ) ;
151+ errorLines . push ( ` Query: ${ query . split ( '\n' ) [ 0 ] . trim ( ) } ` ) ;
152+ if ( params && params . length > 0 ) {
153+ errorLines . push ( ` Params: ${ JSON . stringify ( params . slice ( 0 , 3 ) ) } ${ params . length > 3 ? '...' : '' } ` ) ;
154+ }
155+ errorLines . push ( ` Error Code: ${ error . code || 'N/A' } ` ) ;
156+ errorLines . push ( ` Error Message: ${ error . message || 'N/A' } ` ) ;
157+
158+ // Provide debugging hints for common errors
159+ if ( error . code === '42P01' ) {
160+ errorLines . push ( '💡 Hint: Relation (table/view) does not exist. Check if migrations are applied in correct order.' ) ;
161+ } else if ( error . code === '42883' ) {
162+ errorLines . push ( '💡 Hint: Function does not exist. Check if required extensions or functions are installed.' ) ;
163+ } else if ( error . code === '23505' ) {
164+ errorLines . push ( '💡 Hint: Unique constraint violation. Check for duplicate data.' ) ;
165+ } else if ( error . code === '23503' ) {
166+ errorLines . push ( '💡 Hint: Foreign key constraint violation. Check referential integrity.' ) ;
167+ }
168+
169+ // Log the consolidated error message
170+ log . error ( errorLines . join ( '\n' ) ) ;
171+
172+ throw error ;
173+ }
62174}
0 commit comments