운영체제에서 race condition이라는 상황에 대해서 들어보았다.

race condition이란
둘 이상의 프로세스(또는 스레드)가 동시에 공유 자원(Shared Resource)에 접근하여 데이터를 수정할 때, 실행 순서(interleaving)에 따라 결과가 달라지는 현상
본래 서버에 어떤 요청이 랜덤하게 들어와도 그 결과는 같아야하는데 , 어떤 요청의 순서에 따라서 그 결과값이 달라진다면 , 그것은 서버의 Integrity , 완전함을 해치는 요소라서 클라이언트에게 모두 동일한 상태의 서버를 제공해 줄 수가 없는 것입니다.
Race Condition 중에서 발생할 수 있는 흔한 경우 중 하나가 바로 중복 Race이다.
동일한 생성 요청이 동시에 혹은 굉장히 짧은 시간내에 두개 이상의 요청이 들어올때, 중복 생성되는 경우를 중복 Race라고 합니다.

가장 근본적인 해결책입니다.
DB에서 근본적으로 동일한 값을 넣지 못하도록 제약을 걸어두는 방식입니다.
model User {
id Int @id @default(autoincrement())
email String @unique
}
Unique Constraint, Unique Index 설정을 통해 DB 상에서 중복 생성을 막는 방식입니다.
try {
return await prisma.$transaction(async (tx) => {
const exists = await tx.user.findUnique({ where: { email } });
if (exists) {
throw new ConflictException('이미 존재하는 이메일입니다.');
}
return tx.user.create({
data: { email },
});
});
} catch (e) {
if (e.code === 'P2002') {
throw new ConflictException('이미 존재하는 이메일입니다.');
}
throw e;
}
Prisma Interactive Transaction 방식을 사용해서,
async function을 넘기면, 그 함수 안에서 tx로 실행한 모든 Prisma 쿼리가 하나의 트랜잭션에 포함되는 방식입니다.
이 방식을 사용하게 되면 이미 존재하는 데이터의 경우 에러를 던지며, Prisma가 자동으로 rollback하는 기능이 추가되어, 중복 처리를 할 수 있다는 장점이 있습니다.
하나의 쿼리문이므로 결국 INSERT는 실행되지 않습니다.
동시에 같은 요청 , 생성 요청 등 중복 Race가 발생하게 된다면, Redis가 먼저 그 요청을 확인 , 자원에 대한 Lock을 Redis가 구현하게 되는 방식입니다.
Redis는 메모리에 존재하기 때문에 DB보다 더 사용성이 높고 , 더 빠르게 접근 가능하기 때문에 더 반응성이 좋게 반응 할 수 있다.
const lockKey = `lock:user:${email}`;
const lockValue = crypto.randomUUID();
const acquired = await redis.set(lockKey, lockValue, 'NX', 'EX', 5);
// Lock을 생성 및 자원 점유 !
if (!acquired) {
throw new ConflictException('이미 처리 중인 요청입니다.');
//redis에서 이미 처리중인 요청인지 확인
}
try {
// 중복되면 안 되는 작업
} finally {
// 내가 잡은 락일 때만 삭제
}
하지만 결국 중요한것은 Redis Lock 은 보조적인 수단이며 ,
근본적인 해결책은 DB상에서의 Unique Index, Constraint 를 걸어놔야지만 된다는것.
Redis Lock이 근본적인 해결책은 아니라는것을 명심해야한다!
가장 근본적이며 많이 채택하는 구조는
Client
│
▼
Redis Lock 획득
│
▼
Prisma Transaction 시작
│
├── SELECT
├── INSERT
├── UPDATE
└── ...
│
Commit / Rollback
│
▼
Redis Unlock
언제나 이 Transaction 관리가 백엔드 개발자에게 가장 중요한 덕목인것같다.
서버를 안정화 시키며 무수한 요청에 따라서 서버가 바뀌는것이 아니라, 서버는 항상 일정한 상태를 유지하는 것
그것에 대한 고민을 계속해서 해야하는것이 잘하는 개발자의 덕목중에 하나인것같다.
