23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
RDB같은 경우에는 atomic성이 항상 보존되기 때문에 이에 대한 생각을 할 필요가 없지만,
mongoDB는 그렇지 않기 때문에 일련의 과정으로 처리해야할 경우 session을 사용해서 이를 보장해야 한다. (mongodb 4.0 version 부터 사용 가능하다.)
session 기능은 standalone에서는 동작하지 않기 때문에 replica set 구성을 해 주어야 한다.
예시를 작성할 때 nestjs를 사용하여 작성하였고 동작 방법에 대해 간단히 요약해서 말하자면 아래와 같은 순서로 동작한다고 볼 수 있다.
session을 시작한다.
DB에 접근할 때 session 정보를 같이 전달한다.
모든 session이 정상적으로 완료되었으면 commit을 한다.
만약 중간에 에러가 발생하면 commit을 하지 않고 session을 abort 시킨다.
session을 종료한다.
docker로 mongoDB replica Set 띄우기
우선 session기능을 사용하기 위해서는 replica set 구조가 되어있어야 하기 때문에 이를 먼저 적용한다.
docker는 이미 사용하고 있다고 가정하고 docker-compose를 이용해서 replica set을 구성하도록 하겠다.
본인의 경우 27017,27018,27019 port로 3개의 db를 띄워서 사용하였다.
version: '3'
services:
mongo1:
image: mongo:5.0
command: --replSet replDb --bind_ip_all --port 27017
volumes:
- /Users/ji-hyeonyu/dev/test_db:/data/db
# 본인이 사용할 경로로 지정
ports:
- 27017:27017
healthcheck:
test: test $$(mongosh --port 27017 --quiet --eval "rs.initiate({_id:\"replDb\",members:[{_id:0,host:\"mongo1:27017\"},{_id:1,host:\"mongo2:27018\"},{_id:2,host:\"mongo3:27019\"}]}).ok || rs.status().ok") -eq 1
interval: 20s
timeout: 10s
retries: 6
mongo2:
image: mongo:5.0
command: --replSet replDb --bind_ip_all --port 27018
volumes:
- /Users/ji-hyeonyu/dev/test_db1:/data/db
# 본인이 사용할 경로로 지정
ports:
- 27018:27018
mongo3:
image: mongo:5.0
command: --replSet replDb --bind_ip_all --port 27019
volumes:
- /Users/ji-hyeonyu/dev/test_db2:/data/db
# 본인이 사용할 경로로 지정
ports:
- 27019:27019이제 docker-compose up -d를 하면 mongodb 5.0 version을 다운로드하고 실행한 모습을 볼 수 있다.
이처럼 docker app에서 잘 보인다면 cmd 창에서 제대로 동작하는지를 확인해보자
docker ps 명령어를 이용한다.
이제 replica set으로 잘 동작하고 있는지 확인해 보자.
docker exec -it docker_mongo1_1 /bin/bash
위 명령어를 통해서 container 안으로 접근한다.
mongo --port 27017을 통해서 mongo app을 접근한다. PRIMARY> 라고 보이면 정상적으로 실행 중임을 확인할 수 있다.
rs.status() 명령어를 통해서도 확인할 수 있다.
이제 mongoDB는 켜져있으나 실제로 접근은 알 될 것이다.
왜냐하면 아래의 members 정보로 initialize 가 되어 있는데 mongo1, mongo2, mongo3의 host 정보를 모르기 때문이다.
(dns mapping 과정에 따라서 /etc/hosts file에 정의를 하면 된다. dns lookup 과정을 참조하면 된다.)
[{_id:0,host:\"mongo1:27017\"},{_id:1,host:\"mongo2:27018\"}ㅣ,{_id:2,host:\"mongo3:27019\"}]이를 해결하기 위해서 /etc/hosts 파일을 수정해주자.
sudo vi /etc/hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
127.0.0.1 mongo1
127.0.0.1 mongo2
127.0.0.1 mongo3
# Added by Docker Desktop
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section이렇게 mongo1, mongo2, mongo3에 대한 host를 정의해준다. 이제 제대로 연결이 된다.
Session 사용해서 여러 collection atomic하게 다루기 (nest js)
nestjs를 사용해서 작성했고 아마 다른 언어를 쓴다고 해도 크게 차이가 없을 거라고 생각한다.
예시 코드는 예시 git 에 올려놓았다. stock인 이유는 복기하기 위한 저장소를 만드려고 했는데 그냥 예시로 쓰려고 한다.
#src/post/post.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { PostRepository } from 'src/post/post.repository';
import { Posts } from 'src/post/post.schema';
import { UserRepository } from 'src/user/user.repository';
import { User } from 'src/user/user.schema';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
@Injectable()
export class PostsService {
constructor(
private readonly userRepo: UserRepository<User>,
private readonly postRepo: PostRepository<Posts>,
) { }
async create(input: CreatePostDto) {
//session 을 연다.
const session = await this.userRepo.getSession();
session.startTransaction();
try {
// post를 create하면 user의 total post갯수를 증가시킨다.
const user = await this.userRepo.update(input.author, { $inc: { totalPosts: 1 } }, session);
// post를 생성한다.
const post = await this.postRepo.create({ title: input.title, content: input.content, author: user._id, date: new Date(), index: user.totalPosts }, session);
// 정상적으로 끝나면 session을 commit하여 정상적임을 알린다.
await session.commitTransaction();
return post;
} catch (error) {
//중간에 error 발생 시 session을 abort한다.
await session.abortTransaction();
throw error;
} finally {
// 종료 시 session을 닫는다.
await session.endSession();
}
}
async update(id: string, input: UpdatePostDto) {
return this.postRepo.update(id, input);
}
async delete(id: string) {
return this.postRepo.delete(id);
}
}위와 같이 사용하였다. 이제 create는 두개의 collection을 atomic하게 사용할 수 있다. 실제로 error를 주입하여 사용해 보면 abort 발생 시 다 롤백되는 것을 확인할 수 있다.
이제 mongoDB 사용 시 유지 보수를 훨씬 더 간편하게 해주고 DB 정합성을 신경쓰지 않아도 된다.
주의할 점
같은 DB를 사용해야 session 사용이 가능하다. replica set으로 묶여있는 DB는 괜찮지만,
아예 다른 mongoDB 두개를 상용하려고 하면 DB가 관리하는 session 정보가 맞지 않기 때문에 에러가 발생한다.
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.