Learn / Practice

Java LLM IV: which concurrent response may commit?

AI-APPS / BUILD · Engineering cases

Reproduce reversed response order with real Java threads, then enforce versioned commits, complete turns and bounded receipts.

INTERMEDIATE · 8 MIN · UPDATED 2026-10-03 · PART 4 / 17

What was published after this article?

The original conclusions and next-step proposals are retained. These published follow-ups do not imply that every limitation has been resolved.

KEEP EXPLORING

Related content

  1. Engineering cases

    Java LLM V: why can a timed-out tool keep running?

    Build an allowlisted read-only executor and reproduce the distinction between timeout, cancellation and termination.

    8 MINPublished

View count unavailable

On this page 6

Pinned code · Raw evidence and audit · Experiment record

The previous transactional-history demo committed only validated user/assistant pairs. Its synchronized ask method also held a lock while waiting for the response. Moving that wait outside the lock removes blocking, but exposes a new problem: a response can be based on a history version that is no longer current.

This experiment tests that state boundary with real Java threads and controlled local replies. It makes no model requests and measures no provider throughput.

1. A deterministic reversed completion

Both A and B receive version 0 and an empty snapshot. A latch requires B to commit before A attempts its commit. The recorded order is fast:COMMITTED, followed by slow:STALE. The final history contains only fast / new answer, and the version is 1.

The late response is not necessarily low quality. Its input snapshot has become stale. A product could expose it as a branch, or ask the caller to regenerate against the new state; silently appending it would imply conversational continuity that never existed during generation.

2. Snapshot outside, atomic commit inside

VersionedSession.Ticket ticket = session.begin(requestId, question);
// Build and send a request from this immutable snapshot, outside the lock.
String status = session.commit(ticket, validatedReply, finishReason);

The ticket contains the owning session, version, operation ID, question and an immutable history copy. The synchronized commit method compares the version, appends a complete turn, trims old pairs and increments the version in one critical section. Checking the version outside that section would allow two writers to pass simultaneously.

An atomic reference is another possible implementation, but our compound state also contains history limits and receipts. A short synchronized section makes the invariant easy to inspect. The goal is an atomic state transition, not a particular concurrency primitive.

3. Duplicate versus conflicting IDs

Situation Status Version changes?
Same ID, question and answer, receipt retained DUPLICATE No
Same ID with different content ID_CONFLICT No
New ID based on an old version STALE No
Empty reply or incomplete finish reason INVALID No
Valid reply and current version COMMITTED Yes

Receipt lookup precedes version checking, so a repeated successful commit is recognized precisely. A ticket belonging to another session is rejected even if its numeric version matches.

This is in-process commit idempotency. It does not prevent duplicate remote requests, refund model costs, or provide a database transaction. Receipts are bounded and disappear after eviction or restart. A production design needs an explicit persistence and expiry policy before making stronger execution guarantees.

4. Recorded checks and limits

Eight of the suite's 33 checks cover reversed completion, atomic pairs, duplicates, ID conflicts, incomplete replies, pair trimming, foreign tickets and snapshot isolation. The suite starts actual threads; latches control ordering without timing guesses.

Keeping two turns removes complete question/answer pairs. Earlier tickets retain their original copies. This separates reading a snapshot from updating current state. It does not exhaust all thread interleavings or establish distributed correctness.

5. Reproduce and integrate

cd demos/06-reliable-workflows
python run.py --out evidence/my-run
python audit.py evidence/20261003-reviewed

The first command compiles Java, runs the cases and saves new results, source hashes and an audit; the second checks archived evidence. Use a new output directory. Recorded runtime: Java 1.8.0_171 and Gson 2.10.1, with zero network requests.

To integrate an existing transport, obtain a ticket, construct its request, send outside the lock, validate the reply, and commit. Surface STALE to the caller instead of silently resending, which could introduce extra paid calls. Do not append transport errors as assistant replies.

Reverse the latch ordering as an exercise. Then let an invalid response arrive first: the valid response should still be able to commit. Finally, try a ticket from another session and explain why equal version numbers are insufficient.

Continue with tool execution boundaries. For atomic-reference semantics, see the Java 8 documentation; this demo uses a synchronized critical section.

Read this series

A note to your future selfBefore you move on, keep one thought of your own.

Your private reading note stays in this browser; it is never uploaded or published. Clearing browser data removes it, so export a copy to keep. This article’s translations share the same note.

Next published article:Java LLM V: why can a timed-out tool keep running? →

You are here

BUILD / AI Applications

  1. ○ Java LLM practice 3: don't let a failed request change conversation history
  2. ○ Java LLM IV: which concurrent response may commit?
  3. ○ Java LLM V: why can a timed-out tool keep running?

KEEP EXPLORING

Related content

  1. LAB / 014

    A2 / Concurrent history commits

    Reproduce reversed response order with real Java threads, then enforce versioned commits, complete turns and bounded receipts.

    Completed
  2. PROJECT / 002

    Java LLM Practice

    Reproducible Java conversation, retry and persistence boundaries, alongside attention, cache-cost and quantization-error experiments.

    Building
  3. Engineering cases

    Java LLM VI: validate retrieval evidence before judging RAG answers

    Implement BM25 and citation boundaries over ten owned synthetic documents and fourteen frozen queries, retaining misses and false hits.

    8 MINPublished
  4. Engineering cases

    Java LLM V: why can a timed-out tool keep running?

    Build an allowlisted read-only executor and reproduce the distinction between timeout, cancellation and termination.

    8 MINPublished

Which way next?

Based on article relationships and published same-topic content, not random recommendations.

← All writing