I have a springboot application where am integrating mongock to add indices to the database. When I start the application, I see the below exception:
Caused by: io.mongock.api.exception.MongockException: io.mongock.api.exception.MongockException: Error in method[AddIndexToCoaching.addPartialIndexToCoaching] : Command failed with error 72 (InvalidOptions): 'writeConcern is not allowed within a multi-statement transaction' on server my-mongo-clus-shard-00-02.s5go0.mongodb.net:27017. The full response is {"ok": 0.0, "errmsg": "writeConcern is not allowed within a multi-statement transaction", "code": 72, "codeName": "InvalidOptions", "$clusterTime": {"clusterTime": {"$timestamp": {"t": 1724994211, "i": 24}}, "signature": {"hash": {"$binary": {"base64": "39UxbjF63gosdF8ktyThibybXBQ=", "subType": "00"}}, "keyId": 7357981548798279686}}, "operationTime": {"$timestamp": {"t": 1724994211, "i": 24}}}
at io.mongock.driver.mongodb.springdata.v4.SpringDataMongoV4DriverBase.executeInTransaction(SpringDataMongoV4DriverBase.java:99)
at io.mongock.runner.core.executor.ChangeExecutorBase.processChangeLogInTransactionIfApplies(ChangeExecutorBase.java:147)
at io.mongock.runner.core.executor.ChangeExecutorBase.processSingleChangeLog(ChangeExecutorBase.java:119)
The mongo URL being used in the mongo template is mongodb+srv://user:<password>@my-mongo-clus.s5go0.mongodb.net/dbname?retryWrites=true&w=majority
The below is the change unit am trying to add:
@ChangeUnit(id="addPartialIndexToCoaching", order = "0001", author = "user1")
@Slf4j
public class AddIndexToCoaching {
@Execution
public void addPartialIndexToCoaching(MongoTemplate mongoTemplate) {
// Create the index fields and sorting
Document indexFields = new Document()
.append("field1", 1)
.append("field2", 1)
.append("field3", 1);
// Define the partial filter expression using Criteria
Document partialFilterExpression = Criteria.where("status").is("DRAFT").getCriteriaObject();
// Create the IndexOptions with the partial filter expression
IndexOptions options = new IndexOptions().partialFilterExpression(partialFilterExpression);
// Create the index using MongoTemplate
mongoTemplate.getCollection("mycollection").createIndex(indexFields, options);
}
@RollbackExecution
@RollbackBeforeExecution
public void rollback() {
}
}
My main app has the below enabled:
@EnableKafka
@EnableMongoRepositories(basePackages = {
"my.backend.da.repository"})
@EnableMongoAuditing
@EnableAuthorization
@EnableAsync(proxyTargetClass = true)
@EnableScheduling
@EnableCaching
@EnableRetry
@EnableMongock
@EnableSchedulerLock(defaultLockAtMostFor = "${shedlock.defaultLockAtMostFor}")
@EnableTransactionManagement
@EnableCronMonitoring
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Am trying to start the main springboot app with changeunit added to the package and was expecting that the change unit gets applied to the collection. Am not sure what is wrong here. Any help is appreciated.
This is happening because by default Mongock wraps the changeUnit within a transaction. However, MongoDB doesn't allow DDL operations within transactions.
To fix it, you can mark your changeUnit with transactional=false
, as follow:
@ChangeUnit(id="addPartialIndexToCoaching", order = "0001", author = "user1", transactional = false)
@Slf4j
public class AddIndexToCoaching {
@Execution
public void addPartialIndexToCoaching(MongoTemplate mongoTemplate) {
// Create the index fields and sorting
Document indexFields = new Document()
.append("field1", 1)
.append("field2", 1)
.append("field3", 1);
// Define the partial filter expression using Criteria
Document partialFilterExpression = Criteria.where("status").is("DRAFT").getCriteriaObject();
// Create the IndexOptions with the partial filter expression
IndexOptions options = new IndexOptions().partialFilterExpression(partialFilterExpression);
// Create the index using MongoTemplate
mongoTemplate.getCollection("mycollection").createIndex(indexFields, options);
}
@RollbackExecution
@RollbackBeforeExecution
public void rollback() {
}
}