Home > Software engineering >  Writes to two different collections in a single transaction
Writes to two different collections in a single transaction

Time:05-05

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}}}

CodePudding user response:

Well 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.

CodePudding user response:

The the solution to this was much simpler than I thought, Document One didn't store any meaningful data rather it supports Two 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, and update() would execute non-transactionally as I don't care too much about the data there and it stopped exceptions from being thrown

class OneUpdater{
    @Transactional(propagation = Propagation.NOT_SUPPORTED
    int update() {
     // searches for single document with the same id and increments value by 1
     return mongoTemplate.findAndmodify().getValue();
    }
}
  • Related