본문 바로가기

ThreadPoolTaskExecutor 설정 고민해보기 (feat. 외부 연동 서비스 API 비동기 호출) ThreadPoolTaskExecutor 설정 어떻게 하고 있었나 이 포스트를 읽는 독자들에게 질문 하나를 던져보고 싶다. “외부에 있는 다른 서비스, 예를들면 결제 시스템에 결제 API 요청을 하고 응답을 받아야 하는 상황에서 ThreadPoolTaskExecutor의 설정은 어떻게 하는 것이 적절할까?” 기본적으로 외부 API 요청은 비교적 시간이 오래 걸리는 작업으로 동기 처리를 하지 않을 것이고 동기 처리하지 않는다는 것은 다른 Thread에 작업을 위임할 것이고 그것은 곧 ThreadPool을 관리해야한다는 것이다. @EnableAsync @Configuration public class AsyncConfiguration implements AsyncConfigurer { @Override pu..
블룸필터 (BloomFilter, 대규모 시스템에서 값에 중복이 있는지 확인하는 방법) 배경 개발하면서 이미 존재하는 값인지(중복된 값이 있는지)를 확인하는 기능은 굉장히 흔히 볼 수 있다. 회원 가입 기능에서 유니크(unique)한 값으로 쓰이는 식별자 즉, ID 중복 검사 같은 것이 그 예다. 🤔 사실 뭐 복잡할 게 있나? 싶은 마음이 든다. 중/소규모 애플리케이션에서는 데이터베이스에 적당히 인덱스를 만들고 조회 쿼리만 잘하면 존재하는지 여부 정도는 금방 나오기 때문이다. (심지어 PK는 자동으로 인덱스가 생성되어있다!) 하다 못해 개발을 조금이라도 해본 사람이라면 선형적으로 하나하나 전체를 탐색하는 것이 느리기 때문에 해시 테이블, Set, Map, B-Tree, … 같은 자료구조를 사용한다는 것을 알고 있다. 하지만 대규모 시스템에서는 값이 존재하는지 확인하는 방법에 대한 고민이 필..
분산 처리 시스템에서 유일한 식별자를 만드는 방법(twitter snowflake) 배경 프로그래밍을 하다 보면 유니크한 식별자 값이 필요한 경우가 있다. 객체를 식별하든 무언가 다른 것과 구분이 필요한 경우로 데이터베이스의 Primary Key나 UUID 같은 것이 그 예다. 필자의 경우 자바, 스프링, JPA 환경에서 프로그래밍을 주로 하는데 무의식적으로 JPA 엔티티의 ID 값으로 Long 타입을 사용했고 데이터베이스에 의존한 시퀀스 또는 auto_increment 값을 사용했다. 그러나 대규모 시스템에서는 이렇게 개발하면 문제가 발생할 수 있다. 대규모 시스템이라는 표현이 어떤 정량적인 방법으로 측정할 수 있는 건 아니어서 애매하지만 여기서는 이렇게 정의하겠다. 🦣 대규모 트래픽 + 대용량의 데이터 처리로 인해 “분산 처리”가 반드시 필요한 경우로 정의한다. (이럴거면 분산 시스..
AbstractAggregateRoot의 동작 원리(with @PostUpdate로 맞이한 버그) AbstractAggregationRoot 동작 원리 AbstractAggregateRoot는 DDD(Domain Driven Design)를 구현하기 편리하게 해 주는, 정확히는 도메인 이벤트를 등록하고 가져오기 편리하게 해주는 클래스 정도로 이해하고 있다. (클래스 이름과 다르게 추상 클래스가 아니다.) AbstractAggregateRoot와 같이 무언가에서 편의 기능을 제공해줄 때는 항상 트레이드 오프(trade-off)가 있다. 즉, 편의 기능을 사용하는 만큼 개발 생산성이 올라가지만, 동작 원리를 모른 채 사용하다보면 훗날에 대가를 치르게 되어있다. 이 포스트는 그 대가를 치르고 남기는 포스트다. AbstractAggregateRoot 코드 분석 public class AbstractAggre..
JPA @OneToOne은 FetchType.LAZY가 안 먹힐 수 있다? JPA에서 @OneToOne 연관관계일 때 지연 로딩이 안 될 수 있다? 이제는 JPA가 상당히 많이 쓰이고 있기도 하고 유명한 모 강의도 있어서 많은 사람들이 잘 알고 쓰고 있긴하다. 그러나 실제 경험해본 것과 학습을 통해서 아는 것에는 차이가 있다. 실무 중에 @OneToOne 연관 관계가 설정되어 있는 엔티티에 Querydsl을 이용하여 통계 데이터를 조회해보면서 Lazy로 동작하지 않는 것을 확인하며 블로그로 남겨야겠다고 생각했다. (🔥 이 포스트의 독자의 기준은 JPA 기초 정도는 아는 사람이라고 판단하고 작성했다!) 대부분의 JPA를 학습한 사람들이 알고 있듯, ~ToOne(@OneToOne, @ManyToOne) 연관 관계일 때는 Default로 FetchType.EAGER로 동작하고 있다...
2021년 회고🚩-준비되지 않은 중니어(?)의 미래 두 번째 회고 2021년에 2020년에 대한 회고를 첫 번째로 해보고 이번이 두 번째다. 이번에는 과거에 어떤 생각을 했었는지 회고 내용이 있으니 그것을 읽어보면서 어떤 것을 이뤘는지 이루지 못했는지, 이루지 못했다면 왜 그렇게 되었는지 생각해볼 수 있을 것 같다. (항상 뭐 한 것도 없는데 시간이 흘러 다음 해가 되었네? 하는 생각이 든다.) 2021년 목표와 회고 📘 보고 싶은 책 보기 자바 ORM 표준 JPA 프로그래밍, 친절한 SQL 튜닝, 오라클 성능 고도화 원리와 해법, 새로 쓴 대용량 데이터베이스 솔루션 (총 4권) 실패. 자바 ORM 표준 JPA 프로그래밍만 읽었고 나머지 3권은 구매조차 하지 못했다. 2022년에는 주니어를 넘어 중니어(?)는 되는데 기본에 충실하기 위해서 데이터베이스 특..
MySQL 쿼리 작성 및 최적화 쿼리 작성에 필요한 기초 지식 MySQL은 영어 대소문자를 구분한다. 이유는 통상적으로 DB 내용이 디렉토리나 파일로 관리되는데 윈도우OS에선 디렉토리나 파일의 대소문자를 구분하지 않지만, 유닉스 계열에서는 대소문자를 구분하기 때문이다. 설정 파일에서 모두 소문자로만 저장되도록 할 수 있긴하다. 리터럴 표기법 문자열은 항상 홑 따옴표(')로 감싼다. 예약어와 충돌이 예상되는 경우 백 틱(`)으로 감싼다. (사실 예약어는 되도록 다른 곳에 사용하지 않는 게 정석이다.) 문자열과 상수 비교 시 숫자를 우선으로 하여 문자를 숫자로 변환한다. 숫자 값을 상수로 SQL에서 사용할 때는 DBMS가 자동으로 타입에 맞게 변경해준다. SELECT * FROM tab_test WHERE number_column='100..
MySQL의 동작 방식 MySQL의 주요 동작 방식 MySQL이 일련의 쿼리를 수행할 때 동작하는 방식에 대해서 이해해본다. 세세한 부분까지 다 파악한다기보다 일반적인 쿼리 사용자 레벨에서 주의를 기울여야 하는 부분만 알아본다. 풀 스캔 인덱스를 타지 않고 스토리지 엔진이 처음부터 끝까지 전부 다 읽어서 요청된 작업을 처리하는 방법이다. MySQL은 언제 풀 테이블 스캔 방식을 실행계획으로 선택하는가? 테이블의 레코드 건 수가 너무 적어서 인덱스를 통해 읽는 것 보다 풀 테이블 스캔이 빠를 것으로 예상되는 경우 WHERE 절이나 ON 절에 인덱스를 이용할 수 있는 적절한 조건이 없는 경우 인덱스 레인지 스캔을 사용할 수 있는 경우더라도 조건에 일치하는 레코드 건수가 너무 많을 것으로 예상되는 경우 ORDER BY 처리(Usin..