java - How to handle transactions with concurrency access in Spring -
i've created service 1 method test purpos:
@service public class defaulttestservice implements testservice { private static final logger logger = logger.getlogger(defaulttestservice.class); @autowired private testrepository testrepository; @transactional(readonly = false, isolation = isolation.serializable) @override public void incrementandget(long testmodelid) { logger.debug("transaction active: " + transactionsynchronizationmanager.isactualtransactionactive()); final testmodel tm = testrepository.findone(testmodelid); if (tm != null) { logger.debug("updated " + testmodelid + " value: " + tm.getvalue()); tm.setvalue(tm.getvalue() + 1); testrepository.save(tm); } else { logger.debug("saved id: " + testmodelid); final testmodel ntm = new testmodel(); ntm.setid(testmodelid); testrepository.save(ntm); } } }
and i'm running gatling 2 concurrent accesses(2 calls @ once) method testmodelid = 1l
. result of calls i'm getting error:
org.postgresql.util.psqlexception: error: duplicate key value violates unique constraint "test_model_pkey"
what can see logs 2 concurrent calls have entered method @ once , each printed log
"saved id: 1" "saved id: 1"
i assumed adding transaction annotation on method block 1 of calls on line testrepository.findone(testmodelid)
until other call finishs, can see logs it's not working that.
so question how transaction works in case when concurrent access appears? , how can handle case concurrent access?
a transaction means modifications of persistent objects performed within boundary of transaction either :
- be commited @ end of transaction (i.e. all modification persisted in db)
- be rollbacked @ end of transaction (i.e. none of modifications persisted in db) that's all.
how transaction works in case ?
one of 2 threads reach end of transaction , commit successfully. other thread reach end of transaction , fail commit due constraint violation, second transaction terminate in "rollback" state.
why findone
isn't blocked in second transaction ?
simply because, despite serializable transaction level, there no row lock. findone
returns no results in both transactions , nothing locked (of course if first transaction commited before second transaction execute findone
: it's story).
how handle concurrent transaction in particular case (i.e. constraint violation on pk while inserting new rows) ?
the common strategy let database assign id new rows -with of sequence-
(as experiment, can try set isolation level read_uncommited second transaction may read uncommited changes first transaction. i'm not sure notice difference because if findone
in second transaction executed before testrepository.save(ntm);
first transaction still return no results)
how handle transaction rollback due concurrent modification in general ?
it depends on use case. have choice between:
- catching exception , "retry" operation.
- throwing exception caller (probably display gentle error message user).
be aware if transaction terminate in rollback state : graph of persistent objects modified during transaction not reverted it's original state.
please note using isolation level serializable can cause huge performance issues , used critical , occasional transaction.
Comments
Post a Comment