the sad story of the server that tries to please everyone

34
The Sad ˝ Story ˝ Of the Server ˝ that Tries to ˝ Please Everyone Iulian Dogariu CodeCamp 25 Oct 2014

Upload: iulian-dogariu

Post on 13-Jul-2015

74 views

Category:

Software


1 download

TRANSCRIPT

The Sad ˝Story ˝

Of the Server ˝that Tries to ˝

Please Everyone

Iulian Dogariu CodeCamp 25 Oct 2014

Lil' serverobject Tickets extends Controller {

def book(eventId: Int, count: Int) = Action {

val bookings: Seq[Booking] =

TicketsRepository.bookForEvent(eventId, count)

Ok(s"Thank you! These are your bookings " +

bookings.mkString("\n"))

}

}

Lil' serverobject Tickets extends Controller {

def book(eventId: Int, count: Int) = Action {

val bookings: Seq[Booking] =

TicketsRepository.bookForEvent(eventId, count)

Ok(s"Thank you! These are your bookings " +

bookings.mkString("\n"))

}

}

More than a lil'def bookForEvent(eventId: Int, count: Int): Seq[Booking] = {

DB.withTransaction { implicit c =>

val numAvailableTickets: Int = SQL(

"SELECT num_free FROM tickets WHERE event_id={eventId}").

on("eventId" -> eventId).as(SqlParser.int("num_free").single)

(0 to count).map { _ =>

val bookingId: Option[Long] = SQL(

"INSERT INTO bookings(event_id)" +

" VALUES({eventId})").on('eventId -> eventId).executeInsert()

Booking(bookingId, eventId)

}

}

}

More than a lil'

How bad is it?

Normal CPU instruction

Fetch from RAM

Disk seek

Ping from Iași to Bucharest

Ping from Iași to USA

0.5 nsec

120 nsec

10 msec

30 msec

130 msec

No, really

Normal CPU instruction

Fetch from RAM

Disk seek

Ping from Iași to Bucharest

Ping from Iași to USA

1 sec

4 min

7.5 Months

2 Years

6 Years

Back to the lil' server

Back to the lil' server

Back to the lil' server

What does the user see?

If I only knew...

• I'd go somewhere else!

Show me the bottleneck

Queues and messages bookingActor ! NewBookingCommand(1, 2)

def receive = {

case BookingSucceeded(eventId, bookings) =>

println(s"Thank you!" + bookings.mkString("\n"))

}

class BookingActor extends Actor {

def receive = {

case NewBookingCommand(eventId, ct) =>

val bookings = TicketsRepository.bookForEvent(eventId, ct)

sender ! BookingSucceeded(eventId, bookings)

}

}

One small step

Pulling workGIMME WORK!!

WIP

The math of WIP

Push back

Push back

Reactive streams

Reactive streams

val bookingFlow =

Flow[NewBookingCommand].map { command =>

val bookings = TicketsRepository.

bookForEvent(command.eventId, command.count)

BookingSucceeded(

command.eventId, bookings)

}

val in = ThunkTap[NewBookingCommand] {

// Flood with requests

Some(NewBookingCommand(1, 2))

}

val out = ForeachDrain[BookingSucceeded] {

case BookingSucceeded(eventId, bookings) =>

println(s"Thank you!" + bookings.mkString("\n"))

}

FlowGraph { implicit b =>

in ~> bookingFlow ~> out

}.run()

Altogether nowFlowTap

Drain

Recap

• Do you really know what's slow in your code?

• Queues make bottlenecks obvious

• If you can't serve me, tell me so ("Agree to Start")

• Because people pleasers end up disappointing everyone equally :-)

The Soup Nazi problem

Q & A ?

Thank You, Come Again

And please fill in the evaluation form :-)