The second half of a fintech technical interview is database questions about the transfer you
just wrote in Java: "which isolation level fixes it", "pessimistic or optimistic", "what does
SELECT ... FOR UPDATE do". This project makes you see each answer instead of memorising it.
No Docker, no Postgres install: it runs on an in-memory H2 database in PostgreSQL
compatibility mode. Everything transfers to Postgres one to one (the SQL is the same); only the
error messages differ. Database.java and the TwoSessions test helper are given: they are the
environment. You write the transfer variants and the tests that prove what each one does.
Time box: one evening, and take a page of notes. Those notes become your Q&A answers.
- Java 21, plain JDBC
- H2 2.3 in-memory,
MODE=PostgreSQL - JUnit 5, Mockito 5, AssertJ 3, Maven wrapper
- No Spring, no JPA, no ORM
- JDK 21+
./mvnw test.databaseIsWiredUpis green; the first real test is@Disabled("story 1: remove this line to begin").
Database: a fresh, isolated in-memory schema per instance;accounts(id, balance decimal(18,2) check (balance >= 0), version int). Everyconnect()is a new session with autocommit off.TwoSessions(test scope): runs two sessions on two threads and lets a test script the interleaving step by step, which is the only way to make a race deterministic.
Both found by running the transfers from four threads, not by reasoning, and both worth saying out loud in the interview:
- A plain
UPDATEtakes a row lock too. The optimistic and one-statement variants had noFOR UPDATEanywhere and still deadlocked when two transfers ran in opposite directions, because eachUPDATEholds its row until commit. Every variant therefore writes the two rows in id order, the same rule as the Java ledger. - Optimistic locking thrashes on a hot row. Four threads on the same two accounts produced
twenty consecutive version conflicts and the retry loop gave up. Jittered backoff fixes the
test; the lesson is that optimistic locking is for rarely contended rows, and a hot account
belongs under
FOR UPDATE. That is why ledgers lean pessimistic.
git checkout solutions/sql-transfer then ./mvnw test. The tests are the
documentation: each one is named after the anomaly it shows or prevents.
Reset to the skeleton: git checkout main -- java/sql-transfer.