Slide 1

Slide 1 text

Akka: A shiny new hammer Now show me some nails already! Dag Liodden VP Engineering Tapad North East Scala Symposium 2012

Slide 2

Slide 2 text

So what is Akka?! 2! “Akka is a toolkit and runtime for building highly concurrent, distributed, and fault tolerant event- driven applications on the JVM.” -  http://akka.io

Slide 3

Slide 3 text

3! ZOMG!

Slide 4

Slide 4 text

4! Node.js?

Slide 5

Slide 5 text

5! Automatic web scale?

Slide 6

Slide 6 text

So what is Akka?! 6! Akka is a library library and / or runtime for ❱  Parallelism. Increasing performance by splitting tasks and executing them on multiple cores simultaneously. ❱  Concurrency. Coordinating resources between multiple threads of control.

Slide 7

Slide 7 text

What does this mean?! 7! ❱  Akka is NOT a general purpose framework ❱  Focused feature set, excellent implementation ❱  Surprisingly broad applicability ❱  Not a golden hammer

Slide 8

Slide 8 text

Real-time Ad technology 101! Strictly Confidential"

Slide 9

Slide 9 text

Real-time audience buying! Strictly Confidential" 9! ❱  Before ­  “Sites about travel at a max price of $1 CPM”" ❱  Now ­  Real-time buying" ­  User-level targeting constraints" ­  Value prediction" "

Slide 10

Slide 10 text

Lots of traffic The RTB Eco-system! 10! Browser Website Ad exchange Buyer A Buyer B Buyer C

Slide 11

Slide 11 text

How we use Akka! Strictly Confidential" 11! ❱  Concurrent access to mutable state ❱  Circuit-breaking for graceful degradation of service ❱  Batching and buffering ❱  Resource balancing and throttling ❱  Scheduling of background tasks

Slide 12

Slide 12 text

How we use Akka! Strictly Confidential" 12! ❱  Concurrent access to mutable state ❱  Circuit-breaking for graceful degradation of service ❱  Batching and buffering ❱  Resource balancing and throttling ❱  Scheduling of background tasks

Slide 13

Slide 13 text

Actors for concurrent mutable state! Strictly Confidential"

Slide 14

Slide 14 text

The bidding decision! Strictly Confidential" 14! trait Bidder {! def bid(req: Offer) : Option[Bid]! def onWin(win: WinNotification) : Unit! }!

Slide 15

Slide 15 text

Bidder topology! 15! AuctionRunner AuctionRunner Auction runner threads Campaign A Campaign B Remote buyer A Workers Remote buyer B Workers

Slide 16

Slide 16 text

Why model bidders as actors?! Strictly Confidential" 16! ❱  Local actors have state ❱  Remote actors have unpredictable response times "

Slide 17

Slide 17 text

Local bidders: Should we bid at all?! Strictly Confidential" 17! ❱  Targeting constraints ­  Run on iPads visiting sites about Travel" ­  Stateless" ❱  Budget constraints, even pacing of spend ­  How much spent so far?" ­  How much estimated spend so far?" ­  Stateful, extreme concurrency"

Slide 18

Slide 18 text

A local bidder actor! Strictly Confidential" 18! trait Bidder {! def bid(req: Offer) : Option[Bid]! def onWin(win: WinNotification) : Unit! }! ! class LocalBidderActor(bidder: Bidder) extends Actor {! private[this] _bidder = bidder! def receive = { ! case req: Offer=> self.reply(target.bid(req))! case win: WinNotification => target.onWin(win)! case bidder: Bidder => _bidder = bidder // Hot swap! ...! }! }! ! // Is this just bidder.synchronize {} ?! ! !

Slide 19

Slide 19 text

The auction runner! 19! def runAuction(req: Offer, rules: AuctionRules) = {! val responseFutures = bidderActorList.map {! !_.!!![Option[Bid]](req, MAX_RESPONSE_TIME)! }! try {! Futures.awaitAll(responseFutures) // Block! } catch {! case e: FutureTimeoutException => // One or more timeouts! }! val timelyResponses = responseFutures.flatMap(_.result)! rules.determineResult(req, timelyResponses)! }!

Slide 20

Slide 20 text

Benefits over synchronized! Strictly Confidential" 20! ❱  A single access model for local and remote bidders ❱  Dead simple state management (no leaking) ❱  CPU Pinning ❱  Parallelism (when there are CPU cycles available)

Slide 21

Slide 21 text

Graceful degradation and circuit-breaking! Strictly Confidential"

Slide 22

Slide 22 text

Circuit-breaking! Strictly Confidential" 22! ❱  Relentless traffic with no back-off ❱  VM Warm-up ❱  K/V hick-ups ❱  Traffic spikes ❱  Degrade the service to catch up! "

Slide 23

Slide 23 text

How to degrade service? ! Strictly Confidential" 23! ❱  Accept increased latency? ❱  Refuse connections?

Slide 24

Slide 24 text

How to degrade service? (cont.)! 24! ❱  Domain / service specific options ­  Stop loading user data => Bid with less information" ­  Stop running predictive optimization => Bid conservative" ­  Start passing on offer altogether => Opportunity cost"

Slide 25

Slide 25 text

When to degrade service?! Strictly Confidential" 25! ❱  Response time > 10ms? ­  Actors with timestamped units of work" ❱  Mailbox size > 5000 elements? ­  Custom dispatcher"

Slide 26

Slide 26 text

Circuit-breaking actor! Strictly Confidential" 26! " ! protected def receive = {! case WorkUnit(submittedAt, offer) =>! if (System.currentTimeMillis > (submittedAt + MAX_TIME)) {! Stats.incr(PerformanceMetrics.AUCTION_RUNNER("dropped"))! self reply None! } else {! self reply executeAuction(offer)! }! }!

Slide 27

Slide 27 text

Issues with the CB in the actor?! Strictly Confidential" 27! ❱  Long pauses => Long queues => OOM ❱  Bounded mailbox => Passing on the most recent offers! ­  + a lot of exceptions"

Slide 28

Slide 28 text

Circuit-breaking dispatcher! Strictly Confidential" 28! " " val workerDispatcher = new ExecutorBasedEventDrivenDispatcher(" …" ) with CircuitBreakingDispatcherSemantics {" val policy = new LogAndDiscardCircuitBreakerPolicy(2000, AuctionRunner.this) {" override def replyToOverflow(overflowCount: Int, msg: Any) = {" Stats.incr(PerformanceMetrics.AUCTION_RUNNER("discarded"))" Right(None)" }" }" }"

Slide 29

Slide 29 text

Issues with CB in dispatchers?! Strictly Confidential" 29! ❱  Might mask performance issues ❱  What’s the correct max length? ❱  Hard to do in Akka 2.0

Slide 30

Slide 30 text

Batching and buffering! Strictly Confidential"

Slide 31

Slide 31 text

Batching storage actor! Strictly Confidential" 31! class OfferStorageActor(dao: OfferDao)! extends BatchingStorageActor[(Offer,AuctionResult)] with ActorLogging {! /**! * Flush every 2 seconds of 25000 requests. The former will almost always be the trigger.! */! final def batchSize = 25000! final def purgeInterval = 2000L! ! def save(xs: Seq[(BidRequest,AuctionResult)]) {! logger.debug("Storing {} bid requests.", xs.size)! bidRequestDao.save(xs)! logger.debug("Done storing bid requests.")! }! }!

Slide 32

Slide 32 text

Akka pain points?! Strictly Confidential" 32! ❱  Untyped “interfaces” ❱  Asynchronous indirection => Hard to find original invocation point ❱  Testing

Slide 33

Slide 33 text

Questions?! Strictly Confidential"

Slide 34

Slide 34 text

Resource throttling and balancing! Strictly Confidential"

Slide 35

Slide 35 text

Resource throttling and balancing! Strictly Confidential" 35! ❱  Using actors as an executor service ❱  Turn blocking APIs into non-blocking ❱  Tune connection pools for throughput / latency

Slide 36

Slide 36 text

Scheduling of background tasks! Strictly Confidential"

Slide 37

Slide 37 text

Background processing use cases! Strictly Confidential" 37! ❱  Asynchronous writes ❱  Batching / buffering writes ❱  Lazy caching ❱  “Cron jobs”