데이터 삭제와 조회 성능의 관계
·
Computer Science/데이터베이스
증가하는 데이터서비스를 운영하다 보면 데이터가 계속 쌓이게 된다. 처음에는 데이터가 많지 않기 때문에 조회 쿼리를 실행해도 빠르게 결과를 가져올 수 있다. 하지만 서비스가 성장하면서 데이터가 수천만 건, 수억 건 단위로 늘어나면 조회 성능이 이전보다 느려지는 상황이 발생할 수 있다.물론 데이터가 많다는 이유만으로 모든 쿼리가 느려지는 것은 아니다. 조회 조건이나 실행 계획, 인덱스 구성, 버퍼 풀의 캐시 적중률 등 다양한 요소가 성능에 영향을 미친다. 다만 조회해야 하는 데이터의 범위가 넓거나 많은 데이터를 읽어야 하는 쿼리라면 데이터가 증가하면서 처리해야 하는 페이지의 수도 늘어날 수 있다.그렇다면 데이터가 계속 쌓이는 것을 방지하기 위해 오래된 데이터를 삭제하면 조회 성능도 개선할 수 있을까? 단순히..
Spring Kafka에서 발견한 pause 버그 (feat. 오픈소스 컨트리뷰트)
·
System Architecture/Kafka
들어가며Spring Kafka에 올린 첫 PR이 main 브랜치에 반영되었다.처음부터 오픈소스에 기여해 보겠다는 생각으로 시작한 일은 아니었다. 코드 배포 후 모니터링하다가, pause되었던 파티션들이 재개 조건을 만족했는데도 다시 resume되지 않는 이상 현상을 발견했다. 처음에는 단순히 내가 작성한 코드에서 문제가 있을 것이라 생각했다. 그런데 로그를 추가하고 코드를 하나씩 따라가다 보니 예상과 다른 값이 Spring Kafka 내부에서 반환되고 있음을 알게되었다. 결국 원인을 파악하기 위해 Spring Kafka 라이브러리의 내부 구현까지 읽게 되었고, 그 과정에서 원인으로 의심되는 부분을 찾을 수 있었다. 그저 멈춰 있는 Consumer를 다시 동작시키고 싶어서 시작한 디버깅이, 그렇게 첫 오픈..
Jackson으로 다형성 JSON 역직렬화하기
·
Java & Kotlin
다형성 JSON에서 문제가 생기는 지점Jackson의 ObjectMapper를 이용하면 JSON을 Java 객체로 쉽게 변환할 수 있다. 아래와 같은 코드가 있다고 생각해보자.// 역직렬화 코드ObjectMapper objectMapper = new ObjectMapper();Payment payment = objectMapper.readValue(json, Payment.class);/*// Payment.class 코드public class Payment { private String orderId; private Card card;}// JSON 예시{ "orderId": "ORDER-1", "card": { "cardNumber": "1234-5678", "installm..
놓치기 쉬운 Consumer Offset 설정
·
System Architecture/Kafka
들어가며최근에, 운영 중인 Kafka Topic을 신규 Cluster로 이관해야 하는 상황이 생겼다. 단순히 Producer와 Consumer가 바라보는 bootstrap server만 변경하면 될 것 같지만, 실제 운영 중인 Topic을 이관할 때는 특히 고려해야 할 부분이 하나 더 있다. 바로 Consumer Offset이다.Kafka Cluster 이관작업 단계기존 Kafka Cluster를 A, 새롭게 이관할 Kafka Cluster를 B라고 해보자. 이관 과정에서 Producer와 Consumer를 동시에 변경할 수도 있겠지만, 배포 시점에 따라 기존 Cluster에 아직 소비되지 않은 메시지가 남아 있을 수 있다.때문에 작업 시, 메세지 유실을 최대한 방지하고자 Producer를 먼저 신규 C..
Reserved Method와 Derived Query의 차이
·
JPA&JDBC
같은 조회인데 왜 동작이 다를까?JPA 조회에서 발견한 차이JPA를 사용하면서 Query Log를 보다가 내 예상과 다른 부분을 발견했다. 아래처럼 같은 findByEmail()을 연속해서 두 번 호출하는 코드가 있다고 가정해보자.@Transactionalpublic void test(String email) { Member member1 = memberRepository.findByEmail(email) .orElseThrow(); Member member2 = memberRepository.findByEmail(email) .orElseThrow();}두 메서드는 같은 Transaction 안에서 실행되고 있고 조회 조건도 완..
Feature Flag 패턴이란?
·
System Architecture
Feature Flag는 왜 필요할까?서비스를 운영하다 보면 코드를 배포하는 일과 기능을 사용자에게 공개하는 일을 항상 같은 시점에 하고 싶지는 않다. 기능 개발은 끝났지만 아직 전체 사용자에게 열기에는 부담스러운 경우가 있다. 내부 계정이나 특정 조직에만 먼저 열어보고 싶은 기능도 있다. 반대로 이미 배포된 기능에서 문제가 생겼을 때, 코드를 다시 배포하지 않고 기능만 빠르게 끄고 싶은 경우도 있다.이런 문제를 해결하기 위해 사용하는 방법 중 하나가 Feature Flag다. Feature Flag는 배포된 코드의 동작을 런타임에 바꿀 수 있게 해주는 방법이다. Feature Flag의 핵심은 “코드를 다시 배포하지 않고 동작을 바꿀 수 있다”는 점이다. Feature Flag를 사용하면 기능 공개 시..
mTLS에 대해 알아보자
·
Computer Science/네트워크
mTLS란?TLS 기반인 HTTPS를 떠올리면, 클라이언트가 서버를 신뢰할 수 있는지 검증하는 구조를 떠올린다. 브라우저가 특정 사이트에 접속할 때 서버는 인증서를 제시하고, 브라우저는 해당 인증서가 신뢰할 수 있는 인증기관, 즉 CA에 의해 발급되었는지 확인한다. 이 과정을 통해 사용자는 내가 접속한 서버가 진짜 브라우저가 맞는가를 검증할 수 있다.하지만 서버 입장에서는 반대 방향의 검증이 필요할 때가 있다. 즉, "이 요청을 보낸 클라이언트가 정말 허용된 시스템인가"를 네트워크 연결 단계에서 확인하고 싶은 경우이다. 이때 사용하는 방식이 mTLS이다. mTLS(상호 TLS)는Mutual TLS의 약자로, 말 그대로 상호 TLS 인증을 의미한다. 일반 TSL가 주로 클라이언트가 서버를 검증하는 구조라면..
Hibernate Envers에 대해 알아보자
·
JPA&JDBC
들어가며최근에 코드를 실행하다가 신기한 상황을 발견했다. 서비스 로직에서 A 테이블의 데이터를 update하고 있었는데, 트랜잭션이 끝난 뒤 확인해보니 A의 히스토리 이력을 저장하는 B 테이블에도 데이터가 자동으로 쌓이고 있었다. 때문에 나는 당연히 아래와 같은 코드를 찾기 시작했다.historyRepository.save(...)그런데 아무리 찾아봐도 B 테이블에 위와 같이 명시적으로 직접 저장하는 로직은 없었다. 도대체 어떻게 이 데이터를 저장하는 걸까?결론부터 말하면, 애플리케이션 서비스 코드가 히스토리를 직접 저장한 것이 아니었다. 해당 엔티티에 @Audited가 붙어 있었고, Hibernate Envers가 Hibernate의 flush 과정에 끼어들어 변경 이력을 자동으로 저장하고 있었다.Hi..
[Spring Batch] Spring Batch 아키텍쳐와 전체 흐름 이해하기
·
Spring
Spring Batch는 무엇을 해결하려는 기술일까?애플리케이션을 운영하다보면 사용자의 요청에 즉시 응답하는 API 처리와는 조금 다른 종류의 작업이 필요해진다. 매일 새벽 주문 데이터를 정산하거나, 대량의 회원 데이터를 읽어서 상태를 갱신하거나, 외부 시스템에서 받은 파일을 검증한 뒤 데이터베이스에 적재하는 작업들을 예시로 들 수 있다. 이러한 작업들은 보통 대량의 데이터를 처리해야 하고, 일정한 주기로 반복 실행되며, 실패했을 때 어디까지 처리했는지 추적할 수 있어야 한다. Spring Batch는 이런 배치 작업을 안정적으로 만들기 위한 프레임워크다. 공식 문서에서도 Spring Batch를 대량 데이터 처리를 위한 경량이면서 포괄적인 배치 프레임워크로 설명하고 있다. 다만, Spring Batc..
JpaPagingItemReader의 transacted 옵션
·
JPA&JDBC
JpaPagingItemReaderSpring Batch에서 대용량 데이터를 처리할 때 가장 먼저 부딪히는 문제는 “데이터를 어떻게 안정적으로, 그리고 효율적으로 읽어올 것인가”이다. 이때 등장하는 대표적인 Reader가 바로 JpaPagingItemReader다. 이름 그대로 JPA를 기반으로 데이터를 페이징 방식으로 나눠서 읽어오는 Reader다.조금 더 쉽게 말하면, 데이터베이스에 있는 데이터를 한 번에 전부 가져오는 것이 아니라, 일정 개수씩 끊어서 가져오는 역할을 하는 컴포넌트다. 예를 들어 100만 건의 데이터를 처리해야 한다고 했을 때, 한 번에 전부 메모리에 올리는 것이 아니라 1000건씩 나눠서 가져오는 방식이다. 이렇게 하면 메모리 사용량을 안정적으로 유지하면서도 대용량 처리가 가능해진..