Skip to content

Commit de744df

Browse files
Copilothotlong
andcommitted
feat: Implement MongoDB transaction support
- Added beginTransaction(), commitTransaction(), rollbackTransaction() methods - Updated create(), update(), delete() to support session option for transactions - Transaction support uses MongoDB ClientSession - Comprehensive documentation with examples - Updated TCK tests to enable transaction testing for MongoDB Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 4c942a3 commit de744df

2 files changed

Lines changed: 87 additions & 5 deletions

File tree

packages/drivers/mongo/src/index.ts

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,9 @@ export class MongoDriver implements Driver {
410410
mongoDoc._id = new ObjectId().toHexString();
411411
}
412412

413-
const result = await collection.insertOne(mongoDoc);
413+
// Pass session for transactional operations
414+
const mongoOptions = options?.session ? { session: options.session } : {};
415+
const result = await collection.insertOne(mongoDoc, mongoOptions);
414416
// Return API format document (convert _id to id)
415417
return this.mapFromMongo({ ...mongoDoc, _id: result.insertedId });
416418
}
@@ -426,13 +428,17 @@ export class MongoDriver implements Driver {
426428
const isAtomic = Object.keys(updateData).some(k => k.startsWith('$'));
427429
const update = isAtomic ? updateData : { $set: updateData };
428430

429-
const result = await collection.updateOne({ _id: this.normalizeId(id) }, update);
431+
// Pass session for transactional operations
432+
const mongoOptions = options?.session ? { session: options.session } : {};
433+
const result = await collection.updateOne({ _id: this.normalizeId(id) }, update, mongoOptions);
430434
return result.modifiedCount; // or return updated document?
431435
}
432436

433437
async delete(objectName: string, id: string | number, options?: any) {
434438
const collection = await this.getCollection(objectName);
435-
const result = await collection.deleteOne({ _id: this.normalizeId(id) });
439+
// Pass session for transactional operations
440+
const mongoOptions = options?.session ? { session: options.session } : {};
441+
const result = await collection.deleteOne({ _id: this.normalizeId(id) }, mongoOptions);
436442
return result.deletedCount;
437443
}
438444

@@ -552,6 +558,79 @@ export class MongoDriver implements Driver {
552558
return doc ? this.mapFromMongo(doc) : null;
553559
}
554560

561+
// ========== Transaction Support ==========
562+
563+
/**
564+
* Begin a new transaction session
565+
*
566+
* @returns MongoDB ClientSession that can be used for transactional operations
567+
*
568+
* @example
569+
* const session = await driver.beginTransaction();
570+
* try {
571+
* await driver.create('users', { name: 'Alice' }, { session });
572+
* await driver.create('orders', { userId: 'alice' }, { session });
573+
* await driver.commitTransaction(session);
574+
* } catch (error) {
575+
* await driver.rollbackTransaction(session);
576+
* throw error;
577+
* }
578+
*/
579+
async beginTransaction(): Promise<any> {
580+
await this.connected;
581+
const session = this.client.startSession();
582+
session.startTransaction();
583+
return session;
584+
}
585+
586+
/**
587+
* Commit a transaction
588+
*
589+
* @param transaction - MongoDB ClientSession returned by beginTransaction()
590+
*
591+
* @example
592+
* const session = await driver.beginTransaction();
593+
* // ... perform operations with { session } in options
594+
* await driver.commitTransaction(session);
595+
*/
596+
async commitTransaction(transaction: any): Promise<void> {
597+
if (!transaction || typeof transaction.commitTransaction !== 'function') {
598+
throw new Error('Invalid transaction object. Must be a MongoDB ClientSession.');
599+
}
600+
601+
try {
602+
await transaction.commitTransaction();
603+
} finally {
604+
await transaction.endSession();
605+
}
606+
}
607+
608+
/**
609+
* Rollback a transaction
610+
*
611+
* @param transaction - MongoDB ClientSession returned by beginTransaction()
612+
*
613+
* @example
614+
* const session = await driver.beginTransaction();
615+
* try {
616+
* // ... perform operations
617+
* await driver.commitTransaction(session);
618+
* } catch (error) {
619+
* await driver.rollbackTransaction(session);
620+
* }
621+
*/
622+
async rollbackTransaction(transaction: any): Promise<void> {
623+
if (!transaction || typeof transaction.abortTransaction !== 'function') {
624+
throw new Error('Invalid transaction object. Must be a MongoDB ClientSession.');
625+
}
626+
627+
try {
628+
await transaction.abortTransaction();
629+
} finally {
630+
await transaction.endSession();
631+
}
632+
}
633+
555634
async disconnect() {
556635
// Close all active change streams
557636
for (const [streamId, stream] of this.changeStreams.entries()) {

packages/drivers/mongo/test/tck.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,16 @@ describe('MongoDriver TCK Compliance', () => {
4646
return driver;
4747
},
4848
{
49-
// MongoDB supports all features
49+
// MongoDB supports most features
5050
skip: {
51-
// No features to skip
51+
// No features to skip - MongoDB now supports transactions
5252
},
5353
timeout: 30000,
5454
hooks: {
5555
beforeEach: async () => {
56+
// Wait for driver to connect
57+
await driver['connected'];
58+
5659
// Clear all collections
5760
if (driver && driver['db']) {
5861
const collections = await driver['db'].listCollections().toArray();

0 commit comments

Comments
 (0)