CLOJURE AT A POST OFFICE
HTTP-KIT
• AWS C1-Medium!
• 7GB Ram!
• 8 Cores
• 313,852 Concurrent Users!
• 4756.79 Requests Per Second
If payment is success: display amount remaining on bill!If payment fails: display error
Make a payment on a bill!- Not necessarily a full payment
POST /bills/:bill-id/payments Session: user-id Post Data: amount
1. GET credit card token for user! 1.1. POST request to payment gateway!2. GET how much was left to be payed
CANDIDATES
• Synchronous promises!
• Promise monad let/do!
• Raw promises!
• Raw callbacks!
• core.async!
• Lamina pipeline!
• Meltdown (LMAX Disrupter)!
• Pulsar promises!
• Pulsar Actors
SOLUTION 0: SYNCHRONOUS
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]"""(let'[token"""""""(auth/card/token"user*id)"""""""""details"""""(bill/details"bill*id)"""""""""transaction"(payment/bill"bill*id"amount"@token)]"""""(if'(success?"@transaction)"""""""(render/remaining/response"@details"amount)"""""""error*response)))"
SOLUTION 1.1: PROMISE MONAD DO
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]""""""(do'[token"""""""(auth/card/token"user*id)"""""""""""transaction"(payment/bill"bill*id"amount"token)"""""""""""details"""""(bill/details"bill*id)]""""""""""(return""""""""""""(if'(success?"transaction)""""""""""""""(render/remaining/response"details"amount)""""""""""""""error*response))))
SOLUTION 1.2: PROMISE MONAD LET/DO
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]""""""(let'[token*req"""(auth/card/token"user*id)""""""""""""details*req"(bill/details"bill*id)]""""""""(do'[token"""""""token*req"""""""""""""transaction"(payment/bill"bill*id"amount"token)"""""""""""""details"""""details*req]""""""""""""(return""""""""""""""(if'(success?"transaction)""""""""""""""""(render/remaining/response"details"amount)""""""""""""""""error*response)))))
SOLUTION 1.3: PROMISE MONAD LET/DO/DO
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]""""""(let'[token*req"""(auth/card/token"user*id)""""""""""""details*req"(bill/details"bill*id)]""""""""(do'[token"""""""token*req"""""""""""""transaction"(payment/bill"bill*id"amount"token)]""""""""""""(if'(success?"transaction)""""""""""""""(do'[details"details*req]""""""""""""""""""(return"(render/remaining/response"details"amount)))""""""""""""""(return"error*response)))))
SOLUTION 3: RAW PROMISES
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]""""""(let'[transaction*req"(*>"(auth/card/token"user*id)""""""""""""""""""""""""""""""""(then"(partial"payment/bill"bill*id"amount)))""""""""""""details*req"""""(bill/details"bill*id)]""""""""(when"transaction*req"details*req""""""""""(fn'[transaction"details]""""""""""""(if'(success?"transaction)""""""""""""""(render/remaining/response"details"amount)""""""""""""""error*response)))))
SOLUTION 4: RAW CALLBACKS
Not"Viable
SOLUTION 5: CORE.ASYNC
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]""""""(go"(let'[token"""""""(auth/card/token"user*id)""""""""""""""""details"""""(bill/details"bill*id)""""""""""""""""transaction"(payment/bill"bill*id"amount"(<!"token))]""""""""""""(if'(success?"(<!"transaction))""""""""""""""(render/remaining/response"(<!"details)"amount)""""""""""""""error*response))))
SOLUTION 6: LAMINA PIPELINE
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]""""""(let'[details*req"(bill/details"bill*id)]""""""""(pipeline"(auth/card/token"user*id)""""""""""""""""""(partial"payment/bill"bill*id"amount)""""""""""""""""""(fn'[transaction]""""""""""""""""""""(if'(success?"transaction)""""""""""""""""""""""(on/realized"details*req"""""""""""""""""""""""""""""""""""(fn'[details]"""""""""""""""""""""""""""""""""""""(render/remaining/response"details"amount)))""""""""""""""""""""""error*response)))))
SOLUTION 7: MELTDOWN
No"point
SOLUTION 8: PULSAR PROMISES
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]"""#(let'[token"""""""(auth/card/token"user*id)""""""""""details"""""(bill/details"bill*id)""""""""""transaction"(payment/bill"bill*id"amount"@token)]"""""(if'(success?"@transaction)"""""""(render/remaining/response"@details"amount)"""""""error*response)))
SOLUTION 9: PULSAR ACTORS
Not"Appropriate
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]"""(let'[token"""""""(auth/card/token"user*id)"""""""""details"""""(bill/details"bill*id)"""""""""transaction"(payment/bill"bill*id"amount"@token)]"""""(if'(success?"@transaction)"""""""(render/remaining/response"@details"amount)"""""""error*response))) Synchronous
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]"""(go"(let'[token"""""""(auth/card/token"user*id)"""""""""""""details"""""(bill/details"bill*id)"""""""""""""transaction"(payment/bill"bill*id"amount"(<!"token))]"""""""""(if'(success?"(<!"transaction))"""""""""""(render/remaining/response"(<!"details)"amount)"""""""""""error*response)))) core.async
(GET""/bills/:bill*id/payments""[bill*id"user*id"amount]"""#(let'[token"""""""(auth/card/token"user*id)""""""""""details"""""(bill/details"bill*id)""""""""""transaction"(payment/bill"bill*id"amount"@token)]""""""(if'(success?"@transaction)""""""""(render/remaining/response"@details"amount)""""""""(error/response)))) Pulsar
""def"payBill(billId:"Integer,"userId:"Integer,"amount:"Integer):Future[Option[Json]]"="{ """"val"seq"="for"{ """"""token"<*"Auth.cardToken(userId) """"""tr"<*"Payment.bill(token) """"}"yield"tr " """"async"{ """"""val"transactionProcess"="await(seq.run) """"""val"detailProcess"="await(BillOps.details(billId)) """"""for"{ """"""""transaction"<*"transactionProcess """"""""detail"<*"detailProcess """"""}"yield"renderRemainingResponse(amount,"detail) """"} ""}
SCALA
REQUESTS PER SECOND
CQRS!
• Want fast reads. Reduce the number of queries.!
• Don't want to have to update write code every time we add a new reader.!
• Don't want to have to update reader every time there's a new writer.!
• Would be great to have an event stream to re-calculate current state.
1.#Just#write#to#normalised#copy#1.1.# Just#read#everything#every#time#1.2.# Read#and#cache#1.2.1.# Cache#the#page#1.2.2.# Cache#the#intermediate#data;structure#
2.#Just#write#to#views#2.1.# Direct#write#2.2.# Event#stream#2.2.1.# Persisted#2.2.1.1.# Domain#events#2.2.1.1.1.#Live#update#2.2.1.1.2.#Async#update#
2.2.1.2.# CRUD#event#2.2.1.2.1.#Live#update#2.2.1.2.2.#Async#update#
2.2.2.# Ephemeral#2.2.2.1.# Domain#events#2.2.2.1.1.#Live#update#
2.2.2.2.# CRUD#event#2.2.2.2.1.#Live#update#
3.#Write#to#views#and#normalized#copy##3.1.# Direct#write#
3.2.# Event#stream#3.2.1.# Persisted#3.2.1.1.# Domain#events#3.2.1.1.1.#Live#update#3.2.1.1.2.#Async#update#
3.2.1.2.# CRUD#event#3.2.1.2.1.#Live#update#3.2.1.2.2.#Async#update#
3.2.2.# Ephemeral#3.2.2.1.# Domain#events#3.2.2.1.1.#Live#update#
3.2.2.2.# CRUD#event#3.2.2.2.1.#Live#update#
3.3.# Write#to#normalized#keyspace#causing#trigger#3.3.1.# Direct#update#to#views#3.3.2.# Event#to#be#published#3.3.2.1.# Persisted#3.3.2.1.1.#Live#Update#3.3.2.1.2.#Async#update#
3.3.2.2.# Ephemeral#3.3.2.2.1.#Live#Update
Write
!!
Cassandra
Service A
Service B Index Maintainer
Notify
Read Write
Rabbit MQ
Publish
Triggers
CASSANDRA TRIGGERS
• Can just throw the Clojure jar in there!
• Everything is byte buffers!
• Need to know the type of fields out of band!
• One class per trigger per table!
• Bizarre key names (format changes depending on value type)
User Service
Mail Service
Provider Service
Cassandra
IOS Web Android
User Service
Authentication
Multi Factor Authentication
Authorisation
User Profile
Password Reset
Top Related