MS Lesson15: Locking mechanism & Transaction propogation

1. Optimistic Read = Optimistic

Optimistic Read zamani row-a lock qoyulmur version ile idare olunur. Optimistic o demekdir ki, problem olmayacaq ve ya cox az olacaq. 

package guru.springframework.cruddemo.service.impl;

import guru.springframework.cruddemo.entity.Account;
import guru.springframework.cruddemo.repository.AccountRepository;
import guru.springframework.cruddemo.service.AccountService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Service
@Slf4j
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {

private final AccountRepository accountRepository;

@Override
@Transactional
public void transfer(Integer amount) {
System.out.println(Thread.currentThread().getName());
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());

Account fromAccount = accountRepository.findWithOptimisticLockById(1L).orElseThrow();

try {
Thread.sleep(6000);
} catch (Exception e) {
e.printStackTrace();
}

Account toAccount = accountRepository.findWithOptimisticLockById(2L).orElseThrow();
fromAccount.setAmount(fromAccount.getAmount() - amount);
toAccount.setAmount(toAccount.getAmount() + amount);
}

}


@Lock(LockModeType.OPTIMISTIC)
@Query("select a from Account a where a.id = :id")
Optional<Account> findWithOptimisticLockById(@Param("id") Long id);



2. Optimistic Write = Optimistic force increment

Burada ise versiya mecburi artir

@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
@Query("select a from Account a where a.id = :id")
Optional<Account> findWithOptimisticForceIncrementById(@Param("id") Long id);



3. Pessimistic Read = lock in share mode (S lock)

@Lock(LockModeType.PESSIMISTIC_READ)
@Query("select a from Account a where a.id = :id")
Optional<Account> findWithPessimisticReadLockById(@Param("id") Long id);

Pessimisti Read - de deadlock bash vere biler.


4. Pessimistic Write = select for update (X lock)

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select a from Account a where a.id = :id")
Optional<Account> findWithPessimisticWriteLockById(@Param("id") Long id);



1. A servisi transactional metodu var ve B servisindeki non transactional metodu cagiri:

package guru.springframework.cruddemo.controller;

import guru.springframework.cruddemo.service.impl.AccountServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
@RequiredArgsConstructor
public class AccountController {

private final AccountServiceImpl accountServiceImpl;

@PostMapping("/transfer")
public void transfer() {
accountServiceImpl.transfer(30);
}

}


package guru.springframework.cruddemo.service.impl;

import guru.springframework.cruddemo.entity.Account;
import guru.springframework.cruddemo.repository.AccountRepository;
import guru.springframework.cruddemo.service.AccountService;
import guru.springframework.cruddemo.service.TxProxyDemoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {

private final AccountRepository accountRepository;
private final TxProxyDemoService txProxyDemoService;

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transfer(Integer amount) {
System.out.println(Thread.currentThread().getName());
Account fromAccount = accountRepository.findById(1L).get();
Account toAccount = accountRepository.findById(2L).get();
fromAccount.setAmount(fromAccount.getAmount() - amount);
toAccount.setAmount(toAccount.getAmount() + amount);
txProxyDemoService.test();
}

private void foo() {
throw new RuntimeException("---------> xeta");
}

}


package guru.springframework.cruddemo.service.impl;

import guru.springframework.cruddemo.entity.TxProxyDemo;
import guru.springframework.cruddemo.repository.TxProxyDemoRepository;
import guru.springframework.cruddemo.service.TxProxyDemoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class TxProxyDemoServiceImpl implements TxProxyDemoService {

private final TxProxyDemoRepository repository;

@Override
// @Transactional
public void test() {
System.out.println(Thread.currentThread().getName());
TxProxyDemo txProxyDemo = repository.findById(1L).get();
txProxyDemo.setContent("demo");
foo();
}

private void foo() {
throw new RuntimeException("------> Something went wrong");
}

}


Bu zaman transaction B servisindeki metoda da oturulur,  orada davam edir ve tranzaksiya A servisindeki metodda bitir.


2. Required - bu @Transactional yazanda default secimdir.

A servisindeki @Transactional metoddan B servisindeki @Transactional metodu cagirirsa B servisindeki tranzaksiya yeni tranzaksiya acmir A ni davam eletdirir, yeni A nin tranzaksiyasi B ye oturulur. 

Bu zaman her hansisa bir metodda Runtime exception bash verersen bu tranzaksiya vahid bir tranzaksiya kimi davranacaq ve butun emeliyyat (hem A hem B) rollback olacaq.



3. Requires_new - bu zaman yeni tranzaksiya serhedi teyin olunur. Bunu gormek ucun:

System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());

Qisa qayda: Requires_new neye gore var? Birincide xeta bash verse ikinciye tesir etmesin, bu qeder sade.


4. Supports - tranzaksiya varsa qatil, yoxdursa tranzaksiyasiz ishle.

Qisa qayda: yazsanda yazmasanda ferq etmir.


5. Not_supported - tranzaksiya varsa suspend et, tranzaksiya olmadan ishle. 

package guru.springframework.cruddemo.service.impl;

import guru.springframework.cruddemo.entity.Account;
import guru.springframework.cruddemo.repository.AccountRepository;
import guru.springframework.cruddemo.service.AccountService;
import guru.springframework.cruddemo.service.TxProxyDemoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Service
@Slf4j
@RequiredArgsConstructor
public class AccountServiceImpl implements AccountService {

private final AccountRepository accountRepository;
private final TxProxyDemoService txProxyDemoService;

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transfer(Integer amount) {
System.out.println(Thread.currentThread().getName());
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
Account fromAccount = accountRepository.findById(1L).get();
Account toAccount = accountRepository.findById(2L).get();
fromAccount.setAmount(fromAccount.getAmount() - amount);
toAccount.setAmount(toAccount.getAmount() + amount);
txProxyDemoService.test();
// foo();
}

private void foo() {
throw new RuntimeException("---------> xeta");
}

}


package guru.springframework.cruddemo.service.impl;

import guru.springframework.cruddemo.entity.TxProxyDemo;
import guru.springframework.cruddemo.repository.TxProxyDemoRepository;
import guru.springframework.cruddemo.service.TxProxyDemoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Slf4j
@Service
@RequiredArgsConstructor
public class TxProxyDemoServiceImpl implements TxProxyDemoService {

private final TxProxyDemoRepository repository;

@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void test() {
System.out.println(Thread.currentThread().getName());
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
TxProxyDemo txProxyDemo = repository.findById(1L).get();
txProxyDemo.setContent("demo");
// repository.save(txProxyDemo);
// foo();
}

private void foo() {
throw new RuntimeException("------> Something went wrong");
}

}


test() metodunda RuntimeException bash verse bu exceptionu cagirildigi metoda yoneldecek.

Ne zaman lazim ola biler: cox vaxt uzun ve ya yavash ishleyen ishler (mes. email, HTTP fayl, agir hesabat) ve ya bu addim aktiv DB transaction-da olmasin dediyimiz hallarda - esas transaction-u suspend edib hemin hisseni tx-siz ishletmek ucun.


6. Mandatory - mutleq aktiv tranzaksiya olmalidir, eks halda xeta atilacaq. open-in-view = true buna hec bir tesiri yoxdur, yalniz session aciq saxlayir, response controllere qayitdiqdan sonra session baglanir. 


7. Never - hec vaxt tranzaksiya olmamalidir, varsa xeta atsin. Tranzaksiya yoxdursa normal tranzaksiyasiz (adi metod kimi) ishleyir. 


8. Nested - daxili nested savepoint (JDBC savepoint; DataSource desteklemelidir). Spring-in yeni versiyasinda desteklemir, evezine : Required_new + try/catch bloku. Yeni B-de xeta olsa bele, A davam etsin.





























Комментарии

Популярные сообщения из этого блога

Interview questions

Lesson1: JDK, JVM, JRE

Lesson_2: Operations in Java