S-HOOK 동시성 문제 해결

@VERO
Created Date · 2023년 09월 07일 06:09
Last Updated Date · 2023년 10월 21일 04:10

개요

우리 팀의 현재 killing_part_like 는 문제가 많다.
정합성 문제와 함께 중복 데이터가 사용자에게 전달될 가능성도 존재한다.

현재는 어떻게 해결할 수 있을지 고민하는 시간을 갖고 있다.

너무 느린 API 응답

Locust 로 API 응답 측정

DB 가 느린걸까, 애플리케이션 처리 속도가 느린 걸까

어떻게 측정할 수 있을까

그래서 어떤 게 느릴까

현재 우리의 좋아요 정책

  • 메인에서 좋아요 순으로 정렬된 데이터를 가져온다. 현재는 서버에서 특정 기간(1시간, 30분 등)으로 캐싱하지 않고 매번 실시간으로 정렬해서 조회한다.
  • 유저가 각 노래의 킬링파트에 좋아요를 누르면 메인의 좋아요 순위가 변경될 수 있다.
  • 각 노래의 킬링파트에 유저들의 좋아요한 개수, 내가 좋아요를 눌렀는지 여부가 표시된다.
  • 어떤 노래를 들어가서 스와이프를 했을 때, 앞 뒤로 좋아요 순으로 정렬된 데이터를 각각 10개씩 받아온다.

발생할 수 있는 문제점

  1. 여러 유저가 어떤 노래의 킬링파트에 대해 동시에 좋아요를 눌렀을 때, 정합성 문제가 발생할 수 있다. => 매우 시급

  2. 정합성 이슈를 고려하지 않는다고 했을 때 -> 유저가 좋아요를 눌렀을 때, DB의 킬링파트 좋아요가 추가되기 때문에 기존에 사용자가 메인에서 보았던 좋아요 순 노래 리스트가 변경될 수 있다. 즉, 기존에 들었던 노래가 스와이프 했을 때 다시 등장할 수도 있다. => 꽤나 시급

  3. 좋아요 수를 실시간으로 반영하기 때문에 응답을 캐싱하는 것이 불가능하다.

해결 방법

기술적 관점

  • 좋아요 정합성 이슈의 해결 방법은 shook-like-issue 에서 확인 가능합니다.

DB Replication 을 적용한다

하나는 읽기 DB, 하나는 쓰기 DB 를 둔다.

  • 장점
    • 좋아요 읽기 작업 성능을 높일 수 있다.
  • 단점
    • 일관성 있는 데이터를 얻기 어려울 수 있다.

정책적 관점

좋아요 정합성 문제가 해결되었다고 가정하고 정책을 제안한다.

메인 리스트를 전날 좋아요 순으로 정렬한다

매 요청마다 정렬하지 않고, 전날까지 집계된 좋아요 순으로 정렬한다.
DB 는 그대로 실시간 업데이트 하되, 전날까지 집계된 좋아요 순 정렬을 캐싱해서 서버 인메모리에 저장한다.

  • 장점
    • 데이터 자체는 실시간으로 처리된다. DB와의 정합성 처리는 신경쓰지 않아도 된다.
  • 단점
    • 이전에 들었던 노래가 나오는 문제는 해결할 수 없다.
    • 좋아요 순 정렬된 리스트의 크기는 고정되어야 한다.

해당 사용자의 좋아요 순 리스트를 고정한다

사용자가 좋아요 순 리스트를 클릭해서 노래 듣기를 선택하는 순간, 해당 시점의 좋아요 리스트를 사용자 id 로 인메모리에 캐싱한다.
그 뒤로 요청을 받을 때마다 인메모리에 캐싱된 값을 가져와서 응답한다.

메인 화면에서의 킬링파트 좋아요 많은 순 차트는 옆에 (마지막 새로고침 N분 전) 이라고 표시되도록 한다.
만약 새로고침을 누르거나, 다시 메인에서 요청을 보내는 경우 캐싱된 값을 업데이트 한다.

  • 장점
    • 응답 결과를 캐싱하기 때문에 성능이 매우 빠르게 향상될 것으로 예상한다.
    • 이전에 들었던 노래가 나오는 문제가 해결된다.
  • 단점
    • 사용자가 좋아요를 누른 경우, 좋아요를 누른 정보도 캐싱되어야 하는데 이 처리가 좀 빡셀 거 같다.
    • 좋아요 순 리스트 개수가 고정되어야 한다.
    • 서버가 stateful 하다.
    • 사용자가 많아질수록 캐싱해야 하는 양이 늘어난다.

좋아요 처리 버퍼링

차트의 순위를 결정하는 데이터는 실시간으로 수집하지만, 순위 재정렬은 일정 시간 간격으로 이루어진다.

  1. 좋아요 데이터는 실시간으로 수집한다. 메모리 같은 일시적인 저장 공간에 저장된다.
    1. 킬링파트의 좋아요 수는 업데이트 하지 않는다.
    2. 유저의 좋아요는 killing_part_like 테이블에 저장한다.
  2. 일정 시간마다 버퍼에 저장된 데이터를 한 번에 업데이트한다.
    1. 킬링파트의 좋아요 수를 한 번에 DB로 업데이트한다.
  3. DB 가 업데이트 되었기 때문에

킬링파트에 좋아요 수를 표시하되, TOP 3 옆에 마지막 업데이트 시간 표시하기

해결 방법 고려하기

shook-like-issue