HikariCP auto-commit:false, 데이터가 조용히 사라지는 날
HikariCP auto-commit:false, 데이터가 조용히 사라지는 날
2026-02-26
발단
FCM(Firebase Cloud Messaging, 구글에서 제공하는 모바일 푸시 알림 서비스) 발송 실패한 토큰을 DB에서 비활성 처리하는 기능을 배포했다. 로직은 단순했다.
- FCM 에러 응답이 오면
- 해당 사용자의 push token을 비활성으로 UPDATE
코드도 깔끔하고 테스트도 통과했다. 그런데 배포 후 DB를 확인하니 아무것도 바뀌지 않았다. 에러 로그도 없다.
auto-commit이 뭔데?
JDBC 커넥션에는 auto-commit이라는 설정이 있다.
→ auto-commit은 DB에 쿼리를 실행한 뒤 자동으로 결과를 확정(커밋)할지 말지를 정하는 설정이다.
auto-commit: true(기본값) → 쿼리 하나 실행하면 바로 커밋auto-commit: false→ 명시적으로commit()을 호출해야 반영
auto-commit: true
UPDATE → 바로 커밋 ✅
auto-commit: false
UPDATE → commit() 호출 필요
→ 안 하면? 커넥션 반환 시 롤백 💀
원인
HikariCP(자바 애플리케이션에서 가장 널리 쓰이는 DB 커넥션 풀 라이브러리로, Spring Boot의 기본 커넥션 풀이다) 설정을 보니 auto-commit: false로 되어 있었다.
hikari:
auto-commit: false
문제의 코드:
// @Transactional 없음!
public void markTokenAsInvalid(String userId) {
jdbcTemplate.update("UPDATE ... SET PUSH_TOKEN = 'INVALID' WHERE USER_ID = ?", userId);
}
JdbcTemplate은 커넥션 풀(미리 만들어 둔 DB 연결들을 모아두고 필요할 때 빌려 쓰는 저장소)에서 커넥션을 빌려서 SQL을 실행하고 반환한다. auto-commit: true면 실행 즉시 커밋이 되지만, false면?
1. 커넥션 획득 (auto-commit: false)
2. SQL 실행 → UPDATE 수행 (아직 미확정)
3. commit()? → 아무도 안 부름
4. 커넥션 반환 → HikariCP가 rollback
쿼리는 실행됐지만 커밋 없이 사라진 것이다. 에러 없이.
해결
@Transactional
public void markTokenAsInvalid(String userId) {
jdbcTemplate.update("UPDATE ... SET PUSH_TOKEN = 'INVALID' WHERE USER_ID = ?", userId);
}
@Transactional이 있으면 Spring이 시작-커밋-롤백을 관리해준다.
같은 날 발견한 또 다른 케이스 (2026-02-06)
비동기 스레드에서도 같은 문제가 있었다.
CompletableFuture.runAsync(() -> {
repository.updateProgress(requestId, sent, failed);
// → 부모의 @Transactional이 전파되지 않음!
});
Spring의 @Transactional은 ThreadLocal 기반이다. 새 스레드에는 전파되지 않는다.
→ ThreadLocal은 각 스레드가 자기만의 데이터를 따로 저장하는 공간이다. 새로운 스레드가 생기면 부모 스레드의 ThreadLocal 값은 복사되지 않는다.
메인 스레드 (@Transactional 있음)
└── runAsync → 새 스레드 (트랜잭션 없음!)
└── UPDATE → auto-commit:false → 커밋 안 됨
해결은 호출되는 쪽(Repository) 메서드에 @Transactional을 직접 붙이는 것.
배운 것
auto-commit: false면 모든 쓰기에@Transactional필수 — JdbcTemplate이든 JPA든.- 이 버그는 에러 없이 조용히 데이터를 잃는다. 로그에 아무것도 안 남는다.
- 비동기 스레드는 부모의 트랜잭션을 물려받지 않는다.
- 로컬(
auto-commit: true)에서 개발하고 운영(auto-commit: false)에 배포하면 이런 사고가 난다. 환경 설정을 맞춰놓자.