1

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

1Z10
  • 2,801
  • 7
  • 33
  • 82

1 Answers1

1

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:

Mongock Proxies

Mongock team
  • 1,188
  • 5
  • 9
  • Thanks for the answer. I updated the dependency and the TransactionManager @Bean, however, the exception remains. It only happens with the Questionnaire class, even if it is very simple. I'll update the question with further details. – 1Z10 Mar 17 '23 at 20:42
  • Could you provide the complete Mongock's output log? – Mongock team Mar 17 '23 at 21:26
  • I can’t view the content with the provided link, may it be private? – Mongock team Mar 17 '23 at 22:13
  • Sorry, it is waiting for moderation. Please try here https://justpaste.it/6qxwr – 1Z10 Mar 17 '23 at 22:28
  • Ok now it works. Could you try it with the Mongock’s current version 5.2.4? I think it won’t solve the issue, but lets give a try. – Mongock team Mar 17 '23 at 22:42
  • Hi, I tried to update the Mongock version and also to downgrade from Java 19 to Java 17 but the error remains. I spent the last days to reduce the project to a Minimal Non-Working Example. Please find the project with a Docker-Compose for MongoDB 6.0.5 too, at https://github.com/MaurizioCasciano/Mongock-Example/tree/NoSuchTransaction – 1Z10 Mar 20 '23 at 17:18
  • 1
    Ok, we'll try to reproduce it and let you know. – Mongock team Mar 20 '23 at 18:57
  • 1
    I've just updated the answer with the fix. – Mongock team Mar 20 '23 at 21:02
  • It might help if Mongock users were made more aware that '@BeforeExecution' is where most of the in-memory work should be completed. I literally have nothing in '@Execution' and now treat the empty '@Execute' method like it is flush or persist command and put all in memory work in '@BeforeExecution'. I'm not sure that is a proper analogy to sql transactions, but it is working for me so far. – tlarson Jul 02 '23 at 21:33
  • @tlarson, I am not sure what you mean by "in-memory work", but '@Execution' is where the actual change(potentially transactional) should be placed and the '@BeforeExecution' is where all the non-transactional work should be. For example, MongoDB doesn't allow having DDL operations within a transaction. So if you want to have an "atomic" change that creates a collection and insert some documents afterwards, you should create the collection in the '@BeforeExecution' method and the insertions in the '@Execution' method. – Mongock team Jul 03 '23 at 23:50