우리 서비스에 레디스가 고장나면 ..?
·
운영 & 인프라
운영중인 서비스의 현재 인프라 아키텍처입니다. 이음(EEUM)은 사연과 음악을 공유하는 iOS 커뮤니티 서비스입니다. 서버는 Spring Boot 위에서 MySQL과 Redis를 함께 씁니다. 기능을 하나씩 붙이다 보니, 어느새 Redis가 맡은 일이 꽤 많아져 있었습니다. 흔들기(랜덤 사연)Set + Hash로 만든 랜덤 게시글 풀차단사용자별 차단 목록 캐시, TTL(Time To Live, 만료 시간) 1일스팸 필터Pub/Sub(발행/구독)으로 게시글을 스팸 필터에 전달 MySQL은 이중화나 백업을 계속 고민해 왔지만, Redis는 "캐시니까 괜찮겠지" 하고 넘겨 왔습니다. 그런데 표를 다시 보니 Redis는 이미 캐시 이상의 역할을 하고 있었습니다. 그래서 질문을 하나 던졌습니다.운영 중에 Red..
부하 테스트로 발견한 N+1문제를 TestContainer로 CI 테스트에 고정하기
·
운영 & 인프라
부하테스트를 통해 애플리케이션 서버와 DB의 CPU 점유율이 비정상적으로 높아진 것을 보고이를 N+1문제로 판단하고 테스트 코드와 테스트 컨테이너를 통해 CI로 회귀 검증까지 진행한 내용에 대한 글입니다. 예상 트래픽 잡기우리 앱의 특성 상 아침 시간대에 동시에 많은 API콜이 온다는 것이 GA 관찰로 확인할 수 있었다. 또한 커뮤니티 기능의 고도화 예상에 따라 더 많은 트래픽이 아침 시간대에 서버로 몰릴 것으로 예상된다.따라서, 아침 시간대에 동시에 많은 트래픽이 몰릴 것으로 예상했고 평균 DAU가 10을 맴돌고 있는 상황에서 링아웃 서버에 대한 스파이크 테스트의 필요성이 있다고 판단했다.이를 위해 안정적인 부하테스트를 위해 10 → 200까지 50 단위로(10, 50, 100, 150, 200) 점진..
MySQL을 MQ로 운영중인 서비스의 푸시 알림 전송이 실패했다
·
운영 & 인프라
최종 아키텍처 이미지 1000명이 함께 사용하는 서비스에서메시지큐를 사용하지 않고 어떻게 알림을 구현하면 될까?우리 인프라에서 사용할 수 있는 인프라는 EC2 하나, MySQL 하나다.이걸로만 푸시 알림을 무리 없이 구현할 수 있는 걸까? 의심이 계속 들었지만 언제까지 고민만 할 수 없기에끝까지 이 문제를 풀어보기로 생각했다. 푸시 알림은 서비스에서 필수불가결한 기능이다. 메시지 큐를 언젠간 도입할 수 있겠지만 유저 수가 40명 대인 현재의 서비스에서는 기존에 갖고 있는 인프라를 최대한 활용하는 방식으로 구현해보고 싶었다.즉, MySQL을 MQ처럼 사용해야 한다는 것이다. 시작은 가벼웠으나..기능 구현은 역시 AI가 있으면 어려울 것이 없을 것 같아서처음에는 "유저가 게시글에 댓글을 달면 알림을 준다"..
왜 우리 서비스는 자바 21버전과 스프링 프레임워크4.0 버전을 선택했는가?
·
운영 & 인프라
이 글은 openjdk와 spring-boot wiki의 내용을 참고하여 작성한 글입니다. 새 프로젝트를 시작할 때 가장 먼저 부딪히는 건 코드가 아니라 선택이다. 자바는 몇 버전을 쓸지, 스프링 부트는 어디에 맞출지, 폴더는 어떻게 나눌지. 늘 하던 대로 최신 버전 골라놓고 넘어가도 되지만, 그렇게 정한 결정은 나중에 "이거 왜 이렇게 했더라?"라는 질문에 답을 못 한다.그래서 이번엔 개발 방향과 그 이유를 담은 설계서를 먼저 썼다. 아래는 그 문서를 정리한 내용이다. 1. 자바 버전자바 버전을 고려할 때, 실제 자바 개발자들이 작성하는 문서인 openjdk 문서를 참조하자. 이전 LTS 버전과의 변경점과 최신 버전의 java 변경 사항을 구체적으로 확인할 수 있다. LTS만 본다버전을 고를 때 전체..
클라우드와치로 로깅 시스템을 구축한 후 해결할 수 있었던 문제들
·
운영 & 인프라
일요일 저녁 6시 28분, 메일 한 통이 왔다. 예전이었다면 이런 문제는 늘 이렇게 시작됐다. 프론트 개발자가 "로그인이 안 되는데요?"라고 말한다. 나는 로컬에 서버를 다시 켠다. 재현되지 않는다. 그럼 내가 할 수 있는 말은 두 가지뿐이었다. "한 번만 다시 해보시겠어요?" 아니면 "로그아웃했다가 다시 로그인해보세요."운이 좋으면 그렇게 넘어갔다. 원인은 끝내 모른 채로.이번엔 달랐다. EC2에서 클라우드 와치로 로그를 보내고 로그 그룹을 만들어 로그를 기록하도록 했기 때문이다.메일을 받고 CloudWatch를 열었을 때, 운영 서버의 스택 트레이스가 그대로 남아 있었다. 나는 "다시 해보시겠어요?" 대신"혹시 이 시간에 뉴진스 검색하시고 401 받으셨죠?"라고 물을 수 있었다.그리고 그 대화 끝..
MySQL 공식문서(10.3.9) 분석 - B-Tree 인덱스와 Hash 인덱스의 비교
·
DB
B-Tree와 Hash 데이터 구조를 이해하면 이러한 데이터 구조를 인덱스에 사용하는 다양한 스토리지 엔진에서 다양한 쿼리의 성능을 예측하는 데 도움이 될 수 있습니다.B-Tree 인덱스 특성B-Tree 인덱스는 =, >, >=, SELECT * FROM tbl_name WHERE key_col LIKE 'Patrick%';SELECT * FROM tbl_name WHERE key_col LIKE 'Pat%_ck%';위 문장에서는 조건을 만족하는 행만 고려합니다.‘Patrick' ‘Pat’ 다음 SELECT문장들은 인덱스를 사용하지 않습니다.SELECT * FROM tbl_name WHERE key_col LIKE '%Patrick%';SELECT * FROM tbl_name WHERE key_col L..
자바의 체크 예외 vs 언체크 예외(with @Transactional)
·
언어(java, kotlin, python ...)
에러예외Checked ExceptionUnchecked Exception에러(Error)Error는 애플리케이션이 정상적으로 복구하기 어려운, 주로 JVM 레벨의 심각한 문제를 나타낸다.Exception과 형제 관계이지 부모-자식 관계가 아니라는 점이 중요하다.문법적으로는 Error도 잡을 수 있다(catch (Error e)). 하지만 catch 하지 않는게 관례이다. 이유는 발생 시점에서 이미 JVM이나 시스템이 비정상 상태이기 때문에, 잡아서 복구할 수 없거나 오히려 문제를 숨기는 결과를 만든다. 그래서 Error는 모두 unchecked다. 컴파일러가 처리를 강제하지 않는다.주요 하위 클래스로는 StackOverflowError(메모리 한계), OutOfMemoryError(힙 영역 공간 부족) ..
Mattermost의 인덱스 머지 교집합이 슬로우 쿼리의 원인이 됐던 사례에 대한 해석본과 내 결론
·
DB
똑똑한 최적화가 놀랍게도 역효과가난 사례와 그 최종 해결을 공유.처음 작성한 슬로우 쿼리SELECT id FROM posts WHERE channel_id = '9tne5g44z...'AND deleted_at = 0AND created_at 실행계획 분석 결과는 다음과 같다: 이 쿼리는 그들의 환경에서 실행하는 데 몇 분이나 걸렸는데, USE INDEX(idx_posts_channel_id_delete_at_create_at)을 사용하면 1초도 안걸려 실행됐다고한다. 위의 EXPLAIN 출력을 보면, 옵티마이저가 idx_posts_created_at 인덱스를 선택하고 있는 걸 알 수 있는데, 사실 idx_posts_channel_id_deleted_at_created_at를 선택하는게 명확히 더..
@SoftDelete 의 TIMESTAMP 사용법(Hibernate 7 버전)
·
JPA & Hibernate
Hibernate가 제공하는 SoftDelete@SoftDelete 는 하이버네이트 6.4 버전부터 도입된 애노테이션입니다.https://docs.hibernate.org/orm/6.5/javadocs/org/hibernate/annotations/SoftDelete.html SoftDelete (Hibernate Javadocs)(Optional) Conversion to apply to determine the appropriate value to store in the database. The "domain representation" can be: true Indicates that the row is considered deleted false Indicates that the row is co..
[MySQL] 특정 유저가 댓글 단 모든 게시글 조회 쿼리 개선하기
·
DB
문제 상황특정 유저가 댓글을 단 모든 게시글 중 게시글이 삭제되지 않으면서 댓글도 삭제처리 되지 않은 모든 게시글을 쿼리하는 API 가 존재한다. 현재는 posts를 먼저 조회하기 위해 straight_join을 걸고 left join을 하고 있다.또한, WHERE c.user_id = 827493558402544824 AND c.is_deleted = false를 하고 있어서 쿼리가 inner_join으로 바뀌어서 처리되고 있다. 이를 조금 더 명확하게 inner_join을 의도를 담아 직접 쓰는 방향으로 수정도 필요하다. 4 먼저, 위 쿼리를 이너조인으로 변경하고 straight_join 힌트를 제거해주었다.straight_join 키워드는 post 보다 comment 가 더 많이 달릴거라는 예측..