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.
28 lines
2.3 KiB
Plaintext
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.
|