I am using Spring-Boot 3.0.4
and I have upgraded Mongock from 4.3.8
to 5.2.2
.
pom.xml snipped
<dependencies>
<dependency>
<groupId>io.mongock</groupId>
<artifactId>mongock-springboot</artifactId>
</dependency>
<dependency>
<groupId>io.mongock</groupId>
<artifactId>mongodb-springdata-v4-driver</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.mongock</groupId>
<artifactId>mongock-bom</artifactId>
<version>5.2.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
I have updated the old @Changelog
and @ChangeSet
annotations as explained in the doc https://docs.mongock.io/v5/migration/index.html, and similarly for the transactions https://docs.mongock.io/v5/features/transactions/index.html
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoTransactionManager;
@Configuration
public class DatabaseTransactionConfiguration {
@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory factory) {
return new MongoTransactionManager(factory);
}
}
package org.project.configuration.database.migration;
import io.mongock.api.annotations.*;
import org.project.domain.Questionnaire;
import org.project.service.QuestionnaireService;
import org.springframework.data.mongodb.core.MongoTemplate;
@ChangeUnit(id="questionnaire-initializer", order = "6", author = "dev", runAlways = true)
public class CU06_Questionnaire {
@BeforeExecution
public void beforeExecution(MongoTemplate mongoTemplate) {
if (!mongoTemplate.collectionExists(Questionnaire.class)) {
mongoTemplate.createCollection(Questionnaire.class);
}
}
@RollbackBeforeExecution
public void rollbackBeforeExecution(MongoTemplate mongoTemplate) {
}
@Execution
public void execution(QuestionnaireService questionnaireService) {
Questionnaire questionnaire = questionnaireService.loadQuestionnaireData();
questionnaireService.save(questionnaire);
}
@RollbackExecution
public void rollbackExecution(QuestionnaireService questionnaireService) {
Questionnaire questionnaire = questionnaireService.loadQuestionnaireData();
questionnaireService.delete(questionnaire);
}
}
application.yaml
spring:
data:
mongodb:
host: ${MONGODB_HOST:localhost}
port: ${MONGODB_PORT:27017}
database: ${MONGODB_DATABASE:project}
auto-index-creation: true
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
mongock:
migration-scan-package: org.project.configuration.database.migration
transaction-enabled: true
however I'm having the following error when starting the application:
io.mongock.api.exception.MongockException: Error in
method[CU06_OTQuestionnaire.execution] : Command failed with error 251
(NoSuchTransaction): 'Transaction with { txnNumber: 30 } has been aborted.' on server
localhost:27017. The full response is {"errorLabels": ["TransientTransactionError"],
"ok": 0.0, "errmsg": "Transaction with { txnNumber: 30 } has been aborted.", "code":
251, "codeName": "NoSuchTransaction", "$clusterTime": {"clusterTime": {"$timestamp":
{"t": 1679076061, "i": 99}}, "signature": {"hash": {"$binary": {"base64":
"AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "subType": "00"}}, "keyId": 0}}, "operationTime":
{"$timestamp": {"t": 1679076061, "i": 99}}}
What am I missing that causes this exception between Mongock and the Transactios?
Update
As suggested I fixed the Mongock Runner dependency:
<dependency>
<groupId>io.mongock</groupId>
<artifactId>mongock-springboot-v3</artifactId>
</dependency>
and I improved the MongoTransactionManager
@Bean
too:
@Bean
public MongoTransactionManager transactionManager(MongoTemplate mongoTemplate) {
TransactionOptions transactionalOptions = TransactionOptions.builder()
.readConcern(ReadConcern.MAJORITY)
.readPreference(ReadPreference.primary())
.writeConcern(WriteConcern.MAJORITY.withJournal(true))
.build();
return new MongoTransactionManager(mongoTemplate.getMongoDatabaseFactory(), transactionalOptions);
}
However, the exception remains, but only with the Questionnaire
class and the corresponding @ChangeUnit
. If I comment out the @ChangeUnit
annotation on the CU06_Questionnaire
migration class, all the other migrations work without any problem.
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.project.domain.enumeration.QuestionnaireType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
@Data
@Builder
@Document
@NoArgsConstructor
@AllArgsConstructor
public class Questionnaire {
@Id
private String id;
@Transient
@JsonIgnore
@Builder.Default
private Class<String> idType = String.class;
private String name;
private String description;
@Indexed(unique = true)
private QuestionnaireType type;
private List<String> questions;
}
@RequiredArgsConstructor
@Getter
public enum QuestionnaireType {
A(0), B(1);
private final int value;
}
And this is the corresponding JSON that I'm loading:
{
"id": Questionnaire-001",
"name": "Questionnaire 001",
"description": "Questionnaire to ...",
"type": "A",
"questions": [
"Question-0000",
"Question-0001",
"Question-0002",
"Question-0003",
"Question-0004",
"Question-0005",
"Question-0006",
"Question-0007",
"Question-0008",
"Question-0009",
"Question-0010"
]
}
Minimal Non-Working Example
Mongock provides two specific modules to work with springboot (v3) and springdata (v4). You need to use the springboot v3 dependency as follow:
<dependency>
<groupId>io.mongock</groupId>
<artifactId>mongock-springboot-v3</artifactId>
</dependency>
This sample project is currently working with those dependencies.
UPDATE:
After review your Minimal Non-Working Example, the error is due to Mongock is generating a proxy instance for your injected QuestionnaireService
dependency and its return value Questionnaire
. In this case, although proxing the QuestionnaireService
injection is highly recommended(so the lock is guaranteed), the actual returned items(Questionnaire
) don't need to and actually, in this case, it's producing the error.
So the suggested solution is to annotate your dependency QuestionnaireService
as follow:
@Execution
public void execution(@NonLockGuarded(NonLockGuardedType.RETURN) QuestionnaireService questionnaireService, QuestionnaireRepository questionnaireRepository) {
...
}
Please refer to our official documentation about Mongock's proxy mechanism and how to handle it at the following link: