42partner-backend's Issues
1. 수정 이유
- www.42partner.com 으로 프론트 도메인 고정
- Cloudfront + s3형태에 맞추어 프론트 배포
- Authorization header로 JWT 필터링
2. 수정 내용
1. 기존 상태
- 목록을 받아오는 API의 경우 오프셋 기반으로 요청을 하게됨.(Slice라서 Count를 하지는 않음.)
- 현재 Frontend API의 스펙을 변경시킬 수는 없기때문에 Version 2 API 로서 구현할 계획.
- RDBMS의 성능 개선을 보기위해 Redis 캐싱 기능 배제하여야함.
2. 성능 개선 지점
- 테이블의 레코드가 많은 경우 OffSet기반일 때 Offset적용을 위해 스토리지 엔진으로 부터 읽어와야 하는 레코드가 많아지기 때문에 이부분을
커서 기반으로 변경할 경우 I/O감소로 성능 향상이 기대됨.
- RDS의 메모리 , I/O를 주로 보고, CPU성능 부하도 볼 계획.
3. 예상 결과
- 동일 시스템 스펙에서 전반적인 조회 TPS, 응답속도 향상이 기대됨.
- RDS의 메모리 사용량, I/O등이 감소할 것으로 예상됨, CPU사용률의 경우 감소한다는 보장은 없을듯함.
1. 기존 코드 문제점
- spring batch 기능을 추가할때 module을 분리하여 따로 빌드할 필요가 있음.
2. 리팩토링 내용
- api, core, common, batch 등으로 모듈을 불리할예정.
1. 기존 상태
- access token만을 인증에 활용하여 만료기간을 길게 두었기 때문에 보안상 취약점이 있었음.
2. 성능 개선 지점
- access token의 만료 시간을 5분으로 두고 refresh_token의 만료 시간을 2주로 두어 보안성 강화
3. 예상 결과
- 전반적인 보안성이 강화됨.
- refresh token을 어디에 보관할 지 고민해야봐야함. 현재로서는 브라우저의 cookie에 저장하는 방식 고려중.
1. 기능 상세
- EC2단독으로 되어있는 dev서버에 dev 브랜치 push시 자동적으로 빌드 및 배포 되도록 CICD파이프라인 구성
- 실제 운영 서버는 Main 브랜치에 push시 CICD시 적용되도록 따로 설정
- ASG를 사용중이기 때문에 이 부분에서 더 학습해서 추가해야함.
- appspec.yml, github action의 deploy.yml, ec2내에 code-deploy-agent 설치 및 실행
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기존 상태
- random match batch 에서 redis 트랜잭션이 적용되어 있지 않음.
- redis에 상태 변경 쿼리를 날리고 커밋하는 트랜잭션을 하나로 묶어야함.
- 마찬가지로 mysql에 날리는 commit도 하나로 묶어야함.
- mysql commit바로 이전 시점에 redis commit을 하여 둘중 하나만 commit되는 것을 최대한 방지
- 원래는 둘 모두 commit 되게 하거나 rollback되도록 해야하지만 2 Phase commit을 redis가 지원하지 않고
- event Queue등을 활용하기에는 시간이 부족하여 이렇게 임시방편으로 활용.
2. 성능 개선 지점
- 트랜잭션을 한번에 진행하기 때문에 성능이 향상됨.
- 데이터 정합성이 (redis, mysql간의) 떨어지는 경우가 발생할 수 있지만 실질적으로는 거의 발생하지 않게 됨.
3. 예상 결과
1. 기존 상태
2. 성능 개선 지점
- article 조회 시 Throughput상승
- article 조회 시 Response Time 상승.
- DB부하 줄이기 위해 분산 캐시 활용, local cache는 데이터 정합성을 맞추기 힘들고 SSEmitter in-memory 저장 및 현재 heap memory 용량상 예상치 못한 메모리 관련 에러 발생 가능성 때문에 분산캐시 활용. DB부하를 줄이기 위한 목적이 크기 때문에 분산캐시로도 충분하다고 판단.
- Write Through
- 적정한 TTL만료시간 설정(현재는 10초 예상.)
3. 예상 결과
- db 조회 부하 완화로 같은 조건 시나리오의 부하 테스트 시 DB 부하 개선. 및 RDS CPU 사용률 감소.
- Throughput상승 및 Response Time정상화(현재는 병목 지점에서 5초까지 상승.).
1. 기능 상세
- Redis Client 설정
- RandomMatch 신청
- RandomMatch 취소
- RandomMatch 상태 조회(api 조회 형식으로 할 지, 서버에서 배치서버가 주기적으로 응답주도록 할지, 아예 안할지) - 매칭 여부는 알수있어야함 DB에서 Match에서 Random + createdAt이전에 된걸로 조회해서 확인.
- Spring batch 매칭 or scheduling
- RandomMatch전체 조회
- transaction start
- RandomMatch 매칭 맺어질 사람 있는지 확인
- Random Match매칭 맺어지면 RandomMatch 정보 삭제
- Match table에 등록
- transaction end
- Async로 slack알림 주기.
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기존 코드 문제점
- Service Layer의 단위 테스트 필요
- 단위 테스트 하기에 적합하지 않은 메서드들 리팩토링
2. 리팩토링 내용
1. 기능 상세
- 프로젝트 구조 설정
- 디렉토리 구조
- swagger, BaseEntity,
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기존 상태
DBCP, Thread Pool 이 기본으로 설정되어있음.
2. 성능 개선 지점
DBCP가 적게 되어있어서 병목이 발생함.
Thread pool이 무한정 증가하는 것을 막을 수 있음.
3. 예상 결과
병목 제거 및 하드웨어 자원 활용.
1. 원래 Redis를 사용한 이유
- SortedSet 자료구조로 해주기 때문에 저장 해서 매칭 기록을 관리하는 것 만으로도 매칭 로직을 간접적으로 구현할 수 있음.
- SortedSet의 key값을 매칭 조건을 조합해 만든 문자열로 만들면 매칭 채널을 분리할 수 있음
- SortedSet 내에서 score와 Value를 기준으로 정렬되어 저장됨.(Tree구조)
- 원래는 Score값을 기준으로 정렬 하지만 Score값이 같으면 value를 기준으로 정렬이 이루어지고 value값에 매칭 신청 시간 + memberId 값의 구조로 만들면 매칭 신청 시간을 기준으로 정렬됨.
- appliaction Server에서는 SortedSet들을 조회해 SortedSet내부의 요소개수가 매칭 인원을 충족하는지만 검사하면됨.
- 매칭 신청 데이터는 schema가 변동 가능성이 크고 데이터가 오래 잘 유지 될 필요는 없는 데이터라고 판단 했음.
- In memory이기 때문에 RDB에 I/O 속도 측면에서 더 빠르고 RDB로 구현 할 경우 application Server에서 신청 기록을 분류하고 정렬하는 작업을 매 batch program이 수행해야함(Redis는 저장하는 것 만으로 이작업이 처리됨).
- RDB의 부하를 줄여줄 수 있음.
- 공부 목적.
2. 개발 과정에서 생긴 여러 문제점으로 Redis포기
- 분산된 여러 DataSource를 사용하게 되면서 각각의 노드간에 데이터 정합성 불일치의 문제가 생길 수 있고 이를 관리하는 것이 매우 힘들다는 것을 알게됨.
- 대표적으로, Batch server가 Redis를 조회하여 매칭이 가능한것을 확인하였을 때 먼저, Redis에서 신청 기록을 제거하고 매칭이 맺어진 결과를 RDB의 Match table에도 저장해야함. 이 과정이 하나로 atomic 함이 보장 되지 않으면 데이터 일관성 손상될 수 있음. 매칭이 한번 이미 맺어졌지만 Redis에서 신청기록이 제거되지않아 Batch server가 실행될 때마다 무한하게 매칭이 맺어질 수 있음.
- 이를 해결하기 위해서는 2 Phase Commit을 활용하거나 메세지 큐 같은 것을 추가로 활용하여 Redis, RDB 모두 Rollback하거나 Commit해야함.
- 문제는 Redis가 2 Phase Commit을 지원하지 않고 지원하더라도 2 Phase Commit은 트랜잭션을 자신의 데이터를 처리하지 않는 시간에도 오래 지속해야하기 때문에 성능적인 낭비가 있음.
- 메세지 Queue를 추가로 도입하기에는 학습을 해야할 뿐만 아니라 배보다 배꼽이 커지고 있다는 생각이 듬.
- Redis를 활용해 매칭 batch application에서 최초 매칭 신청 현황을 보고 매칭 결과를 exec하는 구간에서 데이터 변경을 막기가 매우 힘듬.
- Batch server에서 매칭을 맺어주는 것과 매칭 취소 신청은 Serializable 하게 이루어 져야 하고 이를 Redis로 구현할 경우 transaction을 사용하여 해결 할 수 있을 것으로 생각함.
- 문제는 Spring 에서 사용하는 RedisTemplate의 경우 Transaction사용 시 조회 할 때 항상 null을 return 함. (Redis 트랜잭션을 한 단위의 네트워크 통신하는것으로 생각하기 때문에 트랜잭션 내에서 조회를 하더라도 조회가 되지않고 트랜잭션)
- 다른 방법으로 Watch를 활용해 락을 걸고 조회는 외부에서 하는 방법이 있는데 낙관적 락임에도 매칭 신청, 취소 요청과 batch 프로그램 매칭을 맺어주는 로직 사이에 충돌이 빈번하여 문제가 될 수 있음. 이를 처리하기 위한 로직을 추가로 굉장히 많이 작성해야함.
- RDB로 데이터를 작성하여 비지니스로직을 작성했을 때와 달리 코드가 매우 복잡하고 관리하기 힘들어짐.
- 매칭 저장 SortedSet의 경우 <String, String>의 형태를 가지고 있을 뿐인데 여기에 비지니스 로직을 작성했을 때 알아보기도 힘들고 객체 지향적인 코드도 아니면서 테스트하기 힘든 코드가 작성되었음.
1. 기존 상태
- Redis cache 만료 시간 TTL이 5초로 되어있음.
2. 성능 개선 지점
- TTL 만료시 HotKey인 경우 Cache Stampede현상이 발생할 수 있음.
- 이를 대비하기 위해 Probabilistic Early Recomputation을 도입. -> 자주 참조되는 키는 확률적으로 만료 전에 갱신됨.
3. 예상 결과
- TTL만료 시 RDBMS로 부하가 쏟아지는 Cache Stampede 현상을 줄일 수 있을 것으로 예상됨.
4.먼저 해결해야하는 부분
- JMeter를 Local 에서 실행하고 있는데 Thread 생성 개수 제한이 있고 부하 테스트 제한이 있기 때문에 분산환경으로 올려야할 필요가 있음.
1. 수정 이유
- 로그인 실패시 api 페이지가 그대로 보이며 프론트엔드로 리다이렉션 되지 않는 문제가 있음.
2. 수정 내용
- 로그인 실패시 frontend 루트로 Redirection하고 쿼리 파라미터로 login_success=fail을 보냄.
- 랜덤 매칭 참여자가 조건 개수만큼으로 계산되는 버그 쿼리를 수정해야함.
1. 기능 상세
- 처음 OAUTH로그인 실행시 회원가입 진행
- Access Token은 바로 폐기 (분실 시 authorization server 까지 계정정보가 털릴 우려가 있으며, 로그인 된 상태에서 다시 자원을 요청할 일이없음.)
- Session을 통해 로그인 시간 관리.
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기능 상세
- sse를 활용하여 백엔드에서 프론트로 알림 제공.
- 내가 참여한 글이 확정 된 경우.
- 식사인지 공부인지, 글 제목, 확정자 수
- 자세한 매칭 내역 링크
- 랜덤매칭이 잡힌 경우.
- 매칭 인원들, 식사 인지 공부인지
- 자세한 매칭 내역 링크
- 내가 작성한 글에 누군가 참여한 경우.
- 내가 작성한 글에 누군가 참여를 취소한 경우.
2. 수정, 리팩토링, 버그발생 예상 지점
- 알림을 주는 시간과 대상 내용이 달라질 수 있음.
1. 기존 상태
- @Version annotation으로 인해 OptimisticLockException 발생 가능
2. 성능 개선 지점
- 낙관적 록으로 인한 충돌이 발생하면 재시도 하는 기능을 추가한다.
- AOP를 통해 핵심기능에서 벗어난 재시도 보조기능을 공통관심사로 관리할 수 있게 된다.
3. 예상 결과
1. 기능 상세
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기능 상세
- 주기(10초~20초)적으로 랜덤 매칭을 맺어줌.
- 모든 방 조회(Set으로 가져오기, 정렬된 set).
- 모든 방 스캔하면서 생성된지 30분이 넘은 데이터 collection에서 제거하고 따로 id값 기입.
- 각 방 조회 순서 랜덤으로 정함(여러 매칭 조건이 가능한 경우 평등하게 매칭이 되게 하기 위해서).
- 매칭인원(4명 예상)이 되면 생성 시간 기준 빠른 사람 부터(queue)4명씩 짝지음.
- 짝 지어진 경우 collection 에서 바로 제거하고 따로 id값 저장.
- 모든 매칭이 끝 난경우 매칭이 끝나거나, 만료된 사용자 redis에서 지우고 DB에서 isCanceled = true로 바꿈.
- 매칭 된 사용자 로 Match table db생성, random 신청 내역과 연결하는 것은 생각해 봐야할 것 같음.
- Activity점수 부여
- 알림(비동기)
- quertz로 이 행동을 주기적으로 실행시킴
- 배포 시 spring batch서버를 따로 띄움.- stand by로 ec2하나 더 있으면 좋을 것 같음.
2. 수정, 리팩토링, 버그발생 예상 지점
- isCanceled라는 이름보다 만료 되었다 정도의 의미가 더 나을 것 같음.
- 트랜잭션 구간을 어떻게 설정할지(중간에 예외 발생 시 어떻게 처리할지), 비동기 기능 어떻게 db랑 분리할지.
- redis의 트랜잭션에 대한 이해,
1. 기능 상세
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기능 상세
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기능 상세
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기능 상세
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기존 코드 문제점
- Article, Match, Activity에서 연관관계가 달라지는 경우에만 상속으로 Inheritance활용하는 것으로 결론.
- MatchCondition 활용 할때 외래키 둘 필요 없음.
2. 리팩토링 내용
- 공부, 밥의 경우 enum필드로,
- Match만 랜덤, 방에 따라서 나누어 주면 됨.
- Activity의 경우 점수 부여 이유를 어떻게 줄까 Match와 연관 관계 맺기 (만약 향후 댓글 등록 시에도 점수가 부여된다고 하면 이것도 추가 하면 될것.)
1. 기능 상세
- User Entity설계
- 가능한 정도까지 DB설계
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기존 코드 문제점
- test code 에서 불필요한 객체를 생성하거나 테스트 메서드 명이 불 명확하거나 하나의 메서드에서 여러 케이스의 테스트를 진행하는등의 문제가 있음.
- 프로덕션 코드가 테스트 하기 부적합한 경우가 있음.
- Service Layer및 통합 테스트가 적절히 수행되지 않은 경우가 있음.
2. 리팩토링 내용
- test code 메서드 명과 given영역을 복잡하지 않게 리팩토링
- 프로덕션 service 코드에서 entity 생성, 수정 시 id 값만 반환하는 경우가 있는데 전체 entity를 반환하도록 수정.
- 전반적인 테스트 코드 더 다양하게 수정.
1. 수정 이유
- session이 client에게 잘 전달 되지 않음.
- loadbalance사용하면서 clustered session 대신 jwt를 쓰기로함.
2. 수정 내용
1. 기능 상세
- 제목
- 항목별 선택(방 전체 목록에서 보여주기 위해서)
- 본문
2. 수정, 리팩토링, 버그발생 예상 지점
- 댓글 -> Opinion에서 다시 만들것.
- 댓글은 대댓글로 변경될 수 있음.
1. 수정 이유
2. 수정 내용
- Match 기록 단건 조회 api추가, Article조회 시 userId필드 추가
1. 기존 상태
2. 성능 개선 지점
3. 예상 결과
1. 수정 이유
- kafka를 통해 알림을 생성하는 것과 sse알림을 보내는 기능이 하나의 consumer이 수행함.
- 트랜잭션 내에서 kafka 이벤트를 발행하기 때문에 롤백 되어도 이벤트가 발행될 수 있음.
2. 수정 내용
- 알림을 생성하는 것과 sse알림을 보내는 것을 같은 이벤트에 대해서 다르게 처리하는 Consumer group두개로 분리 및 Kafka Consumer Config변경
- 트랜잭션 외부에서 이벤트 발행
1. 기능 상세
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기능 상세
- Logback 설정
- resource 파일 관련 설정 수정
- 테스트 서버 cicd적용
2. 수정, 리팩토링, 버그발생 예상 지점
1. 기존 코드 문제점
- scheduling 작업을 통해 랜덤매칭을 주기적(10초)으로 조회하고 매칭 조건을 맞는 대상을 맺어줌.
- polling으로 인해 불필요한 조회가 지속됨.
- 배치 서버가 따로 필요함.
- 기존에는 해당 코드를 랜덤 매칭 신청 시 수행할 경우 동시에 요청이 들어오거나 할 때 매칭이 맺어질 수 있음에도 맺어질 수 없는 경우(거의 동시에 요청이 발생한 경우)가 발생할 수 있었음.
2. 리팩토링 내용
- 해당 코드를 매칭 신청 시 이벤트큐에 produce하는 형태로 변경
- 혹시나 이벤트 consume(랜덤 매칭을 맺어주는 작업을 수행)가 실패하더라도 ack를 통해 재실행을 보장하고 매칭이 맺어질 수 있는 환경에서 무조건 맺어지도록 할 수 있음.
- event queue를 활용하여 비동기적으로 하나의 api 요청자체와 무관하게 동작시킬 수 있음.
1. 기존 상태
- 멀티 키 인덱스의 경우 컬럼 순서에 따라 인덱스 적용이 달라질 수 있다는 것을 알게됨
- 기존의 방식의 경우 쿼리문을 통해 인덱스가 잘 적용되지 않는 경우였음.
- ConditionCategory,
2. 성능 개선 지점
- 멀티 키 인덱스가 where 조건절 사용 시 인덱스 레인지 스캔을 할 수 없는 방향으로 작성되어 있었음.
- 반정규화된 테이블 이기 때문에 각각 멀티 키 인덱스를 적용해야함.
3. 예상 결과
- index full scan -> index range scan
- 옵티마이저 실행 계획을 공부해서 분석해볼 계획.
1. 기존 코드 문제점
2. 리팩토링 내용
1. 기능 상세
2. 수정, 리팩토링, 버그발생 예상 지점