[IMQA 성능 개선기] 대시보드가 너무 느려요!

IMQA 성능 개선기 첫 번째 시간, 대시보드편을 지금 확인해 보세요!

작성일 2022년 10월 21일

안녕하세요, IMQA 개발팀 이민혁입니다.

이번 시간에는 제가 지난 반년 동안  IMQA의 성능을 개선하면서 겪은 경험을 공유하고자 합니다.
IMQA에는 어떤 성능 문제가 있었고, 어떤 방법으로 개선하였을까요? 또 다양한 모니터링 서비스, 그리고 대용량 트래픽을 감당해야 하는 서비스들이 선택하는 성능 개선 방법은 무엇일까요?


성능 모니터링 솔루션에서 성능이 안 나온다?

IMQA라는 조직에 입사하고 여러 고객사와 미팅을 진행하였습니다. 그리고 그때마다 가슴 아픈 피드백을 받아야만 했습니다.

화면에 데이터가 빨리 나오지 않아 분석을 할 수 없어요.

첫 미팅을 진행한 고객사의 피드백이었는데요.  성능을 모니터링하는 제품에서 성능이 안 나오다니... 눈앞이 깜깜했습니다.

이 고객사의 경우 하루에 30만 정도의 DAU를 가진 서비스였는데 대시보드가 뜨기까지 최소 15초에서 300초까지 걸리는 timeout 문제가 발생했습니다. 하드웨어 스펙에 따라 차이는 있었겠지만 고객의 장애를 빠르게 확인해야 하는 대시보드가 늦게 뜨는 문제가 생긴 것입니다.

IMQA의 경우에는 수집 주기와 전송 주기라는 것이 있습니다. 이것들이 조합이 되어 10초라는 수집 주기와 60초라는 전송 주기를 설정하면, 10초마다 성능 데이터를 덤프로 저장하고, 60초마다 서버로 전송하는 방식입니다.

IMQA에서 수집하는 성능 정보는 매우 다양하기 때문에 파일이 압축되어 전송되어도 실제 서버에서 처리하는 데이터의 양은 어마어마합니다. 1분마다 6개의 덤프가 날아오기 때문에 단순히 웹서비스의 리퀘스트와는 크기가 매우 차이나죠.

만약 평균 앱 사용 시간 5분/  5만 명이 30분 동안 사용하게 된다면,
5(분) * 50000(명) * 30(분) * 6(개 덤프) = 45,000,000 (4천5백만) 개의 덤프가 30분 동안 날아오게 되는 것입니다.

단순 웹서비스에서 사용되는 리퀘스트가 아닌 덤프 파일이 전달되는 무거운 리퀘스트이기 때문에 서비스 입장에서는 엄청난 양의 대용량 트래픽이죠. 이러한 데이터가 순간적으로 많이 날아온다면 DB와 File IO에 크게 영향을 미치게 되어 성능이 나오지 않게 됩니다.

어떻게 개선해야 할까?

우선 기본적으로 캐싱에 대한 전략은 전혀 없었습니다. 모니터링 솔루션이다보니 리얼타임을 중요하게 보고 실시간 데이터를 보여주는 컨셉이었기 때문이죠.

하지만 리얼타임이 정확히 무엇일까요? 0.1초 만에 처리가 되면 리얼타임일까요?

우리가 생각하는 리얼타임은 앱의 장애를 우리가 목표로 하는 시간 안에 고객사가 받기만 하면 된다고 정의를 내렸습니다. 그렇게 하여 IMQA는 1분 안에 고객사가 장애를 받으면 된다는 목표로 캐싱이라는 컨셉을 성능 개선의 가장 큰 작업으로 잡았습니다. 그리고  아래의 3개의 Step으로 나누어 반년 동안 성능 개선에 몰두하였습니다.

성능 개선을 위한  3 STEP

1. Web API 캐싱 전략
2. 스케줄러를 통한 캐싱 데이터 생성
3. 실시간 캐싱 데이터 생성


1. Web API 캐싱 전략

적은 리소스로 빠르게 성능을 검증할 수 있는 1단계 작업이 API 캐싱 작업이었습니다. IMQA 웹 콘솔에서 화면에 데이터를 요청하는 API들을 캐싱 처리하는 기본적인 전략입니다.

캐싱 전략 (무엇을 어떻게?)

  • GET Request에 대한 Response Data를 캐싱하고 어떤 요청들을 캐싱할  것인지 결정해야 합니다.
  • 조회된 데이터를 캐싱하여 성능을 향상하는 것이 목적이기 때문에 GET 요청을 캐싱합니다.
  • POST, PUT, DELETE 요청들은 캐싱과 성격이 맞지 않기 때문에 제외합니다.
  • GET Request 중 날짜(date or timestamp)를 Parameter로 가지는 요청을 캐싱하고 IMQA 데이터들은 많은 경우에 시계열 데이터 형태로 파라미터에 날짜나 timestamp를 넘겨주게 됩니다 위의 경우들을 고려하여 Web API로 들어오는 GET 요청 중 Parameter로 날짜 데이터를 가지는 요청들을 캐싱하기로 했습니다.

캐싱 적용에 사용한 것들

  • Redis (Remote Dictionary Server) - 인 메모리 데이터 스토어
  • Key&Value, hash 등의 방법으로 데이터 보관
  • Database에서 SQL로 데이터를 조회하는 것보다 빠른 속도를 기대
  • Express-Redis-Cache (Express에서 Redis를 이용한 캐싱을 도와주는 npm 모듈)
  • Router 단계에서 요청 별 응답 데이터 캐싱
  • prefix를 지정하여 hash 형태로 캐시 보관 가능 https://www.npmjs.com/package/express-redis-cache

캐시 정책

  • 캐시 사용을 위해서 캐시의 키를 결정해 주고 삭제 시점을 결정하는 정책이 필요합니다.

1. 캐시 키 정책

  • 요청별 캐시 데이터의 구분을 위해 캐시 키를 유일하게 설정할 필요가 있습니다.
  • 이를 위해 요청 URL 을 이용해서 캐시 키를 생성하기로 결정했습니다.

요청 URL 예시 : http://localhost:3081/api/bottleneck/6?type=nativeRendering&app_version=1.7&startUsage=0&endUsage=
0&startDatetime=1626996600000&endDatetime=1626998400000&timezone=9
위의 URL은 통계 페이지의 히트맵 데이터를 가져오기 위한 요청입니다.

이를 아래와 같이 구분이 가능합니다.
{IP:PORT}{pathname}?{GET Parameter (query string)}

먼저 IP:PORT의 경우 항상 같은 값이 들어가게 될 것이기 때문에 이 부분은 제외합니다.

Web API에서는 요청의 path 별로 라우팅을 해주기 때문에 구분을 위해 pathname이 필요합니다. 또한 project_id로 프로젝트별 캐시 데이터를 구분해 줘야 하기 때문에 이 부분을 그대로 캐시 키에 포함시킵니다.

GET Parameter에는 조회할 데이터의 조건이 들어가기 때문에 이 부분도 캐시 키에 포함시킵니다.

하지만 GET Parameter 부분 전체를 키에 포함시키기에는 너무 길어지게 되고, 브라우저별 query string 정렬 방식에 따라 같은 요청임에도 캐시 키가 달라질 수 있기에 아래와 같은 방법을 사용해 키를 생성합니다.

```jsx
createCacheKey: function(req){
		// 먼저 pathname을 그대로 넣어주고 '|' 문자로 영역을 구분합니다.
    let cacheName = req._parsedUrl.pathname + '|';

		// GET Parameter의 key를 알파벳순으로 정렬합니다.
    let keys = Object.keys(req.query).sort();
    keys.forEach(key => {
				// 정렬된 key 순서대로 value 만을 캐시 키에 포함시킵니다.
        cacheName += req.query[key];
    });

    // 마지막으로 redis wildcard 문자열('*')이 포함된다면 제거해줍니다.
    cacheName = cacheName.replace(/\\*/g, '');

    return cacheName;
}
```

2. 캐시 폐기 정책

  • redis는 메모리를 사용하는 데이터 저장소입니다. 지속적으로 사용하지 않을 캐시 데이터를 계속 보관해 둔다는 것은 메모리 낭비이며 성능 저하도 일어날 수 있습니다.
  • 그렇게 때문에 일정하게 캐시를 삭제해 주는 작업이 필요합니다.

여러 가지 방법 중에서 우리는 데이터가 조회되면 데이터 발생 시간으로부터 기준일까지 캐시를 유지시키는 방법으로 결정했습니다.

예를 들어 기준일(criteria)을 10일로 잡았을 경우 2021년 8월 10일에 2021년 8월 5일의 데이터를 조회하면 해당 캐시는 2021년 8월 15일까지 유효하게 됩니다.

다음과 같은 내용으로 빠르게 캐싱이 적용되는 서비스를 만들 수 있게 되었습니다. 하지만 또 다른 문제가 있었죠. DB에 부하가 있어 캐싱할 데이터가 너무 늦게 조회되어 request가 timeout이 나는 경우 캐싱 효과도 누리지 못하고 지속적으로 느린 성능을 고객사가 겪고 있었다는 것입니다.


스케줄러를 통한 캐싱 데이터 생성

저희는 고객사가 요청했을 때 캐싱 데이터를 만드는 전략이 더 이상 유효하지 않다고 생각했습니다. 데이터는 너무 많았고, 너무 많은 DB의 쿼리 작업이 필요했으며, 이미 배포된 고객사들의 하드웨어 스펙은 원하는 것보다 한두 단계 낮았기 때문입니다.

고객의 분석 데이터를 빠르게 보기 위해서는 Summary 단계가 필요했습니다. 이때 IMQA 서비스를 기획부터 바꿔야 하는 큰 이슈가 생겼습니다. Summary를 위해서는 기준치가 고정되어야 하는데 기존 IMQA 서비스의 히스토그램 영역을 유동적으로 선택하고 있어 Summary를 만들기 어려웠습니다.

이번 성능 개선 작업을 위해 통계 기능에서 히스토그램을 50%, 95% 기준선으로 3개의 구간을 선택할 수 있게 변경하였습니다.

또한 실시간으로 데이터를 분석하던 통계 기능을 스케줄러를 통해 데이터를 생성하다 보니 최근 30분 이전 데이터까지 분석되도록 변경하였습니다. 대시보드에서는 실시간으로 데이터를 모니터링하고, 통계에서는 발생한 문제를 분석한다는 컨셉으로 기능 정의가 세분화되었습니다.

이러한 판단으로 데이터가 많은 경우 데이터 조회를 위해 많은 시간을 기다려야 했던 고객에게 쾌적한 속도(:클릭하면 뿅 하고 나옵니다!)로 제공해 드릴 수 있게 되었습니다.  통계에 대한 자세한 내용은 여기서 확인해 보세요!


실시간 캐싱 데이터 생성

통계 데이터는 스케줄러를 통해 데이터를 미리 생성하는 배치 전략을 사용했습니다. 배치 전략을 사용하는 경우 배치가 완료된 시점부터 생성된 콘텐츠를 사용할 수 있다는 특징이 있는데요. 이러한 배치 전략이 실시간 데이터를 표현해야 하는 경우에는 맞지 않았습니다.

IMQA의 대시보드는 실시간으로 앱의 문제를 확인하고, 장애 알람을 받아야 하는 기능을 배치로 만들게 된다면 실시간성이 떨어지기 때문입니다. 고객사가 빠르게 장애를 인지하도록 도와드려야 하는 IMQA의 목표를 위해 대시보드 데이터를 빠르게 보여 드리는 전략을 고민하게 되었습니다.

IMQA의 아키텍처는 다음과 같이 구성이 되어 있습니다. 이중 데이터를 처리하는 부분이 Worker라는 컴포넌트인데요.

IMQA 소개서에는 수집된 데이터를 DB에 저장하기 위한 모듈이라고 설명을 드리고 있지만, DB 처리뿐만 아니라 모바일에서 수집된 덤프 데이터를 가공하는 역할까지 합니다. 이때 가공하는 데이터를 DB에도 저장하고 캐싱 서버에도 저장하도록 만들면 되지 않을까라는 아이디어를 시작으로 바로 작업에 진행하였습니다.

이런 구성이 빠르게 가능했던 이유는 IMQA가 OLAP 아키텍처로 구성되어 있었기 때문에 가능했습니다. AWS에서 제공하고 있는 OLAP 아키텍처 소개에서 OLAP은 어떻게 작동하나요?를 보시면 아래와 같이 나와 있습니다.

출처: AWS, https://aws.amazon.com/ko/what-is/olap/

IMQA에서는 위와 같이 구성된 컴포넌트에 ETL 단계 후 저장을 DB와 캐시 서버 두 개 다 저장하도록 쉽게 만들 수 있게 된 것이죠. 추후 IMQA와 OLAP 아키텍처에 대한 포스팅을 자세히 다뤄보기로 하겠습니다.


마치며

1,2차에 걸쳐 약 4개월 동안의 성능 개선을 통해 50만 DAU 기준 8초가 걸리던 IMQA 대시보드 화면이 1초 이내로 나오기 시작했으며 통계 화면도 빠르게 확인할 수 있게 되었습니다.

IMQA는 고객사의 트래픽이 높은 대용량 트래픽을 감당할 수 있는 서비스로 거듭나고 있습니다. 다만  IMQA의 중요한 분석 정보인 Stack 정보는 캐싱하기에 너무 무거운 데이터라서 조금 느린 부분이 있습니다. 아직 해결해야 할 성능 기술 부채가 남아 있는 것이죠. 하지만 이러한 성능 문제 또한, 해결하여 성능 개선 시리즈로 만나 뵙도록 하겠습니다.

IMQA는 더욱더 쾌적하고 빠른 서비스를 제공해 드리도록 노력하겠습니다.

함께 읽어보면 좋을 콘텐츠

Share on

Tags

IMQA 뉴스레터 구독하기

국내외 다양한 기술 소식을 선별하여 매월 전달해드립니다. IMQA 뉴스레터를 통해 기술 이야기를 함께해보세요.

구독하기