woowacourse-teams / 2022-ternoko

면담은 찐하게, 예약은 손쉽게! 올인원 면담 예약 서비스 터놓고 💖
https://ternoko.site
44 stars 6 forks source link

[BE] refactor: serializable 제거 및 동시성 테스트 추가 #519

Open dongho108 opened 2 years ago

dongho108 commented 2 years ago

Issues

논의사항

코드

@Test
@DisplayName("한 코치의 하나의 면담 가능 시간으로 동시에 여러개의 면담을 생성할 수 없다. - 동시성")
void concurrentCreate() throws InterruptedException {
    final int numberOfThreads = 2;
    final ExecutorService service = Executors.newFixedThreadPool(numberOfThreads);
    final CountDownLatch start = new CountDownLatch(numberOfThreads);
    final CountDownLatch end = new CountDownLatch(numberOfThreads);
    coachService.putAvailableDateTimesByCoachId(준.getId(), 면담가능시간생성요청정보_2022_07_01_10_TO_12);

    for (long i = 5L; i < 5 + numberOfThreads; i++) {
        final long finalI = i;
        service.execute(() -> {
            try {
                start.countDown();
                start.await();
                interviewService.create(finalI, 면담생성요청정보_준_2022_07_01_10_00);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                end.countDown();
            }
        });
    }
    end.await();
    final ScheduleResponse response = interviewService.findAllByCoach(준.getId(), 2022, 7);
    assertThat(response.getCalendar()).hasSize(1);
}

1. serializable + not unique

데드락이 발생합니다.

image

innoDB의 serializable은 기본적으로 모든 단순읽기에도 s lock을 걸기때문에

인터뷰가 생성될때

  1. tx1 이 availableDateTime의 레코드를 읽으며 s lock을 걸고 tx2 가 availableDateTIme의 레코드를 읽으며 s lock을 건다.
  2. tx1이 availableDateTime 을 수정하려고 x lock을 걸때 tx2의 s lock 때문에 기다린다. tx2이 availableDateTime 을 수정하려고 x lock을 걸때 tx1의 s lock 때문에 기다린다.

이러한 문제로 데드락이 발생합니다. -> 틀린 설명

현재 테스트코드는 H2로 돌아가는데 H2 의 serializable은 innoDB처럼 돌아가지 않습니다. (읽기에 slock을 거는 것이 아닙니다) 그럼 데드락이 왜 터질까?

2. repeatable read + not unique

동시성 문제가 발생합니다.

unique가 걸려있지 않기 때문에 2개의 Interview가 동시에 생성이 되었습니다.

image

3. repleatable read + unique

unique 제약조건으로 DB에러가 터집니다.

Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PUBLIC.UK_89AEH6C8PV4YAQSUYDVIE7QE_INDEX_C ON PUBLIC.INTERVIEW(AVAILABLE_DATE_TIME_ID NULLS FIRST) VALUES ( /* 1 */ CAST(1 AS BIGINT) )"; SQL statement:
insert into interview (id, available_date_time_id, coach_id, crew_id, interview_end_time, interview_start_time, interview_status_type) values (default, ?, ?, ?, ?, ?, ?) [23505-214]

동시에 Interview가 생기지 않기 때문에 이대로 해결하는 방식이 좋아 보입니다.

DB에러이기 때문에 500에러가 터지는데 500 에러로 두는 것과 400에러로 바꾸는 방식중에는 회의를 해봐야할 것 같습니다!

close #510

github-actions[bot] commented 2 years ago

Unit Test Results

0 tests   0 :heavy_check_mark:  0s :stopwatch: 0 suites  0 :zzz: 0 files    0 :x:

Results for commit 47c3e28e.

:recycle: This comment has been updated with latest results.