이전글: [동시성] 낙관적 락 & 비관적 락
이번엔 실습을 통해 눈으로 느낄 수 있는 특징이나 에러들을 정리했다.
@Entity
@NoArgsConstructor
@Data
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private int likes;
@CreationTimestamp
@Column(name = "created_at")
private Timestamp createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private Timestamp updatedAt;
}
이번에 사용한 엔티티 코드다.
그냥 단순하게 좋아요 수를 가지고 비교를 한다.
조건은
"좋아요를 약 500번 누른다고 요청이 들어왔을 때, 결과가 어떻게 나오는가?"
를 K6를 통해 "비관적 락"만 테스트 할 예정이다.
데이터를 변경하지 않는 Read에 대해 주어지는 Lock
read-lock, s-lock 이라고도 불린다.
@Lock(LockModeType.PESSIMISTIC_READ)
@Query("SELECT P FROM Post P WHERE P.id = :id")
Optional<Post> findByIdWithShareLock(@Param("id") Long id);
(우리의 친절한 JPA는 모든것을 알아서 지원해준다.)
저렇게 @Lock(LockModeType.PESSIMISTIC_READ) 을 붙여놓으면...
# 1은 예시입니다.
SELECT * FROM post WHERE id = 1 FOR SHARE;
이런식으로 SQL Query가 나간다.
그리고 500개를 요청하니까...

[java.sql.SQLTransactionRollbackException]
Deadlock found when trying to get lock;
try restarting transaction
데드락이 발생했다...😱😱
잘못된 자원 관리로, 둘 이상의 프로세스 또는 스레드들이 아무것도 진행하지 않는 상태로 서로 영원히 대기하는 상황
아마 한국어로는 "교착상태"라고 많이 들어봤을 상황이다.

알기 쉽게 표현하면 이런 상황이다.
프로세스A(경찰)가 자원(돈)을 점유한 상태다.
프로세스A(경찰)은 또 다른 자원(인질)이 있어야 종료되고 자원을 반납한다.
하지만..
프로세스B(범인)은 또 다른 자원(인질)을 점유한 상태다.
B(범인) 역시 또 다른 자원(돈)이 있어야 종료되고 자원을 반납한다.
서로 필요한 자원을 서로 점유하고 있으니...
서로 무한정 대기하는 좋지 않은 상황이 발생한다.

A가 가져온 행의 값을 변경 후, DB에 save()를 할려고 하는 순간..
쓰기가 가능한 X-Lock(베타락)을 얻을려고 시도한다.
S-Lock은 Read가 가능하기에,
B도 S-Lock을 통해 A와 같은 행을 Read하고 S-Lock을 걸어버렸다.
하지만, B가 S-Lock을 걸고 있는 탓에 A는 X-Lock을 얻을 수 없고 대기 상태에 빠진다.
물론 B역시 마찬가지로 A 때문에 대기 상태에 걸리며, 데드락이 발생한다.
MariaDB MySQL 같은 경우,
자동으로 데드락을 감지하고, 하나의 트랜잭션을 롤백하여 데드락을 해소한다고 한다.

그래서 500개 중에, 125개 정도만 살아버렸다...
동일한 행에 다른 트랜잭션을 생성하는 것을 허용하지 않는 Lock
Write Lock으로도 불리며, X-Lock 이라고도 한다.
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT P FROM Post P WHERE P.id = :id")
Optional<Post> findByIdWithExclusiveLock(@Param("id") Long id);
이런식으로 LockModeType.PESSIMISTIC_WRITE를 붙여놓으면 된다.
SELECT * FROM post WHERE id = ? FOR UPDATE;
그럼 이런식으로 Sql Query가 나가게 되고...

정상적으로 500개의 요청이 간다.

500개도 제대로 들어온다.
동시성을 해결할 수 있는 방법이
단일서버: synchronized
다중서버: DB Lock - 베타락
다음엔 한번 DB-Lock의 단점 & Redis를 이용한 동시성 제어를 해봐야겠다.