Meerdere threads met toegang tot de DB
Quickstart project: examples/concurrency
in de cursus repository (download repo zip). Het bevat een JDBC implementatie van de gekende studenten opgave, inclusief een Runnable
thread worker die INSERT
, UPDATE
of DELETE
statements issuen naar de database. Het probleem wat we hier proberen te simuleren is DIRTY READS.
Oefeningen
- Inspecteer de huidige code van het project en vergewis je ervan dat je alle stappen begrijpt. Voer het een aantal keer uit. Wat zie je in de Stdout? Waarom wordt de student wel of niet in de
SELECT
teruggegeven? - Speel met de parameter van
setAutoCommit()
in deConnectionManager
. Zie je een verschil? - Speel met de parameter van
setTransactionIsolation()
op de connection. Merk op dat SQLite enkelSERIALIZABLE
enREAD_UNCOMMITED
ondersteund, maar het onmogelijk is om dirty reads te simuleren. Zie ook https://github.com/changemyminds/Transaction-Isolation-Level-Issue en de Oracle docs voor het verschil tussen bijvoorbeeldREAD_UNCOMMITTED
enREAD_COMMITTED
? Kan je uit de context opmaken wat een “dirty read” is? - Probeer naast een dirty read ook een PHANTOM READ te simuleren met de H2 setup van bovenstaand project.
- Maak in een
for {}
loop tientallen verschillende threads aan en laat ze verschillende acties op de DB uitvoeren (lezen, schrijven, updaten, …). Loopt er iets mis? Wat kan je programmatorisch doen om de chaos tot het minimum te beperken?
Om concurrency problemen makkelijk te kunnen demonstreren gebruiken we géén SQLite vanwege SQLite’s shared cache mode. Let dus op je SQL syntax en connection string. Kleine verschillen kunnen SQL Exceptions veroorzaken bij andere database implementaties.
Connection Pooling
Uit de code blijkt dat alle threads momenteel éénzelfde connection instance delen via de StudentRepository
implementatie. In de praktijk wordt er voor client/server applicaties altijd connection pooling toegepast: een aantal connections zijn beschikbaar in een pool, waar de clients uit kunnen kiezen. Als er een beschikbaar is, kan deze verder gaan. Als dat niet zo is, moet die bepaalde request wachten, tot een andere klaar is met zijn database acties en de connection terug vrijgeeft, opnieuw in de pool. Op die manier kan je bijvoorbeeld 6 connections verdelen over 10+ client threads, zoals in deze figuur (bron: oracle docs):
Voor embedded single-file databases als SQLite is dit niet de gewoonte. Hibernate (zie 3. Database APIs) voorziet verschillende properties om connection pooling in te stellen, zoals aantal connections, aantal threads die kunnen requesten, timeout request, enzovoort. We gaan hier verder niet op in.