1. 개발한 소스에 동시성 테스트 실시
1000명의 사용자가 10초 내 30개의 재고상품이 있는 상품을 구매할 수 있는 동시성(부하테스트)를 실행한 결과이다.
서비스 로직을 보면,
const result: boolean = await this.prisma.$transaction(async (tx) => {
로 트랜젝션 단위를 보장하고 있는데, 이러한 구조로 인해 일부 동시성 이슈가 나타나지 않는것이다.
좀더 자세히 설명하면??
1) 트랜잭션 사용
: prisma.$transaction 내에서 여러 DB 작업을 묶어서 원자적으로 처리할 수 있는데, 트랜잭션이 끝날 때까지 중간에 다른 요청이 개입할 수 없다. 트랜잭션이 성공적으로 커밋되기 전에는 데이터 변경이 실제로 반영되지 않기 때문에, 두 사용자가 동시에 주문할 때도 트랜잭션을 통해 하나의 주문만 먼저 처리되고, 그 후에 다른 주문이 처리된다.
2) 재고 수량 차감 로직
: 재고를 감소시키는 부분에서 "await this.productRepository.updateProductByIds(detailWithOrderNo.product_id, detailWithOrderNo.qty, tx)"가 트랜잭션 내에서 처리되기 때문에, 해당 상품의 재고를 업데이트하는 시점까지 다른 트랜잭션에서 같은 상품의 재고를 차감하려고 해도 트랜잭션을 기다리게 된다.
3) DB 락 (데이터베이스의 잠금)
: Prisma를 사용한 트랜잭션에서는 보통 데이터베이스 자체의 잠금 메커니즘이 동작하게 되는데, 재고가 부족한 상품을 주문하려 할 때, 하나의 트랜잭션에서 UPDATE 문을 실행하여 재고를 차감하고, 다른 트랜잭션은 해당 행을 수정할 수 없도록 row level 락을 걸게 된다.(비관적락 발생)
4) 순차적 처리
: 주문 내역이 담긴 orderDetails 배열을 순차적으로 처리하면서 각 주문이 완료된 후에만 재고 업데이트가 이루어지기 때문에, 여러 사용자가 동시에 재고를 업데이트하려는 상황에서 하나의 트랜잭션이 끝날 때까지 다른 트랜잭션이 대기하게 된다.
2. 여전히 동시성 발생가능성은 존재
: 하지만, 다른 외부 트랜젝션의 개입으로 재고 수량에 대한 동시성 이슈가 발생할 가능성은 일부 존재한다. 이를 해결하기 위한 조치로는 상품정보를 조회하는 시점에 low level 락을 걸어서(update 하는 시점이 아닌) 동시성 이슈를 더 타이틀하게 보장하는 것이다.
async getProductByIdsWithLock(
productIds: Array<number>,
tx: Prisma.TransactionClient = this.prisma,
): Promise<product[]> {
const productInfo = await tx.$queryRaw<product[]>`
SELECT *
FROM product
WHERE id IN (${Prisma.join(productIds)})
FOR UPDATE;
`;
return productInfo;
}
https://www.prisma.io/docs/orm/prisma-client/using-raw-sql/raw-queries
Raw queries | Prisma Documentation
Learn how you can send raw SQL and MongoDB queries to your database using the raw() methods from the Prisma Client API.
www.prisma.io
'Node js > Nest js 강의 내용' 카테고리의 다른 글
8. Redis 연동 (장바구니 리펙토링) (0) | 2025.01.27 |
---|---|
7. Docker (Nest js 로 개발한 서버를 docker로 띄워보자) (0) | 2025.01.27 |
6-1 서비스 로직 완성 (0) | 2025.01.27 |
5-2. repository 완성 ( with Prisma ) (0) | 2024.12.06 |
5-1. prisma 기본 (0) | 2024.12.06 |