Distributed data patterns - microservice yoki modullarga bo‘lingan system’da ma’lumotlar bir nechta joyda saqlanganda consistency, transaction, retry, event va failure muammolarini boshqarish usullari hisoblanadi.
Bu blok quyidagilarni o‘z ichiga oladi: SAGA orchestration vs choreography, Transactional Outbox + Debezium CDC, Idempotent Consumer, Distributed Locking, va Two-Phase Commit’dan qachon qochishni hal qiladi.
1. Muammo nimada?
Monolith’da bitta database bo‘lsa, transaction oddiy:
Order yaratildi
Payment yozildi
Stock kamaytirildi
Delivery yaratildi
↓
bitta database transactionKodda:
@Transactional
public void checkout() {
orderRepository.save(order);
paymentRepository.save(payment);
stockRepository.decrease(productId);
deliveryRepository.create(orderId);
}Agar xato bo‘lsa:
rollbackHammasi bitta database ichida qaytadi.
2. Distributed system’da muammo
Microservice architecture’da har service odatda o‘z database’iga ega:
order-service → order_db
payment-service → payment_db
inventory-service → inventory_db
delivery-service → delivery_dbEndi bitta @Transactional bilan hammasini boshqarib bo‘lmaydi.
Masalan checkout flow:
1. Order yaratish
2. Payment olish
3. Stock kamaytirish
4. Delivery yaratishAgar 3-qadamda stock kamaytirish yiqilsa:
Order yaratildi
Payment olindi
Stock kamaymadi
Delivery yaratilmadiSystem "yarim bajarilgan" holatda qoladi.
Distributed data patterns shuni boshqarish uchun kerak.
3. Muhim tushuncha: local transaction
Har service faqat o‘z database’ida transaction qila oladi.
order-service:
@Transactional
order_db ichida transaction
payment-service:
@Transactional
payment_db ichida transaction
inventory-service:
@Transactional
inventory_db ichida transactionLekin bularni bitta global transaction qilish murakkab va ko‘pincha yomon tanlov.
Shuning uchun distributed system’da ko‘proq quyidagi model ishlatiladi:
Local transaction
+ Event
+ Retry
+ Compensation
+ Idempotency
+ Observability4. SAGA Pattern
SAGA - uzun biznes transaction’ni bir nechta kichik local transactionlarga bo‘lish patterni.
Har qadam:
local transaction bajaradi
keyingi qadamni chaqiradi yoki event chiqaradi
xato bo‘lsa compensation ishlaydiMasalan order checkout:
Create Order
↓
Reserve Stock
↓
Take Payment
↓
Create Delivery
↓
Complete OrderAgar payment fail bo‘lsa:
Payment failed
↓
Release Stock
↓
Cancel OrderBu yerda rollback database rollback emas. Bu compensating action deyiladi.
5. Compensation nima?
Compensation - oldin bajarilgan qadamni biznes jihatdan bekor qilish.
Misollar:
Amal | Compensation |
|---|---|
Order yaratildi | Order cancel qilinadi |
Stock reserve qilindi | Stock release qilinadi |
Payment olindi | Refund qilinadi |
Delivery yaratildi | Delivery cancel qilinadi |
Bonus berildi | Bonus qaytarib olinadi |
Muhim:
Compensation har doim real rollback emas. Masalan payment refund qilinsa, pul darhol qaytmasligi mumkin. Lekin biznes holat tuzatiladi.
6. SAGA ikki xil bo‘ladi
1. Choreography
2. Orchestration7. SAGA Choreography
Choreography’da markaziy boshqaruvchi yo‘q. Har service event eshitadi va o‘z ishini qiladi.
Flow:
OrderCreatedEvent
↓
Inventory service stock reserve qiladi
↓
StockReservedEvent
↓
Payment service pul oladi
↓
PaymentCompletedEvent
↓
Delivery service delivery yaratadi
↓
DeliveryCreatedEvent
↓
Order service orderni CONFIRMED qiladiChoreography example
order-service:
OrderCreatedEvent publish qiladi
inventory-service:
OrderCreatedEvent eshitadi
StockReservedEvent publish qiladi
payment-service:
StockReservedEvent eshitadi
PaymentCompletedEvent publish qiladi
delivery-service:
PaymentCompletedEvent eshitadi
DeliveryCreatedEvent publish qiladiKodda ko‘rinishi:
@Component
class InventoryOnOrderCreated {
private final InventoryService inventoryService;
private final ApplicationEventPublisher events;
@EventListener
public void on(OrderCreatedEvent event) {
inventoryService.reserve(event.orderId(), event.items());
events.publishEvent(new StockReservedEvent(
event.orderId(),
event.items()
));
}
}Choreography afzalliklari
loose coupling
markaziy coordinator yo‘q
service’lar mustaqilroq
event-driven architecture uchun tabiiy
kichik flowlarda yaxshiChoreography kamchiliklari
flow qayerda tugashini tushunish qiyin
debug qilish murakkab
event chain ko‘payib ketadi
failure handling tarqalib ketadi
business process diagrammasi kodda aniq ko‘rinmaydiKatta flow’da shunday bo‘lib ketadi:
OrderCreated → StockReserved → PaymentStarted → PaymentCompleted
→ DeliveryCreated → InvoiceGenerated → NotificationSentXato qayerda bo‘lganini topish qiyinlashadi.
8. SAGA Orchestration
Orchestration’da bitta markaziy orchestrator flow’ni boshqaradi.
Masalan:
OrderSagaOrchestrator
↓
InventoryService.reserve()
↓
PaymentService.charge()
↓
DeliveryService.create()
↓
OrderService.confirm()Agar payment fail bo‘lsa:
OrderSagaOrchestrator
↓
InventoryService.release()
↓
OrderService.cancel()Orchestration misol kod:
@Service
public class OrderSagaOrchestrator {
private final InventoryClient inventoryClient;
private final PaymentClient paymentClient;
private final DeliveryClient deliveryClient;
private final OrderService orderService;
public void process(Long orderId) {
try {
inventoryClient.reserve(orderId);
paymentClient.charge(orderId);
deliveryClient.create(orderId);
orderService.confirm(orderId);
} catch (PaymentFailedException ex) {
inventoryClient.release(orderId);
orderService.cancel(orderId);
}
}
}Bu soddalashtirilgan. Production’da retry, timeout, state machine, event log, idempotency kerak bo‘ladi.
Orchestration afzalliklari
business flow bitta joyda ko‘rinadi
debug qilish osonroq
compensation tartibli
murakkab processlar uchun yaxshi
monitoring qilish osonOrchestration kamchiliklari
orchestrator ko‘p narsani biladi
coupling kuchayishi mumkin
orchestrator failure nuqtasi bo‘lishi mumkin
service’lar orasida command-style dependency paydo bo‘ladi9. Choreography vs Orchestration
Mezon | Choreography | Orchestration |
|---|---|---|
Boshqaruvchi | Yo‘q | Bor |
Coupling | Pastroq | Yuqoriroq |
Flow visibility | Pastroq | Yuqori |
Debug | Qiyinroq | Osonroq |
Kichik flow | Yaxshi | Ortiqcha bo‘lishi mumkin |
Murakkab flow | Chalkashishi mumkin | Yaxshi |
Monitoring | Tarqoq | Markazlashgan |
Qisqa qoida:
Kichik, oddiy event flow → choreography
Murakkab, ko‘p qadamli biznes process → orchestration10. Transactional Outbox Pattern
Distributed system’da eng katta muammolardan biri:
Database’ga yozildi, lekin event publish bo‘lmadi.Masalan:
@Transactional
public void createOrder(CreateOrderCommand command) {
orderRepository.save(order);
kafkaTemplate.send("order-created", event);
}Bu xavfli.
Nima bo‘lishi mumkin?
Order DB’ga yozildi
Kafka publish vaqtida broker down bo‘ldi
Transaction commit bo‘ldi
Lekin event ketmadi
Payment service order borligini bilmadiYoki teskarisi:
Kafka event ketdi
DB transaction rollback bo‘ldi
Payment service mavjud bo‘lmagan order uchun payment boshladi11. Outbox yechimi
Transactional Outbox’da biznes data va event bir xil database transaction’da yoziladi.
orders table
outbox_events tableTransaction:
1. orders table’ga order yoziladi
2. outbox_events table’ga OrderCreated event yoziladi
3. transaction commitKeyin alohida process outbox’dan eventni brokerga yuboradi.
Outbox table
CREATE TABLE outbox_events (
id UUID PRIMARY KEY,
aggregate_type VARCHAR(100) NOT NULL,
aggregate_id VARCHAR(100) NOT NULL,
event_type VARCHAR(100) NOT NULL,
payload JSONB NOT NULL,
status VARCHAR(30) NOT NULL,
created_at TIMESTAMP NOT NULL,
published_at TIMESTAMP
);Order create
@Transactional
public void createOrder(CreateOrderCommand command) {
Order order = Order.create(command.customerId(), command.items());
orderRepository.save(order);
OutboxEvent event = OutboxEvent.of(
"Order",
order.getId().toString(),
"OrderCreated",
toJson(new OrderCreatedEvent(order.getId(), order.getTotal()))
);
outboxRepository.save(event);
}Muhim:
Order va OutboxEvent bitta transaction’da commit bo‘ladi.12. Outbox publisher
Keyin background worker eventlarni publish qiladi:
@Scheduled(fixedDelay = 1000)
public void publishOutboxEvents() {
List<OutboxEvent> events = outboxRepository.findTop100ByStatus("NEW");
for (OutboxEvent event : events) {
kafkaTemplate.send(event.getEventType(), event.getPayload());
event.markPublished();
outboxRepository.save(event);
}
}Bu oddiy variant. Production’da quyidagilar kerak:
batch processing
locking
retry count
dead-letter status
idempotent publish
ordering
metrics
backpressure13. Outbox + Debezium CDC
Outbox polling qilish mumkin, lekin katta system’da ko‘pincha CDC ishlatiladi.
CDC - Change Data Capture.
Flow:
Application
↓
orders + outbox_events DB’ga yozadi
↓
Debezium database transaction log’ni o‘qiydi
↓
Outbox row paydo bo‘lganini ko‘radi
↓
Kafka’ga event chiqaradiArchitecture:
order-service
↓ local transaction
PostgreSQL
↓ WAL/binlog
Debezium
↓
Kafka topic
↓
payment-serviceNima uchun Debezium yaxshi?
application polling qilmaydi
DB transaction log’dan ishonchli o‘qiydi
event yo‘qolish xavfi kamayadi
high-throughput event publishing uchun yaxshi
outbox relay alohida infrastructure bo‘ladiLekin complexity oshadi:
Debezium connector setup
Kafka dependency
schema evolution
connector monitoring
offset management
DLQ strategy14. Outbox pattern nimani kafolatlaydi?
Transactional Outbox shuni kafolatlaydi:
Agar order DB’da bo‘lsa, outbox event ham DB’da bo‘ladi.Lekin shuni kafolatlamaydi:
consumer eventni faqat bir marta oladi
event duplicate bo‘lmaydi
event doim tartibda keladi
consumer xatosiz ishlaydiShuning uchun keyingi pattern kerak:
Idempotent Consumer15. Idempotent Consumer Pattern
Event-driven system’da bitta event bir necha marta kelishi mumkin.
Sabablar:
producer retry
broker retry
consumer ack bermadi
consumer qayta ishga tushdi
network timeout
outbox relay duplicate publish qildiDemak consumer shunday yozilishi kerak:
Bitta event 2 marta kelsa ham biznes natija 1 marta bajarilsin.
Bu idempotency deb ataladi.
Yomon consumerga misol:
@KafkaListener(topics = "payment-completed")
public void on(PaymentCompletedEvent event) {
orderService.markPaid(event.orderId());
notificationService.sendPaymentSuccess(event.orderId());
}Agar event 2 marta kelsa:
order ikki marta update bo‘lishi mumkin
notification ikki marta ketishi mumkin
bonus ikki marta yozilishi mumkinYaxshi consumer: processed_events table
CREATE TABLE processed_events (
event_id UUID PRIMARY KEY,
processed_at TIMESTAMP NOT NULL
);Consumer:
@Transactional
public void handle(PaymentCompletedEvent event) {
if (processedEventRepository.existsById(event.eventId())) {
return;
}
orderService.markPaid(event.orderId());
processedEventRepository.save(new ProcessedEvent(
event.eventId(),
Instant.now()
));
}Bu bilan event duplicate kelsa, ikkinchi marta bajarilmaydi.
16. Idempotency key
REST API’da ham idempotency kerak bo‘ladi.
Masalan payment API:
POST /payments
Idempotency-Key: 8f1a-xyz-123Agar client timeout sabab requestni qayta yuborsa, payment ikki marta olinmasligi kerak.
Table:
CREATE TABLE idempotency_keys (
key VARCHAR(100) PRIMARY KEY,
request_hash VARCHAR(255),
response_body JSONB,
status VARCHAR(30),
created_at TIMESTAMP
);Flow:
1. Request keldi
2. Idempotency-Key tekshirildi
3. Oldin bajarilgan bo‘lsa, eski response qaytarildi
4. Yangi bo‘lsa, operation bajarildi
5. Natija key bilan saqlandi17. Idempotent Consumer’da muhim qoidalar
event’da unique eventId bo‘lsin
processed_events table transaction ichida yozilsin
business operation va processed marker bitta transaction’da bo‘lsin
side effectlar ehtiyot bilan qilinsin
notification/email ham duplicate-safe bo‘lsinYomon:
email yuborildi
keyin processed_events yozishda xato bo‘ldi
event qayta keldi
email yana yuborildiYechim:
email ham outbox orqali yuboriladi
yoki notification service idempotent bo‘ladi18. Distributed Locking
Distributed lock - bir nechta instance bir xil ishni bir vaqtda bajarmasligi uchun lock.
Masalan:
order-service 3 ta podda ishlayapti
har pod scheduled job ishga tushiradi
invoice generation faqat bitta podda ishlashi kerakDistributed lock kerak bo‘lishi mumkin:
scheduled job faqat bitta instance’da
bir resource’ni bir vaqtda bitta process update qilsin
inventory reservation
leader election
rate-limited external API call19. Redis/Redisson lock
Spring projectlarda ko‘p ishlatiladigan variant:
RLock lock = redissonClient.getLock("invoice-generation");
boolean acquired = lock.tryLock(5, 30, TimeUnit.SECONDS);
if (!acquired) {
return;
}
try {
invoiceService.generateInvoices();
} finally {
lock.unlock();
}Bu nimani anglatadi?
5 sekund ichida lock olishga urinadi
lock olinsa 30 sekund lease time
ish tugasa unlock qiladi20. Distributed lock xavflari
Distributed lock oson ko‘rinadi, lekin xavfli.
Muammolar:
lock olindi, process o‘ldi
lease time noto‘g‘ri tanlandi
network partition
clock drift
long-running job lease’dan uzoq davom etdi
unlock noto‘g‘ri instance’dan qilindi
Redis unavailable bo‘ldiQoida:
Distributed lock’ni transaction o‘rniga ishlatmang. U faqat coordination uchun.
21. Database lock alternativasi
Ba’zan DB lock yetarli bo‘ladi.
Masalan PostgreSQL advisory lock:
SELECT pg_try_advisory_lock(12345);Yoki row-level lock:
SELECT *
FROM jobs
WHERE status = 'NEW'
FOR UPDATE SKIP LOCKED
LIMIT 100;SKIP LOCKED job processing uchun juda foydali:
bir nechta worker bor
har biri lock bo‘lmagan joblarni oladi
bir job ikki workerga tushmaydi22. Distributed lock qachon kerak emas?
Ko‘p holatda lock o‘rniga yaxshiroq pattern bor:
Muammo | Lock o‘rniga |
|---|---|
Duplicate event | Idempotent consumer |
Event publish reliability | Outbox |
Concurrent update | Optimistic locking |
Job processing | Queue + SKIP LOCKED |
Global order | Kafka partition key |
Payment duplicate | Idempotency key |
Arxitektor qoida:
Lock - oxirgi variantlardan biri. Avval idempotency, queue, database constraint, optimistic locking o‘ylansin.
23. Two-Phase Commit - 2PC
Two-Phase Commit - bir nechta resource orasida distributed transaction qilish usuli.
Masalan:
order_db
payment_db
inventory_dbhammasi bitta global transaction’da commit/rollback bo‘lishi kerak.
2PC ikki bosqich:
1. Prepare phase
2. Commit phaseCoordinator resource’lardan so‘raydi:
"Commit qilishga tayyormisiz?"Hammasi "ha" desa:
commitBiri "yo‘q" desa:
rollback24. 2PC nega yomon?
Nazariy jihatdan chiroyli. Amalda distributed microservice’da ko‘pincha yomon.
Kamchiliklar:
coordinator kerak
resource’lar lock ushlab turadi
latency oshadi
availability pasayadi
network failure’da murakkab holat
service autonomy buziladi
cloud-native architecture’ga mos emas
scaling qiyinMicroservice’da bitta service yiqilsa, butun transaction osilib qolishi mumkin.
25. 2PC qachon ishlatilishi mumkin?
Ba’zi enterprise systemlarda hali ham ishlatiladi:
legacy enterprise app
XA transaction support bor
kam traffic
strong consistency juda muhim
bitta organization ichidagi controlled environmentLekin zamonaviy cloud-native Spring microservice architecture’da ko‘pincha:
SAGA + Outbox + Idempotencyyaxshiroq.
26. 2PC o‘rniga nima?
Kerak bo‘lgan narsa | Tavsiya |
|---|---|
Cross-service business transaction | SAGA |
Reliable event publish | Transactional Outbox |
DB change event | Debezium CDC |
Duplicate eventdan himoya | Idempotent Consumer |
Concurrent update | Optimistic Locking |
One-at-a-time job | Queue / DB lock / Redis lock |
Strong audit | Event log / outbox / audit table |
27. Consistency turlari
Distributed system’da ko‘pincha eventual consistency qabul qilinadi.
Strong consistency
hamma joy darhol bir xilMonolith + bitta DB’da osonroq.
Eventual consistency
hozircha order CREATED
bir necha sekunddan keyin PAID
keyin CONFIRMEDYa’ni system vaqt o‘tib consistent bo‘ladi.
Misol:
Order created
Payment processing
Payment completed
Order confirmedFoydalanuvchiga status ko‘rsatiladi:
{
"orderId": 123,
"status": "PAYMENT_PROCESSING"
}28. Eventual consistency UX masalasi
Backend eventual consistent bo‘lsa, frontend ham shuni tushunishi kerak.
Yomon UX:
Buyurtma yaratildi, lekin payment hali tugamagan.
Frontend "xato" deb ko‘rsatadi.Yaxshi UX:
Buyurtma qabul qilindi.
To‘lov tekshirilmoqda.
Statusni yangilab turamiz.Statuslar:
CREATED
STOCK_RESERVED
PAYMENT_PROCESSING
PAID
CONFIRMED
FAILED
CANCELLED29. Ordering muammosi
Eventlar noto‘g‘ri tartibda kelishi mumkin:
PaymentCompletedEvent oldin keldi
OrderCreatedEvent keyin keldiYoki bir aggregate uchun eventlar tartibi buziladi.
Yechimlar:
Kafka partition key = aggregateId
event version
sequence number
consumer buffering
idempotent state transitionMasalan:
public record OrderEvent(
UUID eventId,
Long orderId,
long version,
String type,
Instant occurredAt
) {}Consumer eski versionni ko‘rsa ignore qiladi.
30. Optimistic locking
Concurrent update uchun distributed lock shart emas. Ko‘p holatda optimistic locking yetarli.
Entity:
@Entity
public class OrderEntity {
@Id
private Long id;
@Version
private Long version;
private OrderStatus status;
}Agar ikki transaction bir orderni update qilsa:
birinchisi yutadi
ikkinchisi OptimisticLockException oladiKeyin retry yoki business error.
Bu inventory, order status, account update kabi joylarda foydali.
31. Transactional Inbox Pattern
Outbox producer tomonida reliable event publish uchun.
Inbox consumer tomonida reliable event processing uchun.
incoming_events tableConsumer eventni avval inbox’ga yozadi, keyin process qiladi.
Flow:
Kafka event keldi
↓
incoming_events table’ga yozildi
↓
business processing
↓
processed statusBu ayniqsa complex consumer processing uchun foydali bo'ladi.
32. Dead Letter Queue - DLQ
Agar event qayta-qayta process bo‘lmasa, cheksiz retry qilish yomon.
Yechim:
retry 3 marta
keyin DLQ topic
alert
manual investigation
replay toolFlow:
payment-events
↓
consumer fail
↓ retry
consumer fail
↓ retry
consumer fail
↓
payment-events-dlqDLQ production’da shart:
xato event yo‘qolmasin
consumer to‘xtab qolmasin
manual fix/replay bo‘lsin33. Real flow: Order checkout
Production-ready distributed flow:
1. order-service order yaratadi
2. order_db orders + outbox_events yoziladi
3. Debezium outbox eventni Kafka’ga chiqaradi
4. inventory-service OrderCreatedEvent eshitadi
5. inventory stock reserve qiladi
6. inventory outbox StockReservedEvent chiqaradi
7. payment-service StockReservedEvent eshitadi
8. payment idempotency key bilan charge qiladi
9. PaymentCompletedEvent chiqadi
10. order-service eventni idempotent consume qiladi
11. order status CONFIRMED bo‘ladiFailure case:
payment fail
↓
PaymentFailedEvent
↓
inventory stock release
↓
order status CANCELLED34. Spring Boot structure
com.company.order
├── domain
│ ├── Order.java
│ └── OrderStatus.java
│
├── application
│ ├── CreateOrderHandler.java
│ ├── ConfirmOrderHandler.java
│ └── CancelOrderHandler.java
│
├── outbox
│ ├── OutboxEvent.java
│ ├── OutboxRepository.java
│ └── OutboxService.java
│
├── inbox
│ ├── ProcessedEvent.java
│ └── ProcessedEventRepository.java
│
├── messaging
│ ├── OrderEventPublisher.java
│ └── PaymentCompletedListener.java
│
└── web
└── OrderController.java35. Eng ko‘p xatolar
Xato 1: @Transactional bilan Kafka publish qilish
@Transactional
public void createOrder() {
orderRepository.save(order);
kafkaTemplate.send("order-created", event);
}Bu atomic emas.
Yaxshiroq:
DB + Outbox bitta transaction
Kafka publish alohida relay/CDC orqaliXato 2: Consumer idempotent emas
Duplicate event kelganda biznes ikki marta bajariladi.
Xato 3: SAGA’da compensation yo‘q
Flow fail bo‘lsa system yarim holatda qoladi.
Xato 4: Distributed lock bilan hamma narsani yechish
Lock consistency muammolarini to‘liq hal qilmaydi. Ko‘p holatda idempotency va outbox kerak.
Xato 5: 2PC’ni microservice’da default tanlash
Strong consistency xohishi availability va scalability’ni yomonlashtirishi mumkin.
Xato 6: Event schema versioning yo‘q
Event o‘zgarsa consumerlar buziladi.
Yaxshi event:
{
"eventId": "uuid",
"eventType": "OrderCreated",
"eventVersion": 1,
"occurredAt": "2026-05-21T10:00:00Z",
"payload": {
"orderId": 123
}
}36. Architect checklist
Distributed data design qilishdan oldin savollar javob berish kerak:
Har service o‘z database’iga egami?
Cross-service transaction bormi?
Failure bo‘lsa compensation qanday?
Event publish reliable bo‘ladimi?
Consumer duplicate eventga tayyormi?
Event ordering kerakmi?
DLQ strategy bormi?
Retry policy qanday?
Business statuslar eventual consistency’ni ko‘rsatadimi?
Observability event flow’ni ko‘rsatadimi?
2PC haqiqatan kerakmi yoki SAGA yetadimi?37. Qisqa tavsiya
Katta Spring microservice system uchun odatiy stack:
SAGA
+ Transactional Outbox
+ Debezium CDC
+ Kafka
+ Idempotent Consumer
+ DLQ
+ Optimistic Locking
+ ObservabilityLock kerak bo‘lsa:
avval database constraint / optimistic lock / queue o‘yla
keyin Redis/Redisson distributed lock2PC kerak bo‘lsa:
juda ehtiyot bo‘l
cloud-native microservice’da default tanlama38. Arxitektor xulosasi
Distributed data patterns - distributed system’da "hammasi bitta transaction bo‘lsin" degan fikrdan voz kechib, real failure’larni boshqarish yondashuvi hisoblanadi.
Asosiy formula:
Local transaction
+ Reliable event publishing
+ Idempotent consuming
+ Compensation
+ Retry/DLQ
+ Observability
= production-ready distributed data architectureEng muhim qoida:
Distributed system’da failure normal holat. Architecture failure bo‘lmasligiga emas, failure bo‘lganda system to‘g‘ri tiklanishiga quriladi.