Tag: Backend
이 태그가 달린 글들 "Backend"
-
@Transactional(readOnly = true) 한 줄이 응답시간을 132배 줄인 이야기 — JPA Dirty Checking 의 정체 해부
Updated:JPA dirty checking 비용을 1만 row UPDATE 6 시나리오로 분해. 비교의 *기준* 을 분리해서 읽어야 한다 — S1(일반) vs S2(readOnly SELECT) ≈ 132× 는 *readOnly 가 빠진 메서드가 부담하는 비용*, S4(@DynamicUpdate) vs S6(raw JDBC) ≈ 68× 는 *@DynamicUpdate 만으로는 dirty checking dominant 비용을 못 줄인다* 는 의미, S4 vs S5(bulk JPQL) ≈ 50× 는 *모델 자체를 dirty checking 에서 bulk 로 바꿨을 때* 효과, S5 vs S6 ≈ 1.32× 는 JPA 추상화 오버헤드 30% 수준. @DynamicUpdate 는 변경 컬럼 조합마다 SQL string 이 달라져 batch_update 효과를 약화시킬 수 있고 Query Plan Cache 압박을 일으킬 수 있어 *국지적 도구*. loadedState / FlushMode MANUAL / SPR-16956 / bytecode enhancement 까지 Hibernate 6 내부 구조와 함께, 단정 표현은 톤다운한 결론으로 정리한 v2 [실측 — Java/Spring Stage 2, EXP-13].
-
Aggregate boundary 를 *권한* 으로 강제 — 한 schema 에 5 user 분리 + cross-domain write 차단 7/7 실측
Distributed Monolith 의 가장 흔한 함정 — *모든 도메인이 같은 user 로 같은 schema 에 접근* 하니 cross-domain write 가 코드 리뷰에서만 잡힘. W4 P5 에서 같은 schema 에 5 user (be_owner / be_billing / be_persona / be_rule / be_workflow) 분리 + GRANT 로 cross-domain WRITE 를 *런타임* 에 ERROR 1142 로 차단. 7/7 검증 시나리오 모두 의도대로 동작 — 잘못된 코드가 *DB 가 직접* 잡아주는 첫 단계. 이 글은 "왜 같은 schema 안에서 user 분리가 의미 있는가" 의 *진화 전략* (W3 논리적 namespace → W4 user 분리 → W6 별도 DB 인스턴스) 의 두 번째 단계 측정 기록입니다.
-
saveAll() 이 1만 INSERT 가 되는 이유 — IDENTITY + Hibernate batch 비활성화의 구조적 함정
hibernate.jdbc.batch_size=50 으로 설정해도 1만 row 의 saveAll() 이 1만 INSERT 로 발사되는 이유. GenerationType.IDENTITY 는 매 INSERT 마다 LAST_INSERT_ID() 가 즉시 필요해서 Hibernate 가 batch 를 *구조적으로 비활성화* — Statement.RETURN_GENERATED_KEYS 가 batch 와 호환 안 됨. TABLE 전략 시뮬레이션 (애플리케이션이 ID 미리 발급) 은 batch 묶임 → 200 SQL. raw JDBC batchUpdate + rewriteBatchedStatements=true 는 multi-value INSERT 로 약 10 SQL — 가장 빠름. DZone 의 IDENTITY → SEQUENCE 100배 글은 PostgreSQL 기준 — MySQL 은 SEQUENCE 미지원 (TABLE 로 에뮬레이션). MySQL 환경의 진짜 답은 UUID / TableGenerator pooled-lo / Snowflake / raw JDBC batch 중 선택입니다.
-
JPA N+1 + JOIN FETCH 깊이 함정 4종 — MultipleBagFetchException, Pagination OOM, OneToOne LAZY 트랩
4-depth 도메인 (owner→merchant→rule→history) 에서 findAll() + 자식 접근하면 21 SQL. JOIN FETCH 한 단계로 1 SQL. 단 *두 collection 동시 fetch* 면 MultipleBagFetchException — Hibernate 가 List(Bag) 두 개의 cartesian product 를 거부. *JOIN FETCH + setMaxResults* 면 HHH000104 경고 + in-memory pagination — 1만 row 다 읽고 20개만 남기는 OOM. *@OneToOne non-owning LAZY* 는 프록시가 null 인지 알 방법이 없어서 *항상 fetch* — LAZY 무시. @BatchSize 가 N+1 → N/K+1 으로 완화하는 표준 처방. JPA 의 fetch 함정은 기능 한 줄이 아니라 *Bag/List/Set + 프록시 메커니즘 + Hibernate 의 cartesian 처리 정책* 의 상호작용입니다.