Add batch-llm module: 100,000 synthetic tickets classified by a ChatClient in a Spring Batch 6 chunk step with Resilience4j rate limiting and retry, skip-and-record for malformed JSON, restart after kill -9 in a second JVM, and a per-chunk cost ledger. The model is a deterministic simulated ChatModel; prices are configurable placeholders. No provider called.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017ic4b4VQVEJWetTT1dYzyJ
This commit is contained in:
2026-10-29 06:10:22 +00:00
co-authored by Claude Sonnet 5.5
parent aa8b1604ed
commit 6d792f00e0
48 changed files with 2093 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
# Which layer retries what: counts taken from the provider's meter
(a) A transient DB failure on the write of chunk 2 (tickets 101-200), 300 tickets, Spring Batch write retry = 2
no failure status=COMPLETED billed calls=304 rollbacks=0
failure + Spring Batch retry status=COMPLETED billed calls=304 rollbacks=0
-> the write was retried inside the same transaction; the model was not asked again.
(b) The provider answers 429 to everything, 1 ticket. Calls the provider received for that ticket:
Resilience4j retry only (max 4 attempts) status=FAILED attempts=4
+ Spring Batch retry of the same exception (2) status=FAILED attempts=12
-> 4 x (1 + 2) = 12. Two retry layers on the same failure multiply, and each attempt also takes a limiter permit.
(c) A poison row at WRITE time (ticket 250): chunk 3 (201-300) rolls back and is scanned one item at a time.
processorNonTransactional (cached) status=COMPLETED billed calls=304 (no-failure baseline: 304) rollbacks=2 ledger rows=100 skipped=3
default (processor runs again per item) status=COMPLETED billed calls=403 (extra over baseline: 99) rollbacks=2 ledger rows=100 skipped=3
-> without processorNonTransactional(), every item of the failed chunk is classified a second time and paid for twice.