전체 글 (435) 썸네일형 리스트형 TIL - DB 커넥션 점유시간과 Redis 혼재로 인한 동시성 병목 발단k6 부하 테스트 중 Zipkin 트레이스에서 이상한 스팬을 발견했다.coupon-service: connection 295.079ms ← ??omc: query 10.144msomc: result-set 4.771mscoupon-service: coupon-redis-repository#is-already-issued 13.852mscoupon-service: coupon-redis-repository#has-stock 5.658mscoupon-service: coupon-redis-repository#decrement-stock 3.992msomc: query .. TIL - 로또 게임 쇼츠 자동화 파이프라인 구축기 배경로또 번호 추첨 게임을 만든 뒤, 게임 결과를 짧은 세로형 영상으로 만들어 반복적으로 업로드할 수 있는 흐름을 구축했다.처음에는 브라우저에서 게임을 실행하고 화면 녹화 프로그램으로 저장하는 방식을 생각했지만, 매번 사람이 직접 실행하고 녹화하는 방식은 반복 운영에 적합하지 않았다. 그래서 게임 실행부터 영상 파일 생성, 후처리, 업로드까지를 스크립트로 연결했다.목표실제 게임 화면과 동일한 Phaser 캔버스를 영상으로 녹화한다.세로형 Shorts 포맷에 맞는 화면 비율로 렌더링한다.추첨 애니메이션과 결과 화면이 일정하게 재생되도록 만든다.녹화된 영상을 FFmpeg로 인코딩하고 YouTube 업로드까지 자동화한다.영상 제작 과정에서 사람이 반복해서 개입하는 작업을 줄인다.전체 파이프라인Phaser 게임.. TIL - JVM Cold Start: 왜 첫 요청은 항상 느린가 한 줄 요약JVM 애플리케이션은 시작 후 첫 요청이 느린 이유가 네트워크/DB 연결 문제가 아니라, JVM 내부의 클래스 로딩·인터프리터 실행·JIT 컴파일 지연에 있다.배경 — 무엇을 보다 알게 됐나쿠폰 서비스에 HikariCP Eager 워밍업을 적용해 DB TCP cold start(~220ms)를 제거했음에도,앱 재시작 후 첫 smoke 테스트 응답이 3,616ms로 여전히 느렸다.두 번째 실행은 592ms로 6배 빨라졌다.TCP 연결은 이미 열려 있는데 왜 느린가? → JVM 레이어의 cold start.JVM Cold Start란JVM(Java Virtual Machine)은 바이트코드(.class)를 실행하는데, 처음에는 인터프리터가 한 줄씩 읽어 실행한다.이 과정에서 세 가지 비용이 발생한.. TIL - ExecutorService와 CountDownLatch로 동시성 테스트하기 쿠폰 재고가 1개일 때 100명이 동시에 발급 요청을 보내도 쿠폰이 1개만 발급되는지 검증하고 싶었다.핵심 개념ExecutorService여러 작업을 스레드로 실행하고 관리하는 도구다. ExecutorService executor = Executors.newFixedThreadPool(100); 100개의 작업을 동시에 실행할 수 있는 스레드 풀을 만든다. executor.submit(() -> couponService.issueCoupon()); submit()을 호출하면 전달한 작업이 스레드 풀에서 실행된다.CountDownLatch내부 카운터가 0이 될 때까지 스레드를 기다리게 하는 동기화 도구다. CountDownLatch latch = new CountDownLatch(100); 카운터가 100.. TIL - HikariCP 커넥션 풀과 Cold Start 배경부하 테스트 중 Zipkin 트레이스에서 connection 스팬이 220ms 찍히는 현상을 발견. HikariCP 동작 방식과 cold start 원인을 파악함.HikariCP란Spring Boot 기본 JDBC 커넥션 풀. 애플리케이션 ↔ DB 사이의 TCP 연결을 미리 만들어두고 재사용해서 매 요청마다 연결 비용을 없애는 역할.Cold Start가 발생하는 이유HikariCP는 기본적으로 lazy 초기화를 사용함. 앱이 뜰 때 커넥션을 미리 만들지 않고, 첫 요청이 들어올 때 비로소 물리 TCP 연결을 생성함.앱 시작 → 풀 생성 (빈 상태)첫 요청 → 그때서야 PostgreSQL TCP 연결 생성 → ~220ms이후 요청 → 이미 만들어진 커넥션 재사용 → ~0mslazy가 기본값인 이유: 앱.. TIL - MSA에서 HTTP vs Kafka, Spring Cloud Gateway 면접 질문"대용량 트래픽인데 왜 Gateway에서 Kafka를 사용하지 않고 HTTP로 서비스 간 통신을 했나요?"처음에는 "대용량 = Kafka"라고 생각해서 제대로 답하지 못했다.하지만 질문의 의도는 Kafka를 써야 하는 상황과 HTTP를 써야 하는 상황을 구분할 수 있는지를 확인하는 것이었다.내가 구현했던 구조Client │HTTPS ▼Spring Cloud Gateway │HTTP ▼User Service │HTTP ▼Wallet Service 기존에는Gateway │HTTP ▼User Service │userId 조회 와 같이 Gateway가 User Service를 호출하여 userId를 조회했다.이를 개선하면서JWT Token에 userId를 포함.. TIL - Docker restart 정책 비교 (no / on-failure / always / unless-stopped) Docker restart 정책 4가지Docker의 restart 정책은 컨테이너가 종료되었을 때 Docker가 어떻게 동작할지 결정하는 옵션입니다.정책설명no자동 재시작하지 않음 (기본값)on-failure비정상 종료(exit code ≠ 0)일 때만 재시작always종료 원인과 관계없이 항상 재시작unless-stopped항상 재시작하지만 사용자가 수동으로 중지한 경우는 제외참고on-failure는 Docker 데몬이 재시작될 당시 비정상 종료 상태였던 컨테이너도 다시 시작합니다.on-failure vs unless-stopped 차이처음에는 always, on-failure, unless-stopped가 비슷해 보였지만 핵심 차이는 사용자가 docker stop으로 직접 중지한 경우입니다. dock.. TIL - Boolean.TRUE.equals()를 쓰는 이유 배운 것Boolean.TRUE.equals(result) 형태로 쓰는 이유는 result가 null일 수 있기 때문이다.상황Redis에서 Set 멤버 여부를 확인하는 코드에서 발견했다.public boolean isAlreadyIssued(String couponId, String userId) { Boolean result = redisTemplate.opsForSet().isMember(ISSUED_KEY_PREFIX + couponId, userId); return Boolean.TRUE.equals(result);}redisTemplate.opsForSet().isMember()의 반환 타입은 Boolean (래퍼 타입)이다.Java의 래퍼 타입 Boolean은 true, false, .. 이전 1 2 3 4 ··· 55 다음