I have encountered an issue in the application that I’m working with where in a single transaction I am trying to update a document in one collection,
and use the updated value in another document when creating it as I have added a bulk registration of the second document.
this results in TransientTransactionError
and a WriteConflict
log
This has kind of worked for single documents so far, until you have tried to repeat the process again.
Is this even possible?
I have tried using different propagation levels such as REQUIRES_NEW and NESTED (not supported) with no luck on the oneUpdater.update() method, originally it w as implemented without @Transactional
@Document
class One {
String id;
int value;
}
@Document
class Two {
String id;
String currentOneValue;
}
class OneUpdater{
int update() {
// searches for single document with the same id and increments value by 1
return mongoTemplate.findAndmodify().getValue();
}
}
class Service {
private final OneUpdater oneUpdater; // uses mongoTemplate.findAndModify to increment value by 1
private final TwoRepository twoRepository; extends MongoRepository
@Transactional
void createTwo {
int value = oneUpdater.update();
Two t = new Two(UUID.randomUuid.toString(), value);
twoRepository.save(t);
}
}
Caused by: com.mongodb.MongoCommandException: Command failed with error 112 (WriteConflict): 'WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.' on server localhost:27017. The full response is {"errorLabels": ["TransientTransactionError"], "operationTime": {"$timestamp": {"t": 1651680481, "i": 2}}, "ok": 0.0, "errmsg": "WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.", "code": 112, "codeName": "WriteConflict", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1651680481, "i": 2}}, "signature": {"hash": {"$binary": {"base64": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType": "00"}}, "keyId": 0}}}
2
Answers
The the solution to this was much simpler than I thought, Document
One
didn't store any meaningful data rather it supportsTwo
in providing some additional metadata.My solution to this problem was to annotate the
update()
method with@Transactional(propagation = Propagation.NOT_SUPPORTED
so my active transaction would be suspended, andupdate()
would execute non-transactionally as I don't care too much about the data there and it stopped exceptions from being thrownWell Mongo already gave you good advise or "… or multi-document transaction".
Please see Spring example with configuring MongoTransactionManager https://spring.io/blog/2018/06/28/hands-on-mongodb-4-0-transactions-with-spring-data
In this example operation modifies exactly 2 documents.