ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 몽고디비 트랜잭션 설정방법
    각종DB사용법 2023. 7. 4. 19:16

    결론을 말하자면, 몽고디비에서 트랜잭션에 관한 설정을 추가하지 않는다면, jpa 처럼 더티체킹이 작동하지 않는다.

    찾아본 결과 mongodb에서는 더티체킹을 지원하지 않는다. 그래서 이건 데이터 변경하고 save로 저장해주는 방법을 사용한다.

    그리고 트랜잭션 설정 방법에 대해 알아보겠다.

    몽고디비 트랜잭션 설정에는 몽고디비 레플리카셋을 설정해줘야한다. 아래에서 docker-compose 로 만들어보겠다.

     

    스프링 2.7.x 버전에서 아래의 dependency를 추가해주자.

    implementation("org.springframework.boot:spring-boot-starter-data-mongodb")

     

    돈을 충전하는 로직이 아래 처럼 있다고 해보자.

    간단히 jpa를 사용한다면 대충 이해될것이다.

        fun charge(userId:String, money: Long, paymentType: PAYMENT_TYPE) {
            if(paymentType.compareTo(PAYMENT_TYPE.CHARGE)==0) {
                val user = userRepository.findByIdOrNull(userId) ?: throw IllegalArgumentException()
    
                //은행에서 돈이 있는지 확인하는 로직 추가하기
    
                paymentRepository.save(
                    PaymentDetails(
                        paymentId = UUID.randomUUID().toString(),
                        paymentType = PAYMENT_TYPE.CHARGE,
                        userId = userId,
                        money = money,
                        sendDate = LocalDateTime.now()
                    )
                )
                user.money+=money
                userRepository.save(user) //이렇게 업데이트 해주자.
            }
            else {
                log.info("충전 실패 : type 에러")
            }
        }

    paymentType이 charge 라면 충전을 할건데, 실제 은행이랑 연동은 못하므로 userRepository에서 유저를 찾아서 user.money+=money로 늘려주고 save로 저장해준다.

    @Transactional
        fun charge(userId:String, money: Long, paymentType: PAYMENT_TYPE) {
            if(paymentType.compareTo(PAYMENT_TYPE.CHARGE)==0) {
                val user = userRepository.findByIdOrNull(userId) ?: throw IllegalArgumentException()
    
                //은행에서 돈이 있는지 확인하는 로직 추가하기
    
                paymentRepository.save(
                    PaymentDetails(
                        paymentId = UUID.randomUUID().toString(),
                        paymentType = PAYMENT_TYPE.CHARGE,
                        userId = userId,
                        money = money,
                        sendDate = LocalDateTime.now()
                    )
                )
                user.money+=money
                userRepository.save(user)
                throw IllegalArgumentException("트랜잭션 잘되는지")
            }
            else {
                log.info("충전 실패 : type 에러")
            }
        }

    이렇게 만들면, 로직을 수행하고 마지막에 에러가 난다.

     

    몽고디비 트랜잭션 처리에 대해 찾아봤다.

    https://www.baeldung.com/spring-data-mongodb-transactions

    를 보고 해보자.

     

    설정파일을 하나 만들어주자.

    @Configuration
    class MongoConfig: AbstractMongoClientConfiguration() {
        @Bean
        fun transactionManager(dbFactory: MongoDatabaseFactory): MongoTransactionManager {
            return MongoTransactionManager(dbFactory)
        }
    
        override fun getDatabaseName(): String {
            return "pay-kotlin"
        }
    
        override fun mongoClient(): MongoClient {
            val connectionString = ConnectionString("mongodb://localhost:30000/pay-kotlin")
            val mongoClientSettings = MongoClientSettings.builder()
                .applyConnectionString(connectionString)
                .build()
            return MongoClients.create(mongoClientSettings)
        }
    }

     

    docker-compose로 몽고디비를 설정해보겠다.

    version: "3"
    
    services:
      mongodb1:
        image: mongo
        container_name: mongodb1
        restart: always
        command: mongod --replSet rs0 --port 30000
        ports:
          - 30000:30000
        volumes:
          - ./mongodb1:/data/db
    
      mongodb2:
        image: mongo
        container_name: mongodb2
        restart: always
        command: mongod --replSet rs0 --port 30001
        ports:
          - 30001:30001
        volumes:
          - ./mongodb2:/data/db
        depends_on:
          - mongodb1
    
      mongodb3:
        image: mongo
        container_name: mongodb3
        restart: always
        command: mongod --replSet rs0 --port 30002
        ports:
          - 30002:30002
        volumes:
          - ./mongodb3:/data/db
        depends_on:
          - mongodb2

    이렇게 만들고

    docker-compose up -d

    를 하자.

     

    docker-compose를 설명하자면,

    mongo이미지를 받아와서, mongod --replSet rs0 --port 30000 명령어를 실행해준다.

    depends_on 으로 mongodb1이 켜지고, mongodb2, mongodb3 이렇게 켜진다.

    명령어 설명은 replSet이 레플리카셋 이름을 rs0 으로 설정해준다는 뜻이다.

    마운트를 했기 때문에 껐다가 다시 켜도 데이터가 유지된다.

     

    켜주고 로그를 봐보자.

    "NotYetInitialized: Cannot use non-local read concern until replica set is finished initializing.","nextWakeupMillis":2600

    아직 초기화 하지 않았다고 한다.

     

    보면 Terminal 탭이 있고, 거기를 클릭한다음,

    mongosh --port 30000

    으로 접속해주자.

    이제 레플리카 설정을 해야한다.

    config = {
    	"_id" : "rs0",
    	"members" : [
    		{
    			"_id" : 0,
    			"host" : "mongodb1:30000"
    		},
    		{
    			"_id" : 1,
    			"host" : "mongodb2:30001"
    		},
    		{
    			"_id" : 2,
    			"host" : "mongodb3:30002"
    		}
    	]
    }

    를 하고, 

    rs.initiate(config);

    를 하자.

    이렇게 설정이 잘 된다.

    mongodb2 와 mongodb3 도 들어가보자.

    잘 설정되었다.

     

    이제 mongodbCompass에서 primary 인 mongodb1 에 접속해보자.

    이렇게 하고 connect를 하자.

    하지만 연결이 되지 않는다.

     

    필자는 윈도우여서, /etc/hosts가 없다.

    https://offbyone.tistory.com/149

     

    Windows 10에서 hosts 파일 수정하기

    hosts 파일을 문자로된 이름을 숫자로 된 IP 주소로 매칭 시켜 주는 정보를 담고 있는 파일입니다. 우리가 인터넷상의 서버에 접속하기 위해서 주로 사용하는 이름은 offbyone.tistory.com 과 같은 도메

    offbyone.tistory.com

    를 통해 메모장을 관리자권한으로 열고 hosts를 열고 맨 아래에 밑에처럼 써주자.

    127.0.0.1 mongodb1
    127.0.0.1 mongodb2
    127.0.0.1 mongodb3

    그러면 연결이 잘 된다.

    이제 테스트를 트랜잭션 어노테이션을 빼고 시험해보고, 넣고 시험해보면 잘 작동하는것을 볼 수 있다.

     

    추가로 30001과 30002도 연결하고, 데이터를 하나 추가하면 30000과 30001, 30002 모두 데이터가 반영되는걸 볼 수 있다.

Designed by Tistory.