Hi! When i developing a spring-boot project, i tried to check if the @transaction annotation really works by insert duplicate items to the same collection, and then i found something i can’t solve.
The dependencies of this project as follows:
- MongoDB Server 7.0
- spring-boot-starter-parent 3.2.4
- mongodb-driver-sync 5.0
- mongodb-driver-core 5.0
The snippet code for MongoTransactionManager :
@Configuration
@EnableTransactionManagement
class MongoConfig extends AbstractMongoClientConfiguration {
@Value("${mongo.db}")
String database;
@Bean
MongoTransactionManager mongoTransactionManager(MongoDatabaseFactory mongoDatabaseFactory){
MongoTransactionManager manager = new MongoTransactionManager(mongoDatabaseFactory, TransactionOptions.builder()
.readPreference(ReadPreference.primary())
.readConcern(ReadConcern.LOCAL)
.writeConcern(WriteConcern.MAJORITY)
.build());
manager.setRollbackOnCommitFailure(true);
return manager;
}
@Override
protected String getDatabaseName() {
return database;
}
}
The snippet code for Insert:
@Transactional
public void insertMany(String collectionName,
ClientSession session,
List<Document> documentList,
InsertManyOptions options,
Boolean verbose) {
if (Objects.isNull(options)){
options = new InsertManyOptions();
}
long startTime = System.currentTimeMillis();
MongoCollection<Document> collection = collectionMap.get(collectionName);
{
collection.insertMany(documentList, options);
collection.insertMany(documentList.subList(0,100), options);
}
long endTime = System.currentTimeMillis();
if (verbose)
log.info("Collections.insertMany, collection name: {}, size: {}, Time: {}ms", collectionName, documentList.size(), (endTime - startTime));
}
the expected result of running function insertMany is none of the document in the documentList should be inserted to database successfully due to the second insertMany operation would throw an exception, but the result is the first insertMany operation inserted into collection successfully.
the DEBUG logs are:
org.mongodb.driver.connection : Connection checked out: address=localhost:8989, driver-generated ID=5, duration=0 ms
org.mongodb.driver.protocol.command : Command "insert" started on database "form" using a connection with driver-generated ID 5 and server-generated ID 3477 to localhost:8989. The request ID is 9 and the operation ID is 8. Command: {"insert": "single_write_collection", "ordered": true, "txnNumber": 1, "$db": "form", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1711116306, "i": 2}}, "signature": {"hash": {"$binary": {"base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType": "00"}}, "keyId": 0}}, "lsid": {"id": {"$binary": {"base64": "8UzN5xZ0TKmfyIn/lvz8Dg==", "subType": "04"}}}, "documents": [{"_id": {"$oid": "65fd90131cc0173f10682826"}, "task_id": "task_round-0index-0", "template_id": "template_round-0index-0", "tree_id": "tree_round-0index-0", "object_field_0": {"task_id": "task_round-0index-0", "template_id": "template_round-0index-0", "tree_id": "tree_round-0index-0"}, "array_field_0": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "int_field_1": 0, "float_field": 1.0, "date_field_": {"$date": "2024-03-22T22:05:06.335Z"}, "null_field_": null, "true_field_": true, "false_field_": false}, {"_id": {"$oid": "65fd90131cc0173f10682826"}, "task_id": "task_round-0index-0", "template_id": "template_round-0index-0", "tree_id": "tr ...
org.mongodb.driver.protocol.command : Command "insert" succeeded on database "form" in 724.7474 ms using a connection with driver-generated ID 5 and server-generated ID 3477 to localhost:8989. The request ID is 9 and the operation ID is 8. Command reply: {"n": 1, "electionId": {"$oid": "7fffffff0000000000000005"}, "opTime": {"ts": {"$timestamp": {"t": 1711116307, "i": 501}}, "t": 5}, "writeErrors": [{"index": 1, "code": 11000, "errmsg": "E11000 duplicate key error collection: form.single_write_collection index: _id_ dup key: { _id: ObjectId('65fd90131cc0173f10682826') }", "keyPattern": {"_id": 1}, "keyValue": {"_id": {"$oid": "65fd90131cc0173f10682826"}}}], "ok": 1.0, "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1711116307, "i": 502}}, "signature": {"hash": {"$binary": {"base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType": "00"}}, "keyId": 0}}, "operationTime": {"$timestamp": {"t": 1711116307, "i": 501}}}
org.mongodb.driver.connection : Connection checked in: address=localhost:8989, driver-generated ID=5
o.s.d.mongodb.MongoTransactionManager : Initiating transaction rollback
o.s.d.mongodb.MongoTransactionManager : About to abort transaction for session [ClientSessionImpl@5d3f8661 id = {"id": {"$binary": {"base64": "IDoVFI7YQjOiN/7gPUGufw==", "subType": "04"}}}, causallyConsistent = true, txActive = true, txNumber = 1, closed = false, clusterTime = null].
o.s.d.mongodb.MongoTransactionManager : About to release Session [ClientSessionImpl@5d3f8661 id = {"id": {"$binary": {"base64": "IDoVFI7YQjOiN/7gPUGufw==", "subType": "04"}}}, causallyConsistent = true, txActive = false, txNumber = 1, closed = false, clusterTime = null] after transaction.
as you can see, the insert operation returned the errmsg E11000 duplicate key error collection,
and the transaction initialized and released normally.
Why the transaction rollback mechanism not working and how can i get the expected result.
Thank you!