-
디버그 일지 - @DataJpaTest에서 FullText Index 칼럼 Insert 후 조회되지 않는 오류디버그 2024. 5. 29. 21:50
※주의 - 틀린 정보가 있을 수 있습니다.
오늘은 현재 진행 중인 ARchive 프로젝트에서 단위 테스트 작성 시 발생했던 오류에 대해서 공유해볼까 합니다.
문제 상황
- @DataJpaTest로 단위 테스트하기 위해 Full-Text Index가 설정된 `tag` 칼럼을 가지고 있는 `Member` 테이블에 데이터 삽입 해야 합니다.
- @BeforeEach에서 엔티티를 생성하고 EntityManager를 이용해 persist한 후 조회하면 Full-Text Search가 결과를 찾지 못했습니다.
- 반면, LIKE 쿼리는 정상 동작했습니다.
시도한 방법
1. 인터넷에서 찾은 다양한 방법들 (propagation 변경, entitymanager flush, clear, @Rollback(value=false), @Commit 등등) -> 실패
2. 수동 트랜잭션(전파 수준 변경) 사용 후 커밋 (TransactionTemplate, PlatformTransactionManager, REQUIRES_NEW) -> 성공
2. Flyway migration script에 insert문 작성 -> 성공
3. DataSource + Resource + ResourceDatabasePopulator로 sql script 실행 -> 성공
해결 과정
원인 파악
Full-Text Index는 언제 적용되는가?
- 일괄처리와 캐시 때문에 커밋할 때 처리됩니다
- 즉, 커밋 전에 삽입한 데이터는 Full-Text Search로 보이지 않습니다
Spring의 propagation이 REQUIRED인 @Transactional이 중첩되면 하위의 @Transactional의 commit은?
(스프링 공식 문서 참조)
- 하나의 물리 트랜잭션에 논리 트랜잭션이 참여하는 형태입니다.
- 모든 논리 트랜잭션의 전파 수준이 REQUIRED인 경우, 하위 스코프의 논리적인 트랜잭션에서 커밋을 수행하더라도 상위 스코프에서 최종적으로 커밋해야 실제 DB에 커밋됩니다.
- 즉, 상위 스코프의 트랜잭션이 롤백과 커밋을 결정합니다.
원인 파악에 따른 문제의 근본적인 원인
- @DataJpaTest는 @Transactional을 가지고 있습니다.
- @BeforeEach에 @Transactional과 함께 데이터를 삽입했습니다.
- @DataJpaTest의 트랜잭션 때문에 내부의 @BeforeEach의 트랜잭션에서 커밋은 아무런 의미가 없습니다.
- 삽입한 데이터의 커밋이 이루어지지 않으므로 Full-Text Index가 적용되지 않습니다.
- Full-Text Index가 적용되지 않으므로 Full-Text Search로 데이터를 찾을 수 없습니다.
최종적으로 선택한 해결책
@BeforeEach에 앞서서 데이터를 먼저 삽입 후 커밋하거나 @BeforeEach에서 데이터 삽입 후 커밋해야 했습니다.
기존의 테스트 코드의 틀을 최대한 바꾸지 않기 위해서 아래와 같은 방법을 택했습니다.
- @BeforeEach에서 TransactionTemplate과 PROPAGATION_REQUIRES_NEW를 이용해 데이터 삽입 후 커밋
- @AfterEach로 삽입된 데이터 TRUNCATE
TransactionTemplate + PROPAGATION_REQUIRES_NEW TRUNCATE TABLE 결론
1. Full-Text Index가 어느 시점에 인덱스를 구성하는지를 몰랐습니다
2. @DataJpaTest의 @Transactional을 신경 쓰지 않았습니다
3. @Transactional의 전파에 관한 내용을 자세히 파악하지 못했습니다
이 문제는 저번에 사이드 프로젝트를 혼자 진행할 때도 겪었는데 Full-Text Search를 할 때 @BeforeEach에서 데이터를 insert하고 진행하면 문제가 발생합니다.
(1년 7개월 전에도 제가 올렸던 글을 발견했습니다... 기록하지 않았던 것에 후회를)
예전에도 똑같이 이번에도 문제의 근본적인 원인을 몰라서 삽질을 많이 했습니다. 삽질 끝에 알아낸 문제의 원인은 정말 근본적인 문제였습니다.
Mysql에서 삽입된 데이터에 대해 Full-Text Index를 캐싱과 일괄처리하는데 이것이 트랜잭션이 커밋된 경우에 시도한다고 합니다. 즉, Full-Text Search는 커밋된 데이터만 볼 수 있다고 합니다.
InnoDB full-text indexes have special transaction handling characteristics due its caching and batch processing behavior. Specifically, updates and insertions on a full-text index are processed at transaction commit time, which means that a full-text search can only see committed data.
하지만 1년 7개월 만에 진실을 알아서 기쁘기도 하지만 너무 기능 만들기에만 급급했다는 생각이 들기도 합니다.
앞으로는 라이브러리나 프레임워크 또는 그 외의 누군가가 만든 것을 사용한다면 꼭 공식문서의 해당 챕터부터 읽어보고 시작해야겠다는 생각이 듭니다.
Why Mysql Full Text Search in Jpa EntityManager Native query can't find data?
problem When I try to use full text search in Spring with entity manager native query. It's not working (There is a data, but query always return empty list). But when I am not using full text sea...
stackoverflow.com
https://stackoverflow.com/questions/37199082/match-against-and-transactions
Match/Against and transactions
When using match/against inside a transaction, it does not seem to query from the temporary uncommited data: start transaction; insert into feed_full_text (feed_id, full_text) values (5000008, "l...
stackoverflow.com
https://dev.mysql.com/doc/refman/8.4/en/fulltext-search.html
MySQL :: MySQL 8.4 Reference Manual :: 14.9 Full-Text Search Functions
14.9 Full-Text Search Functions MATCH (col1,col2,...) AGAINST (expr [search_modifier]) search_modifier: { IN NATURAL LANGUAGE MODE | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION | IN BOOLEAN MODE | WITH QUERY EXPANSION } MySQL has support for full-text i
dev.mysql.com
Transaction Propagation :: Spring Framework
PROPAGATION_REQUIRES_NEW, in contrast to PROPAGATION_REQUIRED, always uses an independent physical transaction for each affected transaction scope, never participating in an existing transaction for an outer scope. In such an arrangement, the underlying re
docs.spring.io
'디버그' 카테고리의 다른 글
JPA N+1 문제와 해결 방법 (0) 2025.01.30 디버그 일지 - QueryDSL에서 빈 컬렉션에 대한 IN 쿼리 (0) 2024.08.08