@@ -17,6 +17,7 @@ import {
17
17
DatabaseContract ,
18
18
DatabaseClientOptions ,
19
19
DatabaseConfigContract ,
20
+ TransactionClientContract ,
20
21
ConnectionManagerContract ,
21
22
} from '@ioc:Adonis/Lucid/Database'
22
23
@@ -51,6 +52,11 @@ export class Database implements DatabaseContract {
51
52
public InsertQueryBuilder = InsertQueryBuilder
52
53
public ModelQueryBuilder = ModelQueryBuilder
53
54
55
+ /**
56
+ * A store of global transactions
57
+ */
58
+ public connectionGlobalTransactions : Map < string , TransactionClientContract > = new Map ( )
59
+
54
60
constructor (
55
61
private config : DatabaseConfigContract ,
56
62
private logger : LoggerContract ,
@@ -106,6 +112,16 @@ export class Database implements DatabaseContract {
106
112
throw new Exception ( `Invalid mode ${ options . mode } . Must be read or write` )
107
113
}
108
114
115
+ /**
116
+ * Return the global transaction when it already exists.
117
+ */
118
+ if ( this . connectionGlobalTransactions . has ( connection ) ) {
119
+ this . logger . trace ( { connection } , 'using pre-existing global transaction connection' )
120
+ const globalTransactionClient = this . connectionGlobalTransactions . get ( connection ) !
121
+ globalTransactionClient . profiler = options . profiler
122
+ return globalTransactionClient
123
+ }
124
+
109
125
/**
110
126
* Fetching connection for the given name
111
127
*/
@@ -203,4 +219,72 @@ export class Database implements DatabaseContract {
203
219
public raw ( sql : string , bindings ?: any ) {
204
220
return new RawBuilder ( sql , bindings )
205
221
}
222
+
223
+ /**
224
+ * Begin a new global transaction
225
+ */
226
+ public async beginGlobalTransaction (
227
+ connectionName ?: string ,
228
+ options ?: Omit < DatabaseClientOptions , 'mode' > ,
229
+ ) {
230
+ connectionName = connectionName || this . primaryConnectionName
231
+
232
+ /**
233
+ * Return global transaction as it is
234
+ */
235
+ const globalTrx = this . connectionGlobalTransactions . get ( connectionName )
236
+ if ( globalTrx ) {
237
+ return globalTrx
238
+ }
239
+
240
+ /**
241
+ * Create a new transaction and store a reference to it
242
+ */
243
+ const trx = await this . connection ( connectionName , options ) . transaction ( )
244
+ this . connectionGlobalTransactions . set ( trx . connectionName , trx )
245
+
246
+ /**
247
+ * Listen for events to drop the reference when transaction
248
+ * is over
249
+ */
250
+ trx . on ( 'commit' , ( $trx ) => {
251
+ this . connectionGlobalTransactions . delete ( $trx . connectionName )
252
+ } )
253
+
254
+ trx . on ( 'rollback' , ( $trx ) => {
255
+ this . connectionGlobalTransactions . delete ( $trx . connectionName )
256
+ } )
257
+
258
+ return trx
259
+ }
260
+
261
+ /**
262
+ * Commit an existing global transaction
263
+ */
264
+ public async commitGlobalTransaction ( connectionName ?: string ) {
265
+ connectionName = connectionName || this . primaryConnectionName
266
+ const trx = this . connectionGlobalTransactions . get ( connectionName )
267
+
268
+ if ( ! trx ) {
269
+ // eslint-disable-next-line max-len
270
+ throw new Exception ( 'Cannot commit a non-existing global transaction. Make sure you are not calling "commitGlobalTransaction" twice' )
271
+ }
272
+
273
+ await trx . commit ( )
274
+ }
275
+
276
+ /**
277
+ * Rollback an existing global transaction
278
+ */
279
+ public async rollbackGlobalTransaction ( connectionName ?: string ) {
280
+ connectionName = connectionName || this . primaryConnectionName
281
+ const trx = this . connectionGlobalTransactions . get ( connectionName )
282
+
283
+ if ( ! trx ) {
284
+ // eslint-disable-next-line max-len
285
+ throw new Exception ( 'Cannot rollback a non-existing global transaction. Make sure you are not calling "commitGlobalTransaction" twice' )
286
+ }
287
+
288
+ await trx . rollback ( )
289
+ }
206
290
}
0 commit comments