SlideShare ist ein Scribd-Unternehmen logo
1 von 46
The dual write problem
Jeppe Cramon - @jeppec
Cloud Create ApS
@jeppec
Essential complexity of 2 way integration
Component
Warehouse
Component
Order
Component
Billing
UI
Send-
Invoice
Save-Order
Order:Save-Order()è
call Warehouse:Reserve-Items()
call Billing:Send-Invoice()
commit()
Reserve-
Items
Local transaction between
the 3 Components
@jeppec
Let’s (micro)service’ify this
@jeppec
Accidental complexity from distributed service integration
Warehouse
Service
Order
Service
Billing
Service
UI
Send-
Invoice
Save-Order
Reserve-
Items
Local transaction between 2
local “Services”
DUAL WRITE
using Remote call
@jeppec
Microservices turns a
local functional call
into a distributed problem
@jeppec
With cross SERVICE integration
we’re bound by the laws of
distributed computing
@jeppec
The 11 Fallacies of Distributed Computing
These fallacies are assumptions architects, designers and developers
of distributed systems are likely to make. The fallacies will be proven
wrong in the long run - resulting in all sorts of troubles and pains for
the solution and architects who made the assumptions.
1. The network is reliable.
2. Latency is zero.
3. Bandwidth is infinite.
4. The network is secure.
5. Topology doesn't change.
6. There is one administrator.
7. Transport cost is zero.
8. The network is homogeneous.
9. The System is atomic/monolithic
10. The System is Finished
11. Business logic can and should be centralized
See https://arnon.me/wp-content/uploads/Files/fallacies.pdf
@jeppec
Order:Save-Order()è
call Warehouse:Reserve-Items()
call Billing:Send-Invoice()
if (Billing:Call-Failed:Too-Busy?)
Wait-A-While()
call Billing:Send-Invoice()
if (Billing:Call-Failed:Too-Busy?)
Wait-A-Little-While-Longer()
call Billing:Send-Invoice()
if (Billing:Call-Failed:IO-Error?)
Save-We-Need-Check-If-Call-Billing-Succeded-After-All
AND We-Need-To-Retry call Order:Save-Order and call Warehouse:Reserve-Items
AND Tell-Customer-That-This-Operation-Perhaps-Went-Well
if (Billing:Call-Went-Well?)
commit()
Accidental complexity from distributed service integration
Warehouse
Service
Order
Service
Billing
Service
UI
Send-
Invoice
Save-Order
Reserve-
Items
Local transaction between 2
Components
@jeppec
Remote Call
Aka. RPC/REST/SOAP/GraphQL…
@jeppec
What’s the challenge using
state mutating
Request/Response calls
between
distributed components?
@jeppec
Synchronous Request/Response
lowers our tolerance for faults
• When you get an IO error
• When servers crash or restarts
• When databases are down
• When deadlocks occurs in our databases
• Do you retry?
With synchronous Request/Response we can loose business data if there’s no automatic retry mechanism.
Also if the operation we retry isn’t idempotent* we risk having the side effect multiple times!
Client Server
Processing
The same message can be
processed more than once
*Idempotence describes the quality of an
operation in which result and state does
not change if the operation is performed
more than 1 time
Request
Processing
Duplicated Request
Duplicated Response
Response
@jeppec
Idempotence
Don’t press the button twice!
@jeppec
Ensuring
transactional
consistency
becomes much harder
@jeppec
Sales system
Sales
Update customer status (e.g. Gold customer)
Bookkeeping
Deliver goods
Delivery
system
Deliveries
Customer/
CRM system
Customers
SAP
Bookkeeping
Complete
Purchase
Transaction
Coordinator
Transactional
Resource
Request-to-Prepare
Commit
Prepared
Done
Prepare
Phase
Commit
Phase
2 Phase Commit
@jeppec
What’s wrong with distributed transactions?
• Transactions lock resources while active
• Services are autonomous
• Can’t be expected to finish within a certain time interval
• Locking keeps other transactions from completing their
job
• Locking doesn’t scale
• X Phase Commit is fragile by design
How to handle the dual
write problem?
@jeppec
Dual write problem
Order
Service
Billing
Service
Client
Send-
Invoice
Save-Order
Mutating
Remote call
Expected
transaction
boundary No simple solution (except a classic monolith)
• Local-commit-then-publish
• If the app crashes after local-
commit then the publish
operation isn’t performed
• Publish-then-local-commit
• If the local-commit fails then
we’ve already formed the publish
operation
• Billing Service may receive
published message BEFORE the
Order Service has committed
its transaction
@jeppec
Orchestration
@jeppec
Dual write problem
Orchestration / Process Manager / Saga*
Order
Service
Billing
Service
Client
Send-
Invoice
Transaction
boundary
Transaction
boundary
Save-Order
• Requires only local transactions
• Single coordinator
• Increases eventual consistency
• Requires idempotent operations
due to retries
• Requires compensations /
rollback
• Tighter coupling as the
orchestrator instructs other
services about what to do
* This isn’t the real Saga pattern
@jeppec
Choreography
@jeppec
Dual write problem
Choreography
public class OrderService {
public void handle(AcceptOrder cmd) {
orderRepository.add(new Order(cmd.orderId,
cmd.orderLines));
eventTopic.publish(new OrderAccepted(cmd.orderId,
cmd.orderLines));
}
}
public class BillingService {
public void handle(OrderAccepted cmd) {
invoiceRepository.add(new Invoice(cmd.orderId,
cmd.orderLines));
emailGateway.send(new OrderAcceptedEmail(cmd.orderId,
cmd.orderLines));
}
}
@jeppec
Dual write problem
Choreography
Order
Service
Billing
Service
Client
Send-
Invoice
Save-Order
Transaction
boundary
Transaction
boundary
• No Single coordinator, but global
system state and coordination
logic is scattered across all
participants
• Requires only local transactions
• Increases eventual consistency
• Requires idempotent operations
due to retries
• Looser coupling (if events are
used)
• Suffers from a Dual Write problem
it self
Events/Commands
Queue
@jeppec
Outbox pattern
For Choreography
@jeppec
Dual write problem
Choreography with Outbox pattern
Order
Service
Billing
Service
Client
Send-
Invoice
Save-Order
Transaction
boundary
Transaction
boundary
• You can either use a Change
Data Capture (CDC) approach
e.g. using a database Write
Ahead Log (WAL)
• Risk of CRUD events!
• Or a manual OutBox pattern
where Save-Order writes data
and the event/command into
the OutBox within the same
database. Typically requires
polling and coordination +
recovery options in a multi cluster
setup
Events/Commands
Change
Data
Capture
/
Polling
Async Push or Poll
@jeppec
Saga pattern
A failure management pattern
@jeppec
Trip reservation as a Saga
Start
Rent Car Book Hotel Reserve Flight
Cancel Hotel
Cancel Car
Book
Car Rental Hotel Rental
WorkQueue:
- Queue: “Rent Car”
- Queue: “Book Hotel”
- Queue: “Reserve Flight”
Routing slip
Flight Booking
@jeppec
Trip reservation as a Saga
Start
Rent Car Book Hotel Reserve Flight
Cancel Hotel
Cancel Car
Book Book
Car Rental Hotel Rental
WorkQueue:
- Queue: “Book Hotel”
- Queue : “Reserve Flight”
WorkStack:
- ReservationId: ABC123
Cancellation: “Cancel Car”
Routing slip Flight Booking
@jeppec
Trip reservation as a Saga
Start
Rent Car Book Hotel Reserve Flight
Cancel Hotel
Cancel Car
Book Book
Car Rental Hotel Rental
WorkQueue:
- Queue : “Reserve Flight”
WorkStack:
- Hotel ReservationId: XYZ
Cancellation: “Cancel Hotel”
- Car ReservationId: ABC123
Cancellation: “Cancel Car”
Routing slip
Book
Flight Booking
@jeppec
Trip reservation as a Saga
Start
Rent Car Book Hotel Reserve Flight
Cancel Hotel
Cancel Car
Book Book Book
Car Rental Hotel Rental
WorkStack:
- Hotel ReservationId: XYZ
Cancellation: “Cancel Hotel”
- Car ReservationId: ABC123
Cancellation: “Cancel Car”
Routing slip
Flight Booking
@jeppec
Trip reservation as a Saga
Start
Rent Car Book Hotel Reserve Flight
Cancel Hotel
Cancel Car
Book Book Book
Car Rental Hotel Rental
Cancel
WorkStack:
- Hotel ReservationId: XYZ
Cancellation: “Cancel Hotel”
- Car ReservationId: ABC123
Cancellation: “Cancel Car”
Routing slip
Flight Booking
@jeppec
Trip reservation as a Saga
Start
Rent Car Book Hotel Reserve Flight
Cancel Hotel
Cancel Car
Book Book Book
Car Rental Hotel Rental
Cancel
Cancel
WorkStack:
- Car ReservationId: ABC123
Cancellation: “Cancel Car”
Routing slip
Flight Booking
@jeppec
Trip reservation as a Saga
Start
Rent Car Book Hotel Reserve Flight
Cancel Hotel
Cancel Car
Book Book Book
Car Rental Hotel Rental
Cancel
Cancel
Cancelled
Flight Booking
@jeppec
Event Sourcing
@jeppec
Dual write problem
Choreography with Event Sourcing
Order
Service
Billing
Service
Client
Send-
Invoice
Save-Order
Transaction
boundary
Transaction
boundary
Event Store
Async
Push
or Pull
• Requires only local transactions
• Natively event driven: The Service
consumes and produces events
• Proven Audit Trail
• Flexible and adaptable
• Captures business intentions as
events
• Requires idempotent operations
@jeppec
Event Sourcing
Business-Objects/Aggregates track their own Domain Events
and derive their state from said Events
OrderCreated ProductAdded ProductAdded ProductRemoved ProductAdded OrderAccepted
Time
07:39
Time
07:40
Time
07:41
Time
07:45
Time
07:46
Time
07:50
@jeppec
Event Replaying
Type Aggregate
Identifier
GlobalOrder Event
Order
Timestamp Event
Identifier
EventType SerializedEvent
Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>…
Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>…
Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>…
Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>…
Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>…
Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>…
Order
@jeppec
Event Replaying
Type Aggregate
Identifier
GlobalOrder Event
Order
Timestamp Event
Identifier
EventType SerializedEvent
Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>…
Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>…
Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>…
Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>…
Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>…
Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>…
Order
Orderline
@jeppec
Event Replaying
Type Aggregate
Identifier
GlobalOrder Event
Order
Timestamp Event
Identifier
EventType SerializedEvent
Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>…
Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>…
Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>…
Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>…
Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>…
Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>…
Order
Orderline
Orderline
@jeppec
Event Replaying
Type Aggregate
Identifier
GlobalOrder Event
Order
Timestamp Event
Identifier
EventType SerializedEvent
Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>…
Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>…
Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>…
Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>…
Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>…
Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>…
Order
Orderline
@jeppec
Event Replaying
Type Aggregate
Identifier
GlobalOrder Event
Order
Timestamp Event
Identifier
EventType SerializedEvent
Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>…
Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>…
Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>…
Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>…
Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>…
Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>…
Order
Orderline
Orderline
@jeppec
Event Replaying
Type Aggregate
Identifier
GlobalOrder Event
Order
Timestamp Event
Identifier
EventType SerializedEvent
Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>…
Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>…
Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>…
Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>…
Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>…
Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>…
Order
Accepted: true
Orderline
Orderline
@jeppec
Client handled subscriptions
RSocketEvent
StreamSubscription
Local storage
EventStore
RSocketEvent
StreamSubscription
Local storage
EventStreamSubscription
Message
EventStreamSubscription
Message
EventStoreEventStreamPublisher
EventStoreEventStreamPublisher
Event
Event
Event Event
Supports
Single Instance
Subscriber,
which ensures
that only one
instance of
Subscriber B
has an active
subscription.
Other
instances of
the same
subscriber are
hot-standby
<<Topic Subscriber>>
Customer_Service:Some_Ac:OrderEvents
<<Topic Publisher>>
Sales_Service:OrderEvents
RSocketServer
tcp://subscribe-event-stream
A
B Subscriber B
RSocket Request/Stream
Event-Stream
Subscriber A
RSocket Request/Stream
Event-Stream
Flux<PersistedEvent> eventStream(long fromInclusiveGlobalOrder,
Option<String> subscriptionId)
@jeppec
ReactiveBus Pub/Sub
JdbcEventStores eventStores = ….
var customerAggregateType = AggregateType.from("Customer", CustomerId.class);
var customerEventStore = eventStores.getEventStoreFor(customerAggregateType);
var streamName = EventStreamName.from("CustomersStream");
reactiveBus.addEventStreamPublisher(new EventStoreEventStreamPublisher(streamName, customerEventStore, unitOfWorkFactory));
Publisher:
reactiveBus.subscribeToEventStream(
SubscriberId.from(”SalesService->CustomerEvents"),
EventStreamName.from("CustomersStream"),
EventStreamSubscriptionParameters.parameters(GlobalOrderSubscribeFromToken.fromStartOfStream()))
.doOnNext(payload -> {
System.out.println(”Received: " + e.payload.getClass() + "@" + e.globalOrder);
}).subscribe();
Subscriber:
@jeppec
Pub/Sub – Subscriber event handling
reactiveBus.subscribeToEventStream(SubscriberId.from(”SalesService->OrderEvents"),
EventStreamName.from(”OrdersStream"),
EventStreamSubscriptionParameters.parameters(GlobalOrderSubscribeFromToken.fromStartOfStream()))
.doOnNext(payload -> {
if (event instanceof OrderAdded e) {
.. = e.orderNumber;
} else if (event instanceof ProductAddedToOrder e) {
… = e.productId;
} else if (event instanceof OrderAccepted) {
…accepted = true;
}
).subscribe();
@jeppec
Pub/Sub – Subscriber event handling
@EventHandler
private void handle(OrderAdded e, EventMessage message) {
….
}
@EventHandler
private void handle(ProductAddedToOrder e) {
.…
}
@EventHandler
private void handle(OrderAccepted e) {
….
}
@jeppec
For more
see
Blog: https://cramonblog.wordpress.com/
Homepage: http://cloudcreate.dk/
Twitter: @jeppec

Weitere ähnliche Inhalte

Was ist angesagt?

Single-Page-Application & REST security
Single-Page-Application & REST securitySingle-Page-Application & REST security
Single-Page-Application & REST security
Igor Bossenko
 

Was ist angesagt? (20)

A Deep Dive into Kafka Controller
A Deep Dive into Kafka ControllerA Deep Dive into Kafka Controller
A Deep Dive into Kafka Controller
 
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...
 
Microservices + Events + Docker = A Perfect Trio (dockercon)
Microservices + Events + Docker = A Perfect Trio (dockercon)Microservices + Events + Docker = A Perfect Trio (dockercon)
Microservices + Events + Docker = A Perfect Trio (dockercon)
 
Event Sourcing & CQRS, Kafka, Rabbit MQ
Event Sourcing & CQRS, Kafka, Rabbit MQEvent Sourcing & CQRS, Kafka, Rabbit MQ
Event Sourcing & CQRS, Kafka, Rabbit MQ
 
ksqlDB: A Stream-Relational Database System
ksqlDB: A Stream-Relational Database SystemksqlDB: A Stream-Relational Database System
ksqlDB: A Stream-Relational Database System
 
kafka
kafkakafka
kafka
 
Event Driven-Architecture from a Scalability perspective
Event Driven-Architecture from a Scalability perspectiveEvent Driven-Architecture from a Scalability perspective
Event Driven-Architecture from a Scalability perspective
 
Virtual Flink Forward 2020: A deep dive into Flink SQL - Jark Wu
Virtual Flink Forward 2020: A deep dive into Flink SQL - Jark WuVirtual Flink Forward 2020: A deep dive into Flink SQL - Jark Wu
Virtual Flink Forward 2020: A deep dive into Flink SQL - Jark Wu
 
MySQL Monitoring using Prometheus & Grafana
MySQL Monitoring using Prometheus & GrafanaMySQL Monitoring using Prometheus & Grafana
MySQL Monitoring using Prometheus & Grafana
 
Kubernetes Basics
Kubernetes BasicsKubernetes Basics
Kubernetes Basics
 
Simplifying Distributed Transactions with Sagas in Kafka (Stephen Zoio, Simpl...
Simplifying Distributed Transactions with Sagas in Kafka (Stephen Zoio, Simpl...Simplifying Distributed Transactions with Sagas in Kafka (Stephen Zoio, Simpl...
Simplifying Distributed Transactions with Sagas in Kafka (Stephen Zoio, Simpl...
 
Dynamically Scaling Data Streams across Multiple Kafka Clusters with Zero Fli...
Dynamically Scaling Data Streams across Multiple Kafka Clusters with Zero Fli...Dynamically Scaling Data Streams across Multiple Kafka Clusters with Zero Fli...
Dynamically Scaling Data Streams across Multiple Kafka Clusters with Zero Fli...
 
Microservices with Kafka Ecosystem
Microservices with Kafka EcosystemMicroservices with Kafka Ecosystem
Microservices with Kafka Ecosystem
 
[2019] 200만 동접 게임을 위한 MySQL 샤딩
[2019] 200만 동접 게임을 위한 MySQL 샤딩[2019] 200만 동접 게임을 위한 MySQL 샤딩
[2019] 200만 동접 게임을 위한 MySQL 샤딩
 
Kafka Streams: What it is, and how to use it?
Kafka Streams: What it is, and how to use it?Kafka Streams: What it is, and how to use it?
Kafka Streams: What it is, and how to use it?
 
Event driven microservices
Event driven microservicesEvent driven microservices
Event driven microservices
 
Domain Driven Design
Domain Driven Design Domain Driven Design
Domain Driven Design
 
Prometheus design and philosophy
Prometheus design and philosophy   Prometheus design and philosophy
Prometheus design and philosophy
 
Single-Page-Application & REST security
Single-Page-Application & REST securitySingle-Page-Application & REST security
Single-Page-Application & REST security
 
ClickHouse Deep Dive, by Aleksei Milovidov
ClickHouse Deep Dive, by Aleksei MilovidovClickHouse Deep Dive, by Aleksei Milovidov
ClickHouse Deep Dive, by Aleksei Milovidov
 

Ähnlich wie The Dual write problem

The Art of The Event Streaming Application: Streams, Stream Processors and Sc...
The Art of The Event Streaming Application: Streams, Stream Processors and Sc...The Art of The Event Streaming Application: Streams, Stream Processors and Sc...
The Art of The Event Streaming Application: Streams, Stream Processors and Sc...
confluent
 
Kakfa summit london 2019 - the art of the event-streaming app
Kakfa summit london 2019 - the art of the event-streaming appKakfa summit london 2019 - the art of the event-streaming app
Kakfa summit london 2019 - the art of the event-streaming app
Neil Avery
 
Event Driven Architectures
Event Driven ArchitecturesEvent Driven Architectures
Event Driven Architectures
Avinash Ramineni
 
Don't call us - we'll push - on cross tier push architecture (NLJUG JFall 201...
Don't call us - we'll push - on cross tier push architecture (NLJUG JFall 201...Don't call us - we'll push - on cross tier push architecture (NLJUG JFall 201...
Don't call us - we'll push - on cross tier push architecture (NLJUG JFall 201...
Lucas Jellema
 

Ähnlich wie The Dual write problem (20)

Event Driven Architecture (Integration Tech Event 2019)
Event Driven Architecture (Integration Tech Event 2019)Event Driven Architecture (Integration Tech Event 2019)
Event Driven Architecture (Integration Tech Event 2019)
 
Should you be using an event driven architecture - IDA IT (short version)
Should you be using an event driven architecture - IDA IT (short version)Should you be using an event driven architecture - IDA IT (short version)
Should you be using an event driven architecture - IDA IT (short version)
 
SOA, Microservices and Event Driven Architecture
SOA, Microservices and Event Driven ArchitectureSOA, Microservices and Event Driven Architecture
SOA, Microservices and Event Driven Architecture
 
What 5 years of implementation microservices has taught me
What 5 years of implementation microservices has taught meWhat 5 years of implementation microservices has taught me
What 5 years of implementation microservices has taught me
 
Long running processes in DDD
Long running processes in DDDLong running processes in DDD
Long running processes in DDD
 
Inventing the future Business Programming Language
Inventing the future  Business Programming LanguageInventing the future  Business Programming Language
Inventing the future Business Programming Language
 
The Art of The Event Streaming Application: Streams, Stream Processors and Sc...
The Art of The Event Streaming Application: Streams, Stream Processors and Sc...The Art of The Event Streaming Application: Streams, Stream Processors and Sc...
The Art of The Event Streaming Application: Streams, Stream Processors and Sc...
 
Kakfa summit london 2019 - the art of the event-streaming app
Kakfa summit london 2019 - the art of the event-streaming appKakfa summit london 2019 - the art of the event-streaming app
Kakfa summit london 2019 - the art of the event-streaming app
 
Sapbasic
SapbasicSapbasic
Sapbasic
 
Sap overview posted by Parikshit Sanghavi
Sap overview posted by Parikshit SanghaviSap overview posted by Parikshit Sanghavi
Sap overview posted by Parikshit Sanghavi
 
Should you be using an event driven architecture?
Should you be using an event driven architecture?Should you be using an event driven architecture?
Should you be using an event driven architecture?
 
Event Driven Architectures
Event Driven ArchitecturesEvent Driven Architectures
Event Driven Architectures
 
CQRS + Event Sourcing
CQRS + Event SourcingCQRS + Event Sourcing
CQRS + Event Sourcing
 
Don't call us - we'll push - on cross tier push architecture (NLJUG JFall 201...
Don't call us - we'll push - on cross tier push architecture (NLJUG JFall 201...Don't call us - we'll push - on cross tier push architecture (NLJUG JFall 201...
Don't call us - we'll push - on cross tier push architecture (NLJUG JFall 201...
 
Complex Event Processing: What?, Why?, How?
Complex Event Processing: What?, Why?, How?Complex Event Processing: What?, Why?, How?
Complex Event Processing: What?, Why?, How?
 
Data Microservices with Spring Cloud
Data Microservices with Spring CloudData Microservices with Spring Cloud
Data Microservices with Spring Cloud
 
Event Driven Architectures - Phoenix Java Users Group 2013
Event Driven Architectures - Phoenix Java Users Group 2013Event Driven Architectures - Phoenix Java Users Group 2013
Event Driven Architectures - Phoenix Java Users Group 2013
 
SBJUG - Building Beautiful Batch Jobs
SBJUG - Building Beautiful Batch JobsSBJUG - Building Beautiful Batch Jobs
SBJUG - Building Beautiful Batch Jobs
 
Microservices in the Apache Kafka Ecosystem
Microservices in the Apache Kafka EcosystemMicroservices in the Apache Kafka Ecosystem
Microservices in the Apache Kafka Ecosystem
 
Event Sourcing, Stream Processing and Serverless (Benjamin Stopford, Confluen...
Event Sourcing, Stream Processing and Serverless (Benjamin Stopford, Confluen...Event Sourcing, Stream Processing and Serverless (Benjamin Stopford, Confluen...
Event Sourcing, Stream Processing and Serverless (Benjamin Stopford, Confluen...
 

Kürzlich hochgeladen

TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
FIDO Alliance
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 
Hyatt driving innovation and exceptional customer experiences with FIDO passw...
Hyatt driving innovation and exceptional customer experiences with FIDO passw...Hyatt driving innovation and exceptional customer experiences with FIDO passw...
Hyatt driving innovation and exceptional customer experiences with FIDO passw...
FIDO Alliance
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 

Kürzlich hochgeladen (20)

Introduction to FIDO Authentication and Passkeys.pptx
Introduction to FIDO Authentication and Passkeys.pptxIntroduction to FIDO Authentication and Passkeys.pptx
Introduction to FIDO Authentication and Passkeys.pptx
 
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
 
How to Check CNIC Information Online with Pakdata cf
How to Check CNIC Information Online with Pakdata cfHow to Check CNIC Information Online with Pakdata cf
How to Check CNIC Information Online with Pakdata cf
 
JohnPollard-hybrid-app-RailsConf2024.pptx
JohnPollard-hybrid-app-RailsConf2024.pptxJohnPollard-hybrid-app-RailsConf2024.pptx
JohnPollard-hybrid-app-RailsConf2024.pptx
 
Intro to Passkeys and the State of Passwordless.pptx
Intro to Passkeys and the State of Passwordless.pptxIntro to Passkeys and the State of Passwordless.pptx
Intro to Passkeys and the State of Passwordless.pptx
 
Portal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russePortal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russe
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
 
Navigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern EnterpriseNavigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern Enterprise
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Introduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDMIntroduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDM
 
Hyatt driving innovation and exceptional customer experiences with FIDO passw...
Hyatt driving innovation and exceptional customer experiences with FIDO passw...Hyatt driving innovation and exceptional customer experiences with FIDO passw...
Hyatt driving innovation and exceptional customer experiences with FIDO passw...
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
Design and Development of a Provenance Capture Platform for Data Science
Design and Development of a Provenance Capture Platform for Data ScienceDesign and Development of a Provenance Capture Platform for Data Science
Design and Development of a Provenance Capture Platform for Data Science
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
2024 May Patch Tuesday
2024 May Patch Tuesday2024 May Patch Tuesday
2024 May Patch Tuesday
 
Simplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptxSimplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptx
 
Working together SRE & Platform Engineering
Working together SRE & Platform EngineeringWorking together SRE & Platform Engineering
Working together SRE & Platform Engineering
 
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
 

The Dual write problem

  • 1. The dual write problem Jeppe Cramon - @jeppec Cloud Create ApS
  • 2. @jeppec Essential complexity of 2 way integration Component Warehouse Component Order Component Billing UI Send- Invoice Save-Order Order:Save-Order()è call Warehouse:Reserve-Items() call Billing:Send-Invoice() commit() Reserve- Items Local transaction between the 3 Components
  • 4. @jeppec Accidental complexity from distributed service integration Warehouse Service Order Service Billing Service UI Send- Invoice Save-Order Reserve- Items Local transaction between 2 local “Services” DUAL WRITE using Remote call
  • 5. @jeppec Microservices turns a local functional call into a distributed problem
  • 6. @jeppec With cross SERVICE integration we’re bound by the laws of distributed computing
  • 7. @jeppec The 11 Fallacies of Distributed Computing These fallacies are assumptions architects, designers and developers of distributed systems are likely to make. The fallacies will be proven wrong in the long run - resulting in all sorts of troubles and pains for the solution and architects who made the assumptions. 1. The network is reliable. 2. Latency is zero. 3. Bandwidth is infinite. 4. The network is secure. 5. Topology doesn't change. 6. There is one administrator. 7. Transport cost is zero. 8. The network is homogeneous. 9. The System is atomic/monolithic 10. The System is Finished 11. Business logic can and should be centralized See https://arnon.me/wp-content/uploads/Files/fallacies.pdf
  • 8. @jeppec Order:Save-Order()è call Warehouse:Reserve-Items() call Billing:Send-Invoice() if (Billing:Call-Failed:Too-Busy?) Wait-A-While() call Billing:Send-Invoice() if (Billing:Call-Failed:Too-Busy?) Wait-A-Little-While-Longer() call Billing:Send-Invoice() if (Billing:Call-Failed:IO-Error?) Save-We-Need-Check-If-Call-Billing-Succeded-After-All AND We-Need-To-Retry call Order:Save-Order and call Warehouse:Reserve-Items AND Tell-Customer-That-This-Operation-Perhaps-Went-Well if (Billing:Call-Went-Well?) commit() Accidental complexity from distributed service integration Warehouse Service Order Service Billing Service UI Send- Invoice Save-Order Reserve- Items Local transaction between 2 Components
  • 10. @jeppec What’s the challenge using state mutating Request/Response calls between distributed components?
  • 11. @jeppec Synchronous Request/Response lowers our tolerance for faults • When you get an IO error • When servers crash or restarts • When databases are down • When deadlocks occurs in our databases • Do you retry? With synchronous Request/Response we can loose business data if there’s no automatic retry mechanism. Also if the operation we retry isn’t idempotent* we risk having the side effect multiple times! Client Server Processing The same message can be processed more than once *Idempotence describes the quality of an operation in which result and state does not change if the operation is performed more than 1 time Request Processing Duplicated Request Duplicated Response Response
  • 14. @jeppec Sales system Sales Update customer status (e.g. Gold customer) Bookkeeping Deliver goods Delivery system Deliveries Customer/ CRM system Customers SAP Bookkeeping Complete Purchase Transaction Coordinator Transactional Resource Request-to-Prepare Commit Prepared Done Prepare Phase Commit Phase 2 Phase Commit
  • 15. @jeppec What’s wrong with distributed transactions? • Transactions lock resources while active • Services are autonomous • Can’t be expected to finish within a certain time interval • Locking keeps other transactions from completing their job • Locking doesn’t scale • X Phase Commit is fragile by design
  • 16. How to handle the dual write problem?
  • 17. @jeppec Dual write problem Order Service Billing Service Client Send- Invoice Save-Order Mutating Remote call Expected transaction boundary No simple solution (except a classic monolith) • Local-commit-then-publish • If the app crashes after local- commit then the publish operation isn’t performed • Publish-then-local-commit • If the local-commit fails then we’ve already formed the publish operation • Billing Service may receive published message BEFORE the Order Service has committed its transaction
  • 19. @jeppec Dual write problem Orchestration / Process Manager / Saga* Order Service Billing Service Client Send- Invoice Transaction boundary Transaction boundary Save-Order • Requires only local transactions • Single coordinator • Increases eventual consistency • Requires idempotent operations due to retries • Requires compensations / rollback • Tighter coupling as the orchestrator instructs other services about what to do * This isn’t the real Saga pattern
  • 21. @jeppec Dual write problem Choreography public class OrderService { public void handle(AcceptOrder cmd) { orderRepository.add(new Order(cmd.orderId, cmd.orderLines)); eventTopic.publish(new OrderAccepted(cmd.orderId, cmd.orderLines)); } } public class BillingService { public void handle(OrderAccepted cmd) { invoiceRepository.add(new Invoice(cmd.orderId, cmd.orderLines)); emailGateway.send(new OrderAcceptedEmail(cmd.orderId, cmd.orderLines)); } }
  • 22. @jeppec Dual write problem Choreography Order Service Billing Service Client Send- Invoice Save-Order Transaction boundary Transaction boundary • No Single coordinator, but global system state and coordination logic is scattered across all participants • Requires only local transactions • Increases eventual consistency • Requires idempotent operations due to retries • Looser coupling (if events are used) • Suffers from a Dual Write problem it self Events/Commands Queue
  • 24. @jeppec Dual write problem Choreography with Outbox pattern Order Service Billing Service Client Send- Invoice Save-Order Transaction boundary Transaction boundary • You can either use a Change Data Capture (CDC) approach e.g. using a database Write Ahead Log (WAL) • Risk of CRUD events! • Or a manual OutBox pattern where Save-Order writes data and the event/command into the OutBox within the same database. Typically requires polling and coordination + recovery options in a multi cluster setup Events/Commands Change Data Capture / Polling Async Push or Poll
  • 25. @jeppec Saga pattern A failure management pattern
  • 26. @jeppec Trip reservation as a Saga Start Rent Car Book Hotel Reserve Flight Cancel Hotel Cancel Car Book Car Rental Hotel Rental WorkQueue: - Queue: “Rent Car” - Queue: “Book Hotel” - Queue: “Reserve Flight” Routing slip Flight Booking
  • 27. @jeppec Trip reservation as a Saga Start Rent Car Book Hotel Reserve Flight Cancel Hotel Cancel Car Book Book Car Rental Hotel Rental WorkQueue: - Queue: “Book Hotel” - Queue : “Reserve Flight” WorkStack: - ReservationId: ABC123 Cancellation: “Cancel Car” Routing slip Flight Booking
  • 28. @jeppec Trip reservation as a Saga Start Rent Car Book Hotel Reserve Flight Cancel Hotel Cancel Car Book Book Car Rental Hotel Rental WorkQueue: - Queue : “Reserve Flight” WorkStack: - Hotel ReservationId: XYZ Cancellation: “Cancel Hotel” - Car ReservationId: ABC123 Cancellation: “Cancel Car” Routing slip Book Flight Booking
  • 29. @jeppec Trip reservation as a Saga Start Rent Car Book Hotel Reserve Flight Cancel Hotel Cancel Car Book Book Book Car Rental Hotel Rental WorkStack: - Hotel ReservationId: XYZ Cancellation: “Cancel Hotel” - Car ReservationId: ABC123 Cancellation: “Cancel Car” Routing slip Flight Booking
  • 30. @jeppec Trip reservation as a Saga Start Rent Car Book Hotel Reserve Flight Cancel Hotel Cancel Car Book Book Book Car Rental Hotel Rental Cancel WorkStack: - Hotel ReservationId: XYZ Cancellation: “Cancel Hotel” - Car ReservationId: ABC123 Cancellation: “Cancel Car” Routing slip Flight Booking
  • 31. @jeppec Trip reservation as a Saga Start Rent Car Book Hotel Reserve Flight Cancel Hotel Cancel Car Book Book Book Car Rental Hotel Rental Cancel Cancel WorkStack: - Car ReservationId: ABC123 Cancellation: “Cancel Car” Routing slip Flight Booking
  • 32. @jeppec Trip reservation as a Saga Start Rent Car Book Hotel Reserve Flight Cancel Hotel Cancel Car Book Book Book Car Rental Hotel Rental Cancel Cancel Cancelled Flight Booking
  • 34. @jeppec Dual write problem Choreography with Event Sourcing Order Service Billing Service Client Send- Invoice Save-Order Transaction boundary Transaction boundary Event Store Async Push or Pull • Requires only local transactions • Natively event driven: The Service consumes and produces events • Proven Audit Trail • Flexible and adaptable • Captures business intentions as events • Requires idempotent operations
  • 35. @jeppec Event Sourcing Business-Objects/Aggregates track their own Domain Events and derive their state from said Events OrderCreated ProductAdded ProductAdded ProductRemoved ProductAdded OrderAccepted Time 07:39 Time 07:40 Time 07:41 Time 07:45 Time 07:46 Time 07:50
  • 36. @jeppec Event Replaying Type Aggregate Identifier GlobalOrder Event Order Timestamp Event Identifier EventType SerializedEvent Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>… Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>… Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>… Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>… Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>… Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>… Order
  • 37. @jeppec Event Replaying Type Aggregate Identifier GlobalOrder Event Order Timestamp Event Identifier EventType SerializedEvent Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>… Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>… Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>… Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>… Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>… Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>… Order Orderline
  • 38. @jeppec Event Replaying Type Aggregate Identifier GlobalOrder Event Order Timestamp Event Identifier EventType SerializedEvent Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>… Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>… Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>… Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>… Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>… Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>… Order Orderline Orderline
  • 39. @jeppec Event Replaying Type Aggregate Identifier GlobalOrder Event Order Timestamp Event Identifier EventType SerializedEvent Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>… Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>… Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>… Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>… Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>… Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>… Order Orderline
  • 40. @jeppec Event Replaying Type Aggregate Identifier GlobalOrder Event Order Timestamp Event Identifier EventType SerializedEvent Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>… Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>… Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>… Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>… Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>… Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>… Order Orderline Orderline
  • 41. @jeppec Event Replaying Type Aggregate Identifier GlobalOrder Event Order Timestamp Event Identifier EventType SerializedEvent Order 14237 100 0 2014-01-06 7:39 {Guid-1} OrderCreated <serialized event>… Order 14237 101 1 2014-01-06 7:40 {Guid-2} ProductAdded <serialized event>… Order 14237 102 2 2014-01-06 7:41 {Guid-3} ProductAdded <serialized event>… Order 14237 103 3 2014-01-06 7:45 {Guid-4} ProductRemoved <serialized event>… Order 14237 104 4 2014-01-06 7:46 {Guid-5} ProductAdded <serialized event>… Order 14237 105 5 2014-01-06 7:50 {Guid-6} OrderAccepted <serialized event>… Order Accepted: true Orderline Orderline
  • 42. @jeppec Client handled subscriptions RSocketEvent StreamSubscription Local storage EventStore RSocketEvent StreamSubscription Local storage EventStreamSubscription Message EventStreamSubscription Message EventStoreEventStreamPublisher EventStoreEventStreamPublisher Event Event Event Event Supports Single Instance Subscriber, which ensures that only one instance of Subscriber B has an active subscription. Other instances of the same subscriber are hot-standby <<Topic Subscriber>> Customer_Service:Some_Ac:OrderEvents <<Topic Publisher>> Sales_Service:OrderEvents RSocketServer tcp://subscribe-event-stream A B Subscriber B RSocket Request/Stream Event-Stream Subscriber A RSocket Request/Stream Event-Stream Flux<PersistedEvent> eventStream(long fromInclusiveGlobalOrder, Option<String> subscriptionId)
  • 43. @jeppec ReactiveBus Pub/Sub JdbcEventStores eventStores = …. var customerAggregateType = AggregateType.from("Customer", CustomerId.class); var customerEventStore = eventStores.getEventStoreFor(customerAggregateType); var streamName = EventStreamName.from("CustomersStream"); reactiveBus.addEventStreamPublisher(new EventStoreEventStreamPublisher(streamName, customerEventStore, unitOfWorkFactory)); Publisher: reactiveBus.subscribeToEventStream( SubscriberId.from(”SalesService->CustomerEvents"), EventStreamName.from("CustomersStream"), EventStreamSubscriptionParameters.parameters(GlobalOrderSubscribeFromToken.fromStartOfStream())) .doOnNext(payload -> { System.out.println(”Received: " + e.payload.getClass() + "@" + e.globalOrder); }).subscribe(); Subscriber:
  • 44. @jeppec Pub/Sub – Subscriber event handling reactiveBus.subscribeToEventStream(SubscriberId.from(”SalesService->OrderEvents"), EventStreamName.from(”OrdersStream"), EventStreamSubscriptionParameters.parameters(GlobalOrderSubscribeFromToken.fromStartOfStream())) .doOnNext(payload -> { if (event instanceof OrderAdded e) { .. = e.orderNumber; } else if (event instanceof ProductAddedToOrder e) { … = e.productId; } else if (event instanceof OrderAccepted) { …accepted = true; } ).subscribe();
  • 45. @jeppec Pub/Sub – Subscriber event handling @EventHandler private void handle(OrderAdded e, EventMessage message) { …. } @EventHandler private void handle(ProductAddedToOrder e) { .… } @EventHandler private void handle(OrderAccepted e) { …. }