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

Popular posts from this blog

get url and add instance to a model with prefilled foreign key :django admin -

css - Make div keyboard-scrollable in jQuery Mobile? -

android - Keyboard hides my half of edit-text and button below it even in scroll view -