NoSql인 MongoDB가 Transaction을 지원하지만,
방대한 데이터를 다루는 NoSql에서는 Transaction과 같은 고비용 처리 사용을 지양한다.
그럼에도 불구하고, Test code에서 MongoDB Transaction을 Rollback하기 위해
@Transactional Annotation 사용하려고 시도 했던 것을 정리

1. Test code에 @Transactional Annotaiton 추가

Test code에 바로 @Transactional Annotaiton을 작성했을 때 오류가 발생함

@Test
@Transactional
public void test1() throws Exception{
   // ...something
}
  • Error Message
java.lang.IllegalStateException: 
Failed to retrieve PlatformTransactionManager for @Transactional test
  • Spring에서 MongoDB Transaction을 사용하기 위해서는 Configuration을 추가해 줘야함

2. Spring Configuration 추가

  • /test/java/com/.../config/MongoTransactionConfig.java
  • Test Code에서만 Configuraction 추가
@Configuration
public class MongoTransactionConfig {
    @Bean
    public MongoTransactionManager transactionManager(MongoDatabaseFactory mongoDatabaseFactory) {
        return new MongoTransactionManager(mongoDatabaseFactory);
    }
}
  • 이후 발생한 오류
org.springframework.dao.InvalidDataAccessApiUsageException: 
Query failed with error code 20 and error message 
'Transaction numbers are only allowed on a replica set member or mongos'
on server dev-mongodb-ip:27017; 
nested exception is com.mongodb.MongoQueryException: 
Query failed with error code 20 and error message 
'Transaction numbers are only allowed on a replica set member or mongos' 
on server dev-mongodb-ip:27017
  • MongoDB Transaction을 사용하기 위해서는 MongoDB Replica set 구성을 해야만 한다.

3. Docker로 MongoDB Replicaset 구성

  • File 구조
├── js
│   └── dbUserCreate.js
│── scripts
│   ├── mongoCreateUser.sh
│   └── rs-init.sh
├── replicaset
│   ├── rs01a
│   ├── rs01p
│   └── rs01s   
├── .env
├── docker-compose.yml
└── restart-mongodb-replica.sh
  • .env
MONGODB_VERSION=5.0.18
  • docker-compose.yml
version: '3.8'
services:
  rs01p:
    image: mongo:${MONGODB_VERSION}
    container_name: rs01p
    ports:
      - 10021:10021
    volumes:
      - ./replicaset/rs01p:/data/db
      - ./scripts:/scripts
      - ./js:/js
    command:
      - '--port'
      - '10021'
      - '--replSet'
      - 'rs01'
      - '--bind_ip_all'
  rs01s:
    image: mongo:${MONGODB_VERSION}
    container_name: rs01s
    ports:
      - 10022:10022
    volumes:
      - ./replicaset/rs01s:/data/db
    command:
      - '--port'
      - '10022'
      - '--replSet'
      - 'rs01'
      - '--bind_ip_all'
  rs01a:
    image: mongo:${MONGODB_VERSION}
    container_name: rs01a
    ports:
      - 10023:10023
    volumes:
      - ./replicaset/rs01a:/data/db
    command:
      - '--port'
      - '10023'
      - '--replSet'
      - 'rs01'
      - '--bind_ip_all'
  • rs-init.sh
    • replicaset 설정 진행
mongo --port 10021 <<EOF
use databaseName;
var config = {
    "_id": "rs01",
    "version": 1,
    "members": [
        {
            "_id": 1,
            "host": "rs01p:10021",
            "priority": 2
        },
        {
            "_id": 2,
            "host": "rs01s022",
            "priority": 1
        },
        {
            "_id": 3,
            "host": "rs01a:10023",
            "priority": 0
        }
    ]
};
rs.initiate(config);
rs.status();
db.setProfilingLevel(2);
db.getProfilingStatus();
EOF
  • mongoCreateUser.sh
    • DB User 데이터 생성
    • use databaseName을 통해 사용할 DB 선택
mongo --port 10021 <<EOF
use databaseName;
db.createUser({
    user: "mongo",
    pwd: "mongo123", 
    roles: [
      {
        role: "dbOwner",
        db: "databaseName",
      },
    ],
});
db.getUsers();
EOF
  • restart-mongodb.sh
    • 쉽게 컨테이너 reset하기 위해 스크립트 작성
    • ./restart-mongodb.sh 쉘 입력
DATA_FILE_PATH="./replicaset"
DOCKER_FILE_PATH="./docker-compose.yml"
MONGO_PRIMARY_NAME="rs01p"
REPLICA_INIT_FILE_PATH="/scripts/rs-init.sh"
MONGO_CREATE_USER_FILE_PATH="/scripts/mongoCreateUser.sh"

UP_CONTAINER_DELAY=10
REPLICA_CONFIG_DELAY=25
SHARD_CONFIG_DELAY=60

echo "****************** Reset docker container Shell Script ******************"
echo "Data File Path: ${DATA_FILE_PATH}"
echo "Docker File Path: ${DOCKER_FILE_PATH}"
echo "MongoDB Primary name: ${MONGO_PRIMARY_NAME}"
echo "Replica set init Script File Path: ${REPLICA_INIT_FILE_PATH}"

sleep 1;

echo "****************** Stop docker container ******************"
docker-compose -f ${DOCKER_FILE_PATH} stop
echo "****************** Completed Stop docker container ******************"

sleep 1;

echo "****************** Down docker container ******************"
docker-compose -f ${DOCKER_FILE_PATH} down
echo "****************** Completed Down docker container ******************"

sleep 1;

echo "****************** Remove Data ******************"
rm -rf ${DATA_FILE_PATH}
echo "****************** Completed Remove Data ******************"

sleep 1;

echo "****************** Up docker container ******************"
docker-compose -f ${DOCKER_FILE_PATH} up -d 
echo "****************** Completed Up docker container ******************"

echo "****** Waiting for ${UP_CONTAINER_DELAY} seconds ******"
sleep $UP_CONTAINER_DELAY;

echo "****************** Run Replica Set Shell Script ******************"
docker exec -it ${MONGO_PRIMARY_NAME} bash -c "${REPLICA_INIT_FILE_PATH}"

echo "****** Waiting for ${REPLICA_CONFIG_DELAY} seconds for replicaset configuration to be applied ******"
sleep $REPLICA_CONFIG_DELAY

echo "****************** Run Create DB User Shell Script ******************"
docker exec -it ${MONGO_PRIMARY_NAME} bash -c "${MONGO_CREATE_USER_FILE_PATH}"

echo "****************** Completed Replica Shell Script ******************"

Configuration + Replica set 구성을 통해
Test Code에서 @Transactional Annotation 사용 가능

Reference

  • Spring Boot MongoDB Transaction Manager (링크)
  • docker compose로 MongoDB Replica set 구성 (링크)
profile
Junior BE Developer

0개의 댓글

Powered by GraphCDN, the GraphQL CDN