SlideShare a Scribd company logo
How to Send a Receipt
Topics in Concurrency and Distributed Systems | @pascallouisperez
The Situation
‣ It’s Black Friday on an e-commerce site.
‣ When a customer makes a purchase,
‣ We send said customer a purchase receipt.
Not That Simple

Sending a purchase receipt via email after a purchase is a
concrete product requirement which takes us down into
distributed systems design.
Generalizable Problematic

Many problems we encounter can be rephrased as the
canonical ‘sending a receipt’ problem. And hence,
understanding it well is a powerful tool.
How to Send a Receipt
Topics in Concurrency and Distributed
Systems
Bad Solution purchase_handler(…) {
err = charge_cc(cc, order.amount);
if err != nil { return err }
err = mark_as_paid(db, order);
if err != nil { return err }
err = send_receipt(order.customer);
if err != nil { return err }
err = mark_receipt_sent(db, order);
if err != nil { return err }
return nil
}
Bad Solution
Mo Money Mo Problems
‣ Not easy to charge a credit
card.
‣ Auth / Capture model? Single
Charge?
‣ Gateway Idempotency?
‣ Handling of timeouts? TCP
interruptions?
‣ Database crash?
purchase_handler(…) {
err = charge_cc(cc, order.amount);
if err != nil { return err }
err = mark_as_paid(db, order);
if err != nil { return err }
err = send_receipt(order.customer);
if err != nil { return err }
err = mark_receipt_sent(db, order);
if err != nil { return err }
return nil
}
‣ Failure to mark as paid leads
to dangling payment without a
confirmed order!
‣ Or a paid order without an
email receipt.
‣ Retrying the handler would
double charge.
‣ (Many other bad things.)
Bad Solution
Some of the Failure Modes
purchase_handler(…) {
err = charge_cc(cc, order.amount);
if err != nil { return err }
err = mark_as_paid(db, order);
if err != nil { return err }
err = send_receipt(order.customer);
if err != nil { return err }
err = mark_receipt_sent(db, order);
if err != nil { return err }
return nil
}
(Shorthand) purchase_handler(…) {
err = charge_cc(cc, order.amount); on err return
err = mark_as_paid(db, order); on err return
err = send_receipt(order.customer); on err return
err = mark_receipt_sent(db, order); on err return
return nil
}
Let’s Fix Charging
Charging
Single Call Model
‣ Creating a charge debits the
customer’s credit card.
‣ Charges are uniquely
identified, and the payment
provider guarantees
idempotency.
‣ Makes it easy to charge,
simply retry until success.
However, requires care to
avoid dangling payments.
Charging
Auth / Capture Model
‣ Authorize creates a pending
transaction, and reserves
funds. Auth are uniquely
identified… And providers
expose pseudo idempotency.
‣ Capture confirms the
transactions.
‣ Makes it hard to charge.
However, avoids dangling
payments in failure cases.
(Let’s ignore this model for now.)
Charging
Let’s Fix It
purchase_handler(…) {
err = run_transaction(db, func(tx) error {
order.cc_charge_uuid = new_uuid(…);
save(order);
}); on err return
charge, err = stripe.create_charge(order); on err retur
err = run_transaction(db, func(tx) error {
order.cc_charge = charge;
save(order);
}); on err return
err = send_receipt(order.customer); on err return
err = mark_receipt_sent(db, order); on err return
return nil
}
Charging
The Pattern
Prepare, Do, Propagate
‣ Tell them what you are going
to tell them;
‣ Tell them;
‣ Then tell them what you told
them.
purchase_handler(…) {
err = run_transaction(db, func(tx) error {
order.cc_charge_uuid = new_uuid(…);
save(order);
}); on err return
charge, err = stripe.create_charge(order); on err retur
err = run_transaction(db, func(tx) error {
order.cc_charge = charge;
save(order);
}); on err return
err = send_receipt(order.customer); on err return
err = mark_receipt_sent(db, order); on err return
return nil
}
Charging
Let’s Fix It
purchase_handler(…) {
err = run_transaction(db, func(tx) error {
order.cc_charge_uuid = new_uuid(…);
save(order);
}); on err return
charge, err = stripe.create_charge(order); on err retur
err = run_transaction(db, func(tx) error {
order.cc_charge = charge;
save(order);
}); on err return
err = send_receipt(order.customer); on err return
err = mark_receipt_sent(db, order); on err return
return nil
}
‣ No ‘cc_charge_uuid’: failed
early, can retry.
‣ No ‘cc_charge’: retry
payment, Stripe guarantees
idempotency.
‣ Did we send the receipt?
How are Emails Sent
Anyways?
$ telnet smtp.thatsanexample.com 25
HELO thatsanexample.com
MAIL from: <pascal@thatsanexample.com>
RCPT to: <pascallouisperez@gmail.com>
DATA
From: pascal@thatsanexample.com
To: pascallouisperez@gmail.com
Subject: SMTP is low level and simple
This is an example.
.
QUIT
Sending Emails
Quick SMTP Primer
‣ RFC2821 for SMTP protocol;
and
‣ RFC2822 for message format.
Sending Emails
Avoiding Duplicates
‣ Great. How do you avoid
duplicate delivery of
messages?
Sending Emails
Avoiding Duplicates
‣ Only four mentions of
‘duplicate’ in RFC2821:
1. Avoiding duplicates for
mailing lists, not relevant;
2. Specification of a ‘text line’
which has a ‘dot duplicate’,
not relevant;
3. Page 62 represented here,
simplistic recommendation;
4. Reference 28 which points to
an 1988 article “Duplicate
messages and SMTP”, which
became RFC1047.
Sending Emails
Avoiding Duplicates
‣ Oh, and RFC1047 is not useful.
Good Description of the Problem
“If the communications link fails during this synchronization gap,
then the message has been duplicated. Both mailers have active
copies of the message that they will try to deliver.”
Simplistic Suggestion
“The best way to avoid the synchronization problem is to
minimize the length of the synchronization gap. In other words,
receiving mailers should acknowledge the final dot as soon as
possible and do more complex processing of the message later.”
Sending Emails
Avoiding Duplicates
‣ RFC2821 replaced RFC821 in
2001;
‣ In particular, it saw the
introduction of a ‘message-id’.
‣ One can hope most clients
correctly populate this field
(just kidding); and
‣ One can hope most SMTP
relays use this field to de-
duplicate messages.
Sending Emails
Email Delivery Services
‣ Your friendly REST speaking
email delivery services!
‣ Except…
Sending Emails
Email Delivery Services
‣ SendGrid doesn’t expose the
message-id.
Sending Emails
Email Delivery Services
‣ MailChimp doesn’t expose the
message-id;
‣ Though it allows custom
headers to be passed, hence
likely making it possible to
pass a message-id.
Sending Emails
Email Delivery Services
‣ SailThru has a ‘send_id’, yeah!



(Note: Haven’t tested if this is used
to populate the message-id. Seems
to imply as much.)
‣ Impossible to avoid duplicates.
‣ Hence, we have
‣ No delivery; or
‣ At least once delivery.
(It sucks. SMS is worse. So is the post office frankly.)
Sending Emails
The Conclusion
Let’s Fix It
Fix It, How?
Back to the Subject at Hand
purchase_handler(…) {
err = run_transaction(db, func(tx) error {
order.cc_charge_uuid = new_uuid(…);
save(order);
}); on err return
charge, err = stripe.create_charge(order); on err retur
err = run_transaction(db, func(tx) error {
order.cc_charge = charge;
save(order);
}); on err return
err = send_receipt(order.customer); on err return
err = mark_receipt_sent(db, order); on err return
return nil
}
‣ If we are unable to record the
charge, don’t send the receipt.
‣ If we are able to record the
charge, we must guarantee
that the receipt will be
delivered.
‣ Given what we learned about
email delivery, our choice is
between no delivery, or at
least once delivery.
Fix It, How?
Back to the Subject at Hand
purchase_handler(…) {
err = run_transaction(db, func(tx) error {
order.cc_charge_uuid = new_uuid(…);
order.receipt_uuid = new_uuid(…);
save(order);
}); on err return
charge, err = stripe.create_charge(order); on err retur
err = run_transaction(db, func(tx) error {
order.cc_charge = charge;
order.receipt_state = PENDING;
save(order);
}); on err return
return nil
}
Prepare
‣ Record intent to send receipt.
‣ The intent includes the
identifier of the receipt: it is
this specific email we intend
to send.
Do
‣ Charge
Propagate
‣ In the background, process
the email queue and retry
until confirmation of delivery.
Generalizing
Inventoried Item (e.g. a seated ticket to a show)
Prepare: check availability, and ‘soft reserve’ to avoid
double bookings.
Propagate: confirm reservation.
Activating Gift Cards
Prepare: soft creation of the card.
Propagate: activate and fund.
Coupons

Prepare: verify the validity, and ’soft redeem’ to avoid
double use.
Propagate: actually redeem it.
Similar Problematics
Hard Across µServices Boundaries
The Order as Distributed Transaction Coordinator
Distributed transaction processing, and two-phase commit.
Order ManagementOrder Management
Purchase
The Order as Distributed Transaction Coordinator
Distributed transaction processing, and two-phase commit.
Order ManagementOrder Management
Prepare: “Soft Reserve” Prepare: “Create Gift Card”
“The Commit Request Phase”
The Order as Distributed Transaction Coordinator
Distributed transaction processing, and two-phase commit.
Order ManagementOrder Management
Do: “Charge”
“The Commit Phase”
The Order as Distributed Transaction Coordinator
Distributed transaction processing, and two-phase commit.
Order ManagementOrder Management
Propagate: “Confirm” Propagate: “Activate”
Propagate: “Send”
“The Success Phase”
The Order as Distributed Transaction Coordinator
Distributed transaction processing, and two-phase commit.
Order ManagementOrder Management
Propagate: “Cancel” Propagate: “Dispose”
“The Failure Phase”
Propagate: “Reconcile to
Cancel (or Refund) Dangling
Payment”
The Order as Distributed Transaction Coordinator
Distributed transaction processing, and two-phase commit.
Order ManagementOrder Management
Propagate: “Expiry” Propagate: “Garbage Collect”
“The (Unofficial) Cleanup Post Failure Phase”
In Closing
‣ RTFM (“Read The Fucking Manual”)
‣ It's turtles all the way down, follow the thread
‣ Correctness at app level requires understand of low level details
‣ Distributed Systems are Hard
‣ No need to re-invent the wheel, most of CS was invented in the 1970s, and we’re just
repurposing known techniques
‣ Design Early, Design Often
‣ You can’t iterate your way to correctness
‣ Think through your systems early, write it down, rinse, repeat

More Related Content

Similar to How to Send a Receipt, Topics in Concurrency and Distributed Systems

Invoicing Gem - Sales & Payments In Your App
Invoicing Gem - Sales & Payments In Your AppInvoicing Gem - Sales & Payments In Your App
Invoicing Gem - Sales & Payments In Your App
Martin Kleppmann
 
Integration of payment gateways using Paypal account
Integration of payment gateways using Paypal account Integration of payment gateways using Paypal account
Integration of payment gateways using Paypal account
Phenom People
 
Nosqloakland 20091102.1
Nosqloakland 20091102.1Nosqloakland 20091102.1
Nosqloakland 20091102.1
NorthScale
 
Banks offer various types of accounts, such as savings, checking, cer.pdf
Banks offer various types of accounts, such as savings, checking, cer.pdfBanks offer various types of accounts, such as savings, checking, cer.pdf
Banks offer various types of accounts, such as savings, checking, cer.pdf
rajeshjain2109
 
Barcelona Developers Conference 2011
Barcelona Developers Conference 2011Barcelona Developers Conference 2011
Barcelona Developers Conference 2011PayPal
 
JCon 2017: Let your microservices flow
JCon 2017: Let your microservices flowJCon 2017: Let your microservices flow
JCon 2017: Let your microservices flow
Bernd Ruecker
 
Long running processes in DDD
Long running processes in DDDLong running processes in DDD
Long running processes in DDD
Bernd Ruecker
 
Heropay_Ultimate-guide-booklet_Final
Heropay_Ultimate-guide-booklet_FinalHeropay_Ultimate-guide-booklet_Final
Heropay_Ultimate-guide-booklet_FinalAdam J. Rebolloso
 
Paymill vs Stripe
Paymill vs StripePaymill vs Stripe
Paymill vs Stripe
betabeers
 
2 ivan pashko - fake it 'til you make it
2   ivan pashko - fake it 'til you make it2   ivan pashko - fake it 'til you make it
2 ivan pashko - fake it 'til you make it
Ievgenii Katsan
 
Beyond Microservices: Streams, State and Scalability
Beyond Microservices: Streams, State and ScalabilityBeyond Microservices: Streams, State and Scalability
Beyond Microservices: Streams, State and Scalability
confluent
 
Banks offer various types of accounts, such as savings, checking, cer.pdf
 Banks offer various types of accounts, such as savings, checking, cer.pdf Banks offer various types of accounts, such as savings, checking, cer.pdf
Banks offer various types of accounts, such as savings, checking, cer.pdf
akbsingh1313
 
Order to cash
Order to cashOrder to cash
Order to cash
Chetan Khanzode
 
Monetizing your apps with PayPal API:s
Monetizing your apps with PayPal API:sMonetizing your apps with PayPal API:s
Monetizing your apps with PayPal API:s
Disruptive Code
 
CQRS and Event Sourcing
CQRS and Event SourcingCQRS and Event Sourcing
CQRS and Event Sourcing
Kentucky JavaScript Users Group
 
Fusion recivables
Fusion recivablesFusion recivables
Fusion recivables
kotesh amburi
 
Consignment process
Consignment processConsignment process
Consignment process
Srinivasulu Algaskhanpet
 
SQL Server 2008 Portfolio
SQL Server 2008 PortfolioSQL Server 2008 Portfolio
SQL Server 2008 Portfolio
anthonyfeliciano
 
Acceptance Test Driven Development
Acceptance Test Driven DevelopmentAcceptance Test Driven Development
Acceptance Test Driven Development
John Ferguson Smart Limited
 
Serverless Functions and Vue.js
Serverless Functions and Vue.jsServerless Functions and Vue.js
Serverless Functions and Vue.js
Sarah Drasner
 

Similar to How to Send a Receipt, Topics in Concurrency and Distributed Systems (20)

Invoicing Gem - Sales & Payments In Your App
Invoicing Gem - Sales & Payments In Your AppInvoicing Gem - Sales & Payments In Your App
Invoicing Gem - Sales & Payments In Your App
 
Integration of payment gateways using Paypal account
Integration of payment gateways using Paypal account Integration of payment gateways using Paypal account
Integration of payment gateways using Paypal account
 
Nosqloakland 20091102.1
Nosqloakland 20091102.1Nosqloakland 20091102.1
Nosqloakland 20091102.1
 
Banks offer various types of accounts, such as savings, checking, cer.pdf
Banks offer various types of accounts, such as savings, checking, cer.pdfBanks offer various types of accounts, such as savings, checking, cer.pdf
Banks offer various types of accounts, such as savings, checking, cer.pdf
 
Barcelona Developers Conference 2011
Barcelona Developers Conference 2011Barcelona Developers Conference 2011
Barcelona Developers Conference 2011
 
JCon 2017: Let your microservices flow
JCon 2017: Let your microservices flowJCon 2017: Let your microservices flow
JCon 2017: Let your microservices flow
 
Long running processes in DDD
Long running processes in DDDLong running processes in DDD
Long running processes in DDD
 
Heropay_Ultimate-guide-booklet_Final
Heropay_Ultimate-guide-booklet_FinalHeropay_Ultimate-guide-booklet_Final
Heropay_Ultimate-guide-booklet_Final
 
Paymill vs Stripe
Paymill vs StripePaymill vs Stripe
Paymill vs Stripe
 
2 ivan pashko - fake it 'til you make it
2   ivan pashko - fake it 'til you make it2   ivan pashko - fake it 'til you make it
2 ivan pashko - fake it 'til you make it
 
Beyond Microservices: Streams, State and Scalability
Beyond Microservices: Streams, State and ScalabilityBeyond Microservices: Streams, State and Scalability
Beyond Microservices: Streams, State and Scalability
 
Banks offer various types of accounts, such as savings, checking, cer.pdf
 Banks offer various types of accounts, such as savings, checking, cer.pdf Banks offer various types of accounts, such as savings, checking, cer.pdf
Banks offer various types of accounts, such as savings, checking, cer.pdf
 
Order to cash
Order to cashOrder to cash
Order to cash
 
Monetizing your apps with PayPal API:s
Monetizing your apps with PayPal API:sMonetizing your apps with PayPal API:s
Monetizing your apps with PayPal API:s
 
CQRS and Event Sourcing
CQRS and Event SourcingCQRS and Event Sourcing
CQRS and Event Sourcing
 
Fusion recivables
Fusion recivablesFusion recivables
Fusion recivables
 
Consignment process
Consignment processConsignment process
Consignment process
 
SQL Server 2008 Portfolio
SQL Server 2008 PortfolioSQL Server 2008 Portfolio
SQL Server 2008 Portfolio
 
Acceptance Test Driven Development
Acceptance Test Driven DevelopmentAcceptance Test Driven Development
Acceptance Test Driven Development
 
Serverless Functions and Vue.js
Serverless Functions and Vue.jsServerless Functions and Vue.js
Serverless Functions and Vue.js
 

More from Pascal-Louis Perez

Fuchsia RFCs
Fuchsia RFCsFuchsia RFCs
Fuchsia RFCs
Pascal-Louis Perez
 
Products’ Love Story with Biz
Products’ Love Story with BizProducts’ Love Story with Biz
Products’ Love Story with Biz
Pascal-Louis Perez
 
Corporate Finance Primer
Corporate Finance PrimerCorporate Finance Primer
Corporate Finance Primer
Pascal-Louis Perez
 
SLL Conf - Continuous Deployment
SLL Conf - Continuous DeploymentSLL Conf - Continuous Deployment
SLL Conf - Continuous DeploymentPascal-Louis Perez
 
Alchemist Startup Primer - Lean Development Practices
Alchemist Startup Primer - Lean Development PracticesAlchemist Startup Primer - Lean Development Practices
Alchemist Startup Primer - Lean Development PracticesPascal-Louis Perez
 
Database compatibility
Database compatibilityDatabase compatibility
Database compatibility
Pascal-Louis Perez
 
Applying Compiler Techniques to Iterate At Blazing Speed
Applying Compiler Techniques to Iterate At Blazing SpeedApplying Compiler Techniques to Iterate At Blazing Speed
Applying Compiler Techniques to Iterate At Blazing Speed
Pascal-Louis Perez
 
Iterate Like a Whirling Dervish
Iterate Like a Whirling DervishIterate Like a Whirling Dervish
Iterate Like a Whirling Dervish
Pascal-Louis Perez
 
Extreme Testing at kaChing
Extreme Testing at kaChingExtreme Testing at kaChing
Extreme Testing at kaChing
Pascal-Louis Perez
 
Type Checking JavaScript
Type Checking JavaScriptType Checking JavaScript
Type Checking JavaScript
Pascal-Louis Perez
 
Xignite's Dedicate kaChing Api
Xignite's Dedicate kaChing ApiXignite's Dedicate kaChing Api
Xignite's Dedicate kaChing ApiPascal-Louis Perez
 
Add (Syntactic) Sugar To Your Java
Add (Syntactic) Sugar To Your JavaAdd (Syntactic) Sugar To Your Java
Add (Syntactic) Sugar To Your Java
Pascal-Louis Perez
 
kaChing's API garage event
kaChing's API garage eventkaChing's API garage event
kaChing's API garage event
Pascal-Louis Perez
 

More from Pascal-Louis Perez (13)

Fuchsia RFCs
Fuchsia RFCsFuchsia RFCs
Fuchsia RFCs
 
Products’ Love Story with Biz
Products’ Love Story with BizProducts’ Love Story with Biz
Products’ Love Story with Biz
 
Corporate Finance Primer
Corporate Finance PrimerCorporate Finance Primer
Corporate Finance Primer
 
SLL Conf - Continuous Deployment
SLL Conf - Continuous DeploymentSLL Conf - Continuous Deployment
SLL Conf - Continuous Deployment
 
Alchemist Startup Primer - Lean Development Practices
Alchemist Startup Primer - Lean Development PracticesAlchemist Startup Primer - Lean Development Practices
Alchemist Startup Primer - Lean Development Practices
 
Database compatibility
Database compatibilityDatabase compatibility
Database compatibility
 
Applying Compiler Techniques to Iterate At Blazing Speed
Applying Compiler Techniques to Iterate At Blazing SpeedApplying Compiler Techniques to Iterate At Blazing Speed
Applying Compiler Techniques to Iterate At Blazing Speed
 
Iterate Like a Whirling Dervish
Iterate Like a Whirling DervishIterate Like a Whirling Dervish
Iterate Like a Whirling Dervish
 
Extreme Testing at kaChing
Extreme Testing at kaChingExtreme Testing at kaChing
Extreme Testing at kaChing
 
Type Checking JavaScript
Type Checking JavaScriptType Checking JavaScript
Type Checking JavaScript
 
Xignite's Dedicate kaChing Api
Xignite's Dedicate kaChing ApiXignite's Dedicate kaChing Api
Xignite's Dedicate kaChing Api
 
Add (Syntactic) Sugar To Your Java
Add (Syntactic) Sugar To Your JavaAdd (Syntactic) Sugar To Your Java
Add (Syntactic) Sugar To Your Java
 
kaChing's API garage event
kaChing's API garage eventkaChing's API garage event
kaChing's API garage event
 

Recently uploaded

Planning Of Procurement o different goods and services
Planning Of Procurement o different goods and servicesPlanning Of Procurement o different goods and services
Planning Of Procurement o different goods and services
JoytuBarua2
 
DESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docxDESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docx
FluxPrime1
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
Jayaprasanna4
 
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdfHybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
fxintegritypublishin
 
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Dr.Costas Sachpazis
 
Nuclear Power Economics and Structuring 2024
Nuclear Power Economics and Structuring 2024Nuclear Power Economics and Structuring 2024
Nuclear Power Economics and Structuring 2024
Massimo Talia
 
Halogenation process of chemical process industries
Halogenation process of chemical process industriesHalogenation process of chemical process industries
Halogenation process of chemical process industries
MuhammadTufail242431
 
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
AJAYKUMARPUND1
 
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
bakpo1
 
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptxCFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
R&R Consult
 
Immunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary AttacksImmunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary Attacks
gerogepatton
 
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
obonagu
 
weather web application report.pdf
weather web application report.pdfweather web application report.pdf
weather web application report.pdf
Pratik Pawar
 
Forklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella PartsForklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella Parts
Intella Parts
 
HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
Robbie Edward Sayers
 
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
H.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdfH.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdf
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
MLILAB
 
Student information management system project report ii.pdf
Student information management system project report ii.pdfStudent information management system project report ii.pdf
Student information management system project report ii.pdf
Kamal Acharya
 
Automobile Management System Project Report.pdf
Automobile Management System Project Report.pdfAutomobile Management System Project Report.pdf
Automobile Management System Project Report.pdf
Kamal Acharya
 
Architectural Portfolio Sean Lockwood
Architectural Portfolio Sean LockwoodArchitectural Portfolio Sean Lockwood
Architectural Portfolio Sean Lockwood
seandesed
 
Gen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdfGen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdf
gdsczhcet
 

Recently uploaded (20)

Planning Of Procurement o different goods and services
Planning Of Procurement o different goods and servicesPlanning Of Procurement o different goods and services
Planning Of Procurement o different goods and services
 
DESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docxDESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docx
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
 
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdfHybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
 
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
 
Nuclear Power Economics and Structuring 2024
Nuclear Power Economics and Structuring 2024Nuclear Power Economics and Structuring 2024
Nuclear Power Economics and Structuring 2024
 
Halogenation process of chemical process industries
Halogenation process of chemical process industriesHalogenation process of chemical process industries
Halogenation process of chemical process industries
 
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
 
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
 
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptxCFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
 
Immunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary AttacksImmunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary Attacks
 
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
 
weather web application report.pdf
weather web application report.pdfweather web application report.pdf
weather web application report.pdf
 
Forklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella PartsForklift Classes Overview by Intella Parts
Forklift Classes Overview by Intella Parts
 
HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
 
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
H.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdfH.Seo,  ICLR 2024, MLILAB,  KAIST AI.pdf
H.Seo, ICLR 2024, MLILAB, KAIST AI.pdf
 
Student information management system project report ii.pdf
Student information management system project report ii.pdfStudent information management system project report ii.pdf
Student information management system project report ii.pdf
 
Automobile Management System Project Report.pdf
Automobile Management System Project Report.pdfAutomobile Management System Project Report.pdf
Automobile Management System Project Report.pdf
 
Architectural Portfolio Sean Lockwood
Architectural Portfolio Sean LockwoodArchitectural Portfolio Sean Lockwood
Architectural Portfolio Sean Lockwood
 
Gen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdfGen AI Study Jams _ For the GDSC Leads in India.pdf
Gen AI Study Jams _ For the GDSC Leads in India.pdf
 

How to Send a Receipt, Topics in Concurrency and Distributed Systems

  • 1. How to Send a Receipt Topics in Concurrency and Distributed Systems | @pascallouisperez
  • 2. The Situation ‣ It’s Black Friday on an e-commerce site. ‣ When a customer makes a purchase, ‣ We send said customer a purchase receipt.
  • 3. Not That Simple
 Sending a purchase receipt via email after a purchase is a concrete product requirement which takes us down into distributed systems design. Generalizable Problematic
 Many problems we encounter can be rephrased as the canonical ‘sending a receipt’ problem. And hence, understanding it well is a powerful tool. How to Send a Receipt Topics in Concurrency and Distributed Systems
  • 4. Bad Solution purchase_handler(…) { err = charge_cc(cc, order.amount); if err != nil { return err } err = mark_as_paid(db, order); if err != nil { return err } err = send_receipt(order.customer); if err != nil { return err } err = mark_receipt_sent(db, order); if err != nil { return err } return nil }
  • 5. Bad Solution Mo Money Mo Problems ‣ Not easy to charge a credit card. ‣ Auth / Capture model? Single Charge? ‣ Gateway Idempotency? ‣ Handling of timeouts? TCP interruptions? ‣ Database crash? purchase_handler(…) { err = charge_cc(cc, order.amount); if err != nil { return err } err = mark_as_paid(db, order); if err != nil { return err } err = send_receipt(order.customer); if err != nil { return err } err = mark_receipt_sent(db, order); if err != nil { return err } return nil }
  • 6. ‣ Failure to mark as paid leads to dangling payment without a confirmed order! ‣ Or a paid order without an email receipt. ‣ Retrying the handler would double charge. ‣ (Many other bad things.) Bad Solution Some of the Failure Modes purchase_handler(…) { err = charge_cc(cc, order.amount); if err != nil { return err } err = mark_as_paid(db, order); if err != nil { return err } err = send_receipt(order.customer); if err != nil { return err } err = mark_receipt_sent(db, order); if err != nil { return err } return nil }
  • 7. (Shorthand) purchase_handler(…) { err = charge_cc(cc, order.amount); on err return err = mark_as_paid(db, order); on err return err = send_receipt(order.customer); on err return err = mark_receipt_sent(db, order); on err return return nil }
  • 9. Charging Single Call Model ‣ Creating a charge debits the customer’s credit card. ‣ Charges are uniquely identified, and the payment provider guarantees idempotency. ‣ Makes it easy to charge, simply retry until success. However, requires care to avoid dangling payments.
  • 10. Charging Auth / Capture Model ‣ Authorize creates a pending transaction, and reserves funds. Auth are uniquely identified… And providers expose pseudo idempotency. ‣ Capture confirms the transactions. ‣ Makes it hard to charge. However, avoids dangling payments in failure cases. (Let’s ignore this model for now.)
  • 11. Charging Let’s Fix It purchase_handler(…) { err = run_transaction(db, func(tx) error { order.cc_charge_uuid = new_uuid(…); save(order); }); on err return charge, err = stripe.create_charge(order); on err retur err = run_transaction(db, func(tx) error { order.cc_charge = charge; save(order); }); on err return err = send_receipt(order.customer); on err return err = mark_receipt_sent(db, order); on err return return nil }
  • 12. Charging The Pattern Prepare, Do, Propagate ‣ Tell them what you are going to tell them; ‣ Tell them; ‣ Then tell them what you told them. purchase_handler(…) { err = run_transaction(db, func(tx) error { order.cc_charge_uuid = new_uuid(…); save(order); }); on err return charge, err = stripe.create_charge(order); on err retur err = run_transaction(db, func(tx) error { order.cc_charge = charge; save(order); }); on err return err = send_receipt(order.customer); on err return err = mark_receipt_sent(db, order); on err return return nil }
  • 13. Charging Let’s Fix It purchase_handler(…) { err = run_transaction(db, func(tx) error { order.cc_charge_uuid = new_uuid(…); save(order); }); on err return charge, err = stripe.create_charge(order); on err retur err = run_transaction(db, func(tx) error { order.cc_charge = charge; save(order); }); on err return err = send_receipt(order.customer); on err return err = mark_receipt_sent(db, order); on err return return nil } ‣ No ‘cc_charge_uuid’: failed early, can retry. ‣ No ‘cc_charge’: retry payment, Stripe guarantees idempotency. ‣ Did we send the receipt?
  • 14. How are Emails Sent Anyways?
  • 15. $ telnet smtp.thatsanexample.com 25 HELO thatsanexample.com MAIL from: <pascal@thatsanexample.com> RCPT to: <pascallouisperez@gmail.com> DATA From: pascal@thatsanexample.com To: pascallouisperez@gmail.com Subject: SMTP is low level and simple This is an example. . QUIT Sending Emails Quick SMTP Primer ‣ RFC2821 for SMTP protocol; and ‣ RFC2822 for message format.
  • 16. Sending Emails Avoiding Duplicates ‣ Great. How do you avoid duplicate delivery of messages?
  • 17. Sending Emails Avoiding Duplicates ‣ Only four mentions of ‘duplicate’ in RFC2821: 1. Avoiding duplicates for mailing lists, not relevant; 2. Specification of a ‘text line’ which has a ‘dot duplicate’, not relevant; 3. Page 62 represented here, simplistic recommendation; 4. Reference 28 which points to an 1988 article “Duplicate messages and SMTP”, which became RFC1047.
  • 18. Sending Emails Avoiding Duplicates ‣ Oh, and RFC1047 is not useful. Good Description of the Problem “If the communications link fails during this synchronization gap, then the message has been duplicated. Both mailers have active copies of the message that they will try to deliver.” Simplistic Suggestion “The best way to avoid the synchronization problem is to minimize the length of the synchronization gap. In other words, receiving mailers should acknowledge the final dot as soon as possible and do more complex processing of the message later.”
  • 19. Sending Emails Avoiding Duplicates ‣ RFC2821 replaced RFC821 in 2001; ‣ In particular, it saw the introduction of a ‘message-id’. ‣ One can hope most clients correctly populate this field (just kidding); and ‣ One can hope most SMTP relays use this field to de- duplicate messages.
  • 20. Sending Emails Email Delivery Services ‣ Your friendly REST speaking email delivery services! ‣ Except…
  • 21. Sending Emails Email Delivery Services ‣ SendGrid doesn’t expose the message-id.
  • 22. Sending Emails Email Delivery Services ‣ MailChimp doesn’t expose the message-id; ‣ Though it allows custom headers to be passed, hence likely making it possible to pass a message-id.
  • 23. Sending Emails Email Delivery Services ‣ SailThru has a ‘send_id’, yeah!
 
 (Note: Haven’t tested if this is used to populate the message-id. Seems to imply as much.)
  • 24. ‣ Impossible to avoid duplicates. ‣ Hence, we have ‣ No delivery; or ‣ At least once delivery. (It sucks. SMS is worse. So is the post office frankly.) Sending Emails The Conclusion
  • 26. Fix It, How? Back to the Subject at Hand purchase_handler(…) { err = run_transaction(db, func(tx) error { order.cc_charge_uuid = new_uuid(…); save(order); }); on err return charge, err = stripe.create_charge(order); on err retur err = run_transaction(db, func(tx) error { order.cc_charge = charge; save(order); }); on err return err = send_receipt(order.customer); on err return err = mark_receipt_sent(db, order); on err return return nil } ‣ If we are unable to record the charge, don’t send the receipt. ‣ If we are able to record the charge, we must guarantee that the receipt will be delivered. ‣ Given what we learned about email delivery, our choice is between no delivery, or at least once delivery.
  • 27. Fix It, How? Back to the Subject at Hand purchase_handler(…) { err = run_transaction(db, func(tx) error { order.cc_charge_uuid = new_uuid(…); order.receipt_uuid = new_uuid(…); save(order); }); on err return charge, err = stripe.create_charge(order); on err retur err = run_transaction(db, func(tx) error { order.cc_charge = charge; order.receipt_state = PENDING; save(order); }); on err return return nil } Prepare ‣ Record intent to send receipt. ‣ The intent includes the identifier of the receipt: it is this specific email we intend to send. Do ‣ Charge Propagate ‣ In the background, process the email queue and retry until confirmation of delivery.
  • 29. Inventoried Item (e.g. a seated ticket to a show) Prepare: check availability, and ‘soft reserve’ to avoid double bookings. Propagate: confirm reservation. Activating Gift Cards Prepare: soft creation of the card. Propagate: activate and fund. Coupons
 Prepare: verify the validity, and ’soft redeem’ to avoid double use. Propagate: actually redeem it. Similar Problematics Hard Across µServices Boundaries
  • 30. The Order as Distributed Transaction Coordinator Distributed transaction processing, and two-phase commit. Order ManagementOrder Management Purchase
  • 31. The Order as Distributed Transaction Coordinator Distributed transaction processing, and two-phase commit. Order ManagementOrder Management Prepare: “Soft Reserve” Prepare: “Create Gift Card” “The Commit Request Phase”
  • 32. The Order as Distributed Transaction Coordinator Distributed transaction processing, and two-phase commit. Order ManagementOrder Management Do: “Charge” “The Commit Phase”
  • 33. The Order as Distributed Transaction Coordinator Distributed transaction processing, and two-phase commit. Order ManagementOrder Management Propagate: “Confirm” Propagate: “Activate” Propagate: “Send” “The Success Phase”
  • 34. The Order as Distributed Transaction Coordinator Distributed transaction processing, and two-phase commit. Order ManagementOrder Management Propagate: “Cancel” Propagate: “Dispose” “The Failure Phase”
  • 35. Propagate: “Reconcile to Cancel (or Refund) Dangling Payment” The Order as Distributed Transaction Coordinator Distributed transaction processing, and two-phase commit. Order ManagementOrder Management Propagate: “Expiry” Propagate: “Garbage Collect” “The (Unofficial) Cleanup Post Failure Phase”
  • 36. In Closing ‣ RTFM (“Read The Fucking Manual”) ‣ It's turtles all the way down, follow the thread ‣ Correctness at app level requires understand of low level details ‣ Distributed Systems are Hard ‣ No need to re-invent the wheel, most of CS was invented in the 1970s, and we’re just repurposing known techniques ‣ Design Early, Design Often ‣ You can’t iterate your way to correctness ‣ Think through your systems early, write it down, rinse, repeat