MS Lesson13: Proxy Design Pattern, ACID & Transaction

Proxy Design Pattern

Proxy - it provides an object that acts as substitute for a real service object used by a client. A proxy receives client requests, does some work and then passes the request to a original object. 


package guru.springframework.cruddemo.proxy;

public interface DailySession {

void attendLesson();

}
package guru.springframework.cruddemo.proxy;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Attendance {

private Date date;
private boolean isPresent;

}
package guru.springframework.cruddemo.proxy;

import lombok.Data;

@Data
public class Student implements DailySession {

private final Attendance attendance;

public Student(Attendance attendance) {
this.attendance = attendance;
}

@Override
public void attendLesson() {
System.out.println("Attending the session...");
}

}


package guru.springframework.cruddemo.proxy;

public class StudentProxy extends Student {

public StudentProxy(Attendance attendance) {
super(attendance);
}

@Override
public void attendLesson() {
if (super.getAttendance().isPresent()) {
super.attendLesson();
} else {
throw new RuntimeException("Student is not present and can't attend session");
}
}

}


package guru.springframework.cruddemo.proxy;

import java.util.Date;

public class Teacher {

public static void main(String[] args) {
DailySession dailySession = new StudentProxy(new Attendance(new Date(), true));
dailySession.attendLesson();
}

}


bu daha cox CGLIB proxylemeye oxshayir


ve ya

package guru.springframework.cruddemo.proxy;

public class StudentProxy implements DailySession {

private final DailySession target;
private final Attendance attendance;

public StudentProxy(DailySession target, Attendance attendance) {
this.target = target;
this.attendance = attendance;
}

@Override
public void attendLesson() {
if (attendance.isPresent()) {
target.attendLesson();
} else {
throw new RuntimeException("Student is not present and can't attend session");
}
}

}


package guru.springframework.cruddemo.proxy;

import java.util.Date;

public class Teacher {

public static void main(String[] args) {
Attendance attendance = new Attendance(new Date(), true);
DailySession realStudent = new Student(attendance);
DailySession dailySession = new StudentProxy(realStudent, attendance);
dailySession.attendLesson();
}

}


bu ise klassik Static Proxy patterni "canonical" forma


Proxy bize ne qazandirdi:

* Biznes kodunu ayirdi:

Student.attendLesson() sadece oz ishini edir. 

isPresent kimi qaydalar ayrica StudentProxy-dedir.

* Cross-cutting logic elave etdi

Elave davranishi (check, logging, security, transaction) esas klasa toxunmadan elave edildi. 

* Single responsibility yaxshilashdi

Student = lesson logic

StudentProxy = access/control logic

* Deyishiklikde risk azaldi

Yeni qayda gelende real klasi dagitmiriq, proxy qatinda deyishirik

* Spring AOP ucun zehni model verdi

@Transactional, @Cashable, @PreAuthorize hamisi eyni fikir:

cagirish proxy-den kecir, evvel/sonra elave ish gorulur




1. Static proxy


interface PaymentService {
void pay(long amount);
}
class PaymentServiceImpl implements PaymentService {
@Override
public void pay(long amount) {
System.out.println("Real payment: " + amount);
}
}
class PaymentServiceLoggingProxy implements PaymentService {
private final PaymentService target;
PaymentServiceLoggingProxy(PaymentService target) {
this.target = target;
}
@Override
public void pay(long amount) {
long start = System.currentTimeMillis();
System.out.println("[LOG] pay start");
try {
target.pay(amount); // real call
System.out.println("[LOG] pay success");
} catch (Exception e) {
System.out.println("[LOG] pay failed: " + e.getMessage());
throw e;
} finally {
System.out.println("[LOG] took ms = " + (System.currentTimeMillis() - start));
}
}
}


Istifadesi:

PaymentService service = new PaymentServiceLoggingProxy(new PaymentServiceImpl());
service.pay(100);



2. Protection proxy

class PaymentServiceSecurityProxy implements PaymentService {
private final PaymentService target;
private final String currentRole;
PaymentServiceSecurityProxy(PaymentService target, String currentRole) {
this.target = target;
this.currentRole = currentRole;
}
@Override
public void pay(long amount) {
if (!"ADMIN".equals(currentRole)) {
throw new RuntimeException("Access denied");
}
target.pay(amount);
}
}



3. Virtual proxy

interface ReportService {
String buildReport();
}
class HeavyReportService implements ReportService {
HeavyReportService() {
System.out.println("Heavy object created...");
// expensive init
}
@Override
public String buildReport() {
return "REPORT";
}
}
class ReportServiceVirtualProxy implements ReportService {
private HeavyReportService target; // initially null
@Override
public String buildReport() {
if (target == null) {
target = new HeavyReportService(); // lazy create
}
return target.buildReport();
}
}



Spring @Transactional elaqesi

Spring de eyni pattern-den istifade edir:

* target bizim service class

* proxy: JDK ve ya CGLIB

* interceptor: TransactionInterceptor

* around logic: begin -> method call -> commit/rollback


=========================================================================== 

ACID - tranzaksiyalarin etibarli ishlemesi ucun 4 esas prinsip teqdim edir. ACID-i esasen DB engine temin edir.

A - Atomicity 

Ya hamisi ya hec biri. Bir tranzaksiyanin icinde 5 addim varsa ya 5-de commit olur ya da 1 fail olursa rollback olur. 


C - Consistency

Tranzaksiyadan evvel DB duzgun idise tranzaksiyada sonra da DB duzgun olmalidir. 
Duzgun ne demekdir?
* DB constaint-ler: non null, unique, foreign key, check
* Biznes qaydalari: balans menfi olmasin, stok sifirdan ashagi dushmesin ve s.

Mes: email unique olmalidir, eyni email ikinci defe insert edilse DB reject versin


I - Isolation

Paralele tranzaksiyalar bir birini korlamasin. Eyni anda iki istifadeci eyni datani ishledende biri o birinin yarimciq veya qarishiq neticelerini gormeli/gormemelidir (biznes mentiqden asili olaraq). Yeni Isolation paralel ishlemenin riskini idare edir.


D - Durability

Tranzaksiya commit olduqdan sonra server restart olsada, proses cokse de, melumat itmir. Bu adeten WAL/log, disk flush ve DB recovery mexanizmleri ile temin olunur. 


@Transactional - ACID-in tetbiqde istifade olunan duymesidir; duymeni Spring basir, ACID-i DB heyata kecirir.

* Atomicity 
@Transactional metod daxilinde bir nece emeliyyati bir transaciton-a salir. Exception olarsa rollback edir ve yarimciq veziyyet qalmir. 

* Consistency 
Commit zamani DB constraint-lerini yoxlayir (FK, UNIQUE, CHECK). Qaydalar pozularsa commit olmur/rollback olur. 

* Isolation
@Transactional(isolation = ...) ile paralel tranzaksiya davranishini teyin etmek olur (READ_COMMITTED, SERIALIZABLE ve s). 

* Durability
Commit verildikden sonra neticenin qalici olmasini DB temin edir (WAL/log ve recovery mexanizmi ile). 
@Transactional sadece commit emri verir. 



WAL nedir?

Write-Ahead Logging

DB bir deyishiklik edende, evvelce hemin deyishikliyin "jurnalini" (log record) yazir, sonra esas data fayllarini yenileyir. Esas sebeb durability ve crash recovery. Server sondu, process oldu, elektrik kesildi ve s. DB restart olanda WAL-i oxuyub: commit olunmush deyishiklikleri berpa edir (redo), yarimciq tranzaksiyalari tetbiq etmir. 



@Transactional

@Transactional birbasha DB-ye "Begin/Commit" gondermir. Arada bu zencirler var:

1. Cagirish bir Spring Proxy-den kecir (AOP)

2. Proxy TransactionInterceptor-u ishe salir

3. Interceptor PlatformTransactionManager ile transaction acir

4. Yazdigimiz metod ishleyir

5. Exception yoxdursa commit, uygun exception varsa rollback


PlatformTransactionManager Spring-in transaction ucun esas interface-dir. En cox rast gelinen implementasiyalari: 

*DatasourceTransactionManager (JDBC)

*JpaTransactionManger (JPA/Hibernate)

*HibernateTransactionManager (pure Hibernate hallarinda)

Adeten default olaraq JpaTransactionManager ishleyir.


TransactionInterceptor

AOP interceptor-dur, @Transactional qaydalarini oxuyur:

*propagation nedir?

*isolation nedir?

*rollback qaydalari nedir?

sonra transaction manager-e deyir:

*"metoddan evvel transaction ac"

*"metod bitende commit/rollback ele"



Spring @Transactional olan bean-i birbasha yox, cox vaxt proxy kimi verir.

Proxy novleri:

* JDK Dynamic Proxy: interface uzerinden

* CGLIB Proxy: class-i subclass edib


CGLIB proxy:

package guru.springframework.cruddemo.service.impl;

import guru.springframework.cruddemo.service.PersonService;
import lombok.RequiredArgsConstructor;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class PersonServiceImpl implements PersonService {

private final ApplicationContext applicationContext;

@Override
@Transactional
public void inspectProxyType() {
Object bean = applicationContext.getBean(PersonService.class);
System.out.println("PersonService bean class = " + bean.getClass().getName());
System.out.println("isAopProxy = " + AopUtils.isAopProxy(bean));
System.out.println("isCglib = " + AopUtils.isCglibProxy(bean));
System.out.println("isJdk = " + AopUtils.isJdkDynamicProxy(bean));
System.out.println("targetClass = " + AopUtils.getTargetClass(bean).getName());
}

}



JDK Dynamic Proxy:

spring:
aop:
proxy-target-class: false

Bu tip proxy-de klasi kast etmek olmaz, bu ClassCastException verecek.

CGLIB-de bu cast cox vaxt ishleyir (proxy subclass olduguna gore).


Interface olmada JDK Dynamic Proxy yaratmaq olmur, avtomatik CGLIB Proxy yaranacaq. 



                                            JDK Dynamic Proxy vs CGLIB Proxy

1. Neyi proxyleyir?

* JDK Proxy: yalniz interface metodlarini

* CGLIB: class-i subclass edib, interface olmasa da metodlari intercept edir. 


2. Final mehdudiyyetleri.

* JDK Proxy: final class/method problemi yoxdur (interfaceye baxir)

* CGLIB: final class ve final method proxylemir (override ede bilmir)


3. Performans.

* Muasir JVM lerde ferq adeten cox kicikdir. Real sistemde secimi performansdan cox dizayn ve mehdudiyyetler mueyyen edir. 


4. Ustunlukler.

* JDK ustunluyu: sade, interface kontraktina sadiq, final metod problemi yoxdur.

* CGLIB ustunluyu: interface olmasa da ishleyir, class-based interception imkani verir.


5. Secim qaydasi.

* Interface-le ishleyirsense ve "contract-first" isteyirsense -> JDK

* Interface yoxdur ve ya klaslari proxy elemek lazimdirsa -> CGLIB 





                                            S (Shared) lock vs X (Exclusive) lock

S lock - sen oxuyanda bashqasi eyni anda deyishib "oxudugun melumati yari yolda deyishmesin".

S lock = tehlukesiz oxu. PESSIMISTIC_READ

X lock - iki tranzaksiya eyni setri eyni anda update edib neticeni pozmasin ("lost update").

X lock = tehlukesiz yazi. PERSSIMISTIC_WRITE










Комментарии

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

Interview questions

Lesson1: JDK, JVM, JRE

Lesson_2: Operations in Java