Files
llm-wiki/raw/errors/testcontainers-two-context-shared-datasource-close-2026-06-11.md

3.2 KiB

title, source_type, status, related_branches, related_projects, tags, created, status_label
title source_type status related_branches related_projects tags created status_label
error / testcontainers-two-context-shared-datasource-close-2026-06-11 error-note raw
feature-domain-event-outbox-contract
ca-skeleton
error
ca-skeleton
testing
testcontainers
spring
datasource
outbox
2026-06-11 resolved

error: testcontainers-two-context-shared-datasource-close-2026-06-11

Layer: raw/errors/ — 작업 중 마주친 단일 실패·트러블슈팅 기록. 원본은 raw에 영구 보관한다.

Parent / 부모

증상 / Symptom

  • 에러 메시지 (원문 그대로):
    java.sql.SQLException: HikariDataSource HikariDataSource (HikariPool-1) has been closed.
    
  • 발생 컨텍스트: cd src && ./gradlew :app-bootstrap:test --tests '*Outbox*' — 하나의 PostgreSQL Testcontainers 컨테이너를 공유하는 두 개의 AnnotationConfigApplicationContext 중 첫 번째를 close() 하자 두 번째 context 의 쿼리가 전부 실패.
  • 재현 가능 여부: always (공유 DataSource 를 bean 으로 등록한 두 context 중 하나라도 닫으면).

재현 절차 / Reproduction

  1. Testcontainers PG 컨테이너 1개에서 HikariDataSource 1개를 만들고, 이를 두 개의 AnnotationConfigApplicationContextregisterBean(DataSource.class, () -> sharedDs) 로 등록.
  2. 두 context 로 동시 작업 후 첫 번째 context 를 close().
  3. 기대: 외부에서 생성한 DataSource 는 context 가 소유하지 않으므로 살아 있어야 함.
  4. 실제: Spring 이 bean 의 추론된 destroy method(close)를 호출해 공유 풀이 닫힘 → 두 번째 context 의 모든 쿼리 실패.

원인 / Root cause

  • Spring 의 registerBean 기본 동작은 inferred destroy method — bean 이 close()/shutdown() 을 가지면 context close 시 자동 호출한다. 외부 소유(externally-owned) 자원이라는 사실을 Spring 은 모른다.

해결 / Resolution

  • DataSource bean definition 에 beanDefinition.setDestroyMethodName("") 을 지정해 destroy 추론을 끈다 (소유권은 테스트 support 클래스가 유지, 마지막에 직접 close).
  • 같은 맥락에서 LocalContainerEntityManagerFactoryBean 대신 직접 EntityManagerFactory 를 등록하고 ContextClosedEvent listener 로 EMF 만 정리.
  • 적용 위치: ca-tmpl src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/contract/outbox/OutboxContainerTestSupport.java (actually-implemented, locally-verified — 전체 ./gradlew check 836/836 green).

동반 발견 (같은 테스트 인프라에서)

  • 고정 Clock(t0) 으로 relay 를 돌리면서 row 는 Instant.now() 로 insert → next_attempt_at <= :now 술어가 전부 false 가 되어 published=0. 해결: row 와 relay 가 같은 t0 기반, relay clock 은 t0.plusSeconds(1) 버퍼.
  • 병렬 Gradle 실행 2개가 같은 모듈 테스트를 돌리면 JUnit XML report 쓰기 경합으로 위양성 실패 — 검증 명령은 직렬화할 것.