Posts
A collection of everything I've written so far.
-
[JPA + Spring Mastery 01] L1 Cache · flush · Transaction Lifecycle — what readOnly really shaves off, dirty checking's true cost
How PersistenceContext sits on Fowler's Identity Map (PoEAA, 2002), how the four ActionQueue lists decide SQL emission order, what AutoFlush actually inspects right before a query, and how dirty checking's cost differs sharply between reflection and bytecode enhancement — decomposed line-by-line from Hibernate 6's DefaultFlushEventListener through Kakao Pay's readOnly + set_option (QPS +58%) report. The +0.4 ms baseline measured between raw JDBC (0.74 ms) and JPA variants (0.99–1.95 ms) is unpacked, and `readOnly`'s three-layer effect (Hibernate flush mode + Spring tx marker + MySQL Com_set_option round-trips) is taken apart line-by-line.
-
[JPA + Spring Mastery 08] Transaction Split Patterns — Saga / Outbox / REQUIRES_NEW, from academic origins to a 9-scenario EXP-09b measurement
The maxim *don't call external APIs inside a transaction* is well known; the *how* is rarely treated honestly. This article goes from PROPAGATION's seven semantics to 2PC (XA)'s limits, to Garcia-Molina's 1987 Sagas paper, Pat Helland's CIDR 2005 Data on the Outside, and Vogels's ACM Queue 2008 Eventually Consistent — then through Toss SLASH24's SAGA, 29CM and Ridi's Outbox in production — and lands on the EXP-09b 9-scenario measurement matrix (patterns A/B/C × OFF/DB_FAIL/EXT_FAIL). Payments to Saga, notifications to Outbox, cache only to plain split — academic + production + measurement, in three layers.
-
[JPA + Spring Mastery 07] Spring AOP self-invocation — the real reason @Transactional doesn't work, decomposed down to TransactionInterceptor.invoke 6 stages
In an optimistic-lock measurement, successes=100 but balance stays at 100. The code logic was fine — the cause was a same-class call bypassing Spring AOP's proxy, so @Transactional never fired and flush never happened. This article decomposes the 6 stages of TransactionInterceptor.invoke, the line in MethodInvocation.proceed() that calls the raw target, the 6 annotations sharing the same trap (@Async / @Cacheable / @Validated / @Retryable / @PreAuthorize), and 4 workarounds (separate bean / getBean(self) / AopContext.currentProxy / AspectJ weaving), citing Spring 6 / Hibernate 6 source line-by-line.
-
MySQL Credit Deduction — 4 Locks Compared, Pessimistic at 180ms / 100% accurate, plus the self-invocation trap I hit during measurement
An ordinary scenario — 100 workers concurrently subtracting 1 from an account with balance 100. Four lock strategies (optimistic / pessimistic / MySQL GET_LOCK / Redisson) all produce different results — pessimistic 180ms / 100% / balance 0, optimistic 549ms (retry storm under contention), GET_LOCK 5015ms (advisory lock cost), Redisson 53/100 (single-instance limitation). And during measurement I hit the self-invocation trap — successes=100 but the balance never moved. The real Spring/JPA pitfall is not logic, it is AOP proxy bypass. A walkthrough including direct demos of the connection-bound GET_LOCK traps in 4 scenarios.