Files
spring-modulith-demo/cqrs/output/01-async-profile-staleness-window.txt
asmhatre b4a623b889 Add cqrs module: CQRS in Spring Boot Without a Framework
Separate write/read DataSources, a JPA command side, and two interchangeable
projection listeners (sync and @Async) demonstrating the real latency-versus-
freshness trade-off CQRS forces. Includes a reflection-based proof that the
query side has no dependency on the write side, and a real failure/fix
transcript for the -parameters compiler flag this standalone reactor doesn't
inherit from spring-boot-starter-parent.
2026-10-03 20:59:55 +00:00

28 lines
2.3 KiB
Plaintext

$ mvn -pl cqrs test -Dtest=AsyncProfileStalenessWindowTest
(async-projection profile -- AsyncOrderSummaryProjection active; @Async hands the same
write off to a separate thread instead of running it inline. A throwaway warm-up POST
runs first and is not measured: the first HTTP request against a freshly started test
context pays Hikari/Hibernate first-use costs that have nothing to do with this
profile's actual behaviour -- measuring that cold first request directly made POST
/orders look like it took ~370ms even in this profile, which would have buried the
exact thing the test exists to show.)
2026-10-04T02:27:31.206+05:30 INFO 17346 --- [cqrs] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:cqrs-write user=SA
2026-10-04T02:27:32.304+05:30 INFO 17346 --- [cqrs] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-2 - Added connection conn10: url=jdbc:h2:mem:cqrs-read user=SA
POST /orders (async profile) took 14 ms, orderId=8451a829-7ef6-4d19-b839-2d9ce5885437
Immediately after POST returned (inside the staleness window), GET /order-summaries/8451a829-7ef6-4d19-b839-2d9ce5885437 -> status=404 NOT_FOUND, body=null
After waiting for the projection (outside the staleness window), GET /order-summaries/8451a829-7ef6-4d19-b839-2d9ce5885437 -> status=200 OK, body=OrderSummary[orderId=8451a829-7ef6-4d19-b839-2d9ce5885437, customerName=Dev, itemCount=1, totalCents=8999, status=PLACED, updatedAt=2026-10-03T20:57:33.971972Z]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
This is the trade-off in one transcript, warmed-up and measured honestly. The identical
command, in the identical module, against the identical 300ms simulated write cost, took
14ms here instead of 670ms -- because the projection's write moved to another thread and
the HTTP response stopped waiting for it. The price is the 404 on the very next line: a
client that queries in that window, a few dozen milliseconds wide in this run, gets a
"not found" for an order that was just placed successfully. Only after the test waits
(Awaitility, polling every 50ms, up to 2 seconds) does the same GET return 200 with the
order correctly summarized. Neither profile is "more correct" than the other; they are
the same correctness guarantee, paid for at a different point in the request.