24841

Spray.io: When (not) to use non-blocking route handling?

If we are thinking of production grade REST API, should we use non-blocking as much as possible, e.g.

def insertDbAsync(rows: RowList): Future[Unit] = ... ... val route = path("database" / "insertRowList") { post { entity(as[RowList]) { rows => log.info(s"${rows.length} rows received") val async = insertDbAsync(rows) onComplete(async) { case Success(response) => complete("success") case Failure(t) => complete("error") } } } }

I'm thinking that the answer will most likely be a 'yes', but what are some guidelines in deciding what should and should not be a blocking code, and why?

Answer1:

Spray uses Akka as underlying platform, so recommendations are same as for actors (Blocking Needs Careful Management). Blocking code may require too much threads, which may:

    <li>

    kill actor's lightweightness: millions of actors may operate on one thread by default. Let's say one non-blocked actor requires 0.001 threads for example. One blocked actor (which blocking time is, let's say, 100 times more than usual) will take 1 thread avg (not always same thread). First, The more threads you have - the more memory you loose - every blocked thread holds full callstack allocated before blocking, including references from stack (so GC can't erase them). Second, if you have more than number_of_processors threads - you will loose the performance. Third, if you use some dynamical pool - adding new thread may take some significant amount of time.

    </li> <li>

    cause thread's starvation - you may have pool filled with threads, which doing nothing - so new tasks can't be processed before blocking operation complete (0 % CPU load, but 100500 messages waiting to be processed). It may even cause deadlocks. However, Akka uses Fork-Join-Pool by default so if your blocking code is managed (surrounded with scala.concurrent.blocking - Await.result have such surrounding inside ) - it will prevent starvation by cost of creating new thread instead of blocked one, but it won't compensate other problems.

    </li> <li>

    traditionally cause deadlocks, so it's bad for design

    </li> </ul>

    If code is blocking from outside, you can surround it with future:

    import scala.concurrent._ val f = Future { someNonBlockingCode() blocking { //mark this thread as "blocked" so fork-join-pool may create another one to compensate someBlocking() } }

    Inside separate actor:

    f pipeTo sender //will send the result to `sender` actor

    Inside spray routing:

    onComplete(f) { .. }

    It's better to execute such futures inside separate pool/dispatcher (fork-join-pool based).

    P.S. As an alternative to futures (they may not be much convinient from design perspectvive) you may consider Akka I/O, Continuations/Coroutines, Actor's pools (also inside separate dispatcher), Disruptor etc.

    Answer2:

    If you're using spray everything must be non-blocking as a matter of correctness - otherwise you'll block the (very small number of) dispatch threads and your server will stop responding.

Recommend

  • Exercise #5 in Chapter 6 of Programming in C, “simple “printing” calculator”
  • Excel worksheet to Numpy array
  • Pygame with Multiple Windows
  • What is the difference between new Function and vm?
  • How exactly BIC in Augmented Dickey–Fuller test work in Python?
  • stackexchange redis hashscan return all fields in one time
  • Solr MoreLikeThis: Can I give Solr 5 document IDs and get more documents like these 5?
  • Rails Database Design: Use strings or integer?
  • R - get the column index of a matrix based on a function for each row
  • Given an array of integers, find the second largest and second smallest within the array
  • Get onpage to work correctly
  • Scala, gremlin-scala, HLists, Poly2, RightFold and a missing implicit Prepend
  • Preventing executables with invalid Authenticode signatures from running
  • tomcat : How to get opened jdbc:oracle connection's stack trace?
  • Should concrete implementation provide any public API not present in the interface it implements?
  • In Akka Java actor model, can a router create actors with non-default constructor?
  • How to enumerate Azure subscriptions and tenants programmatically?
  • Prevent Emacs from modifying the OS X clipboard?
  • Entity Framework Core: Include many-to-many related objects in WebAPI
  • Java : Simple XML not parsing the xml. Gives Exception
  • limited threads in soapUI free version
  • Parallel sieve of Eratosthenes - Java Multithreading
  • In Akka, is ActorContext thread safe?
  • Timeout a query
  • How do you create a Fuseki SPARQL server using the Apache Jena Java API?
  • Is it possible to define rest argument in OCaml?
  • Why must we declare a variable name when adding a method to a struct in Golang?
  • rspec simple example getting error on request variable in integration test
  • How to determine if there are bytes available to be read from boost:asio:serial_port
  • How to attach a node.js readable stream to a Sendgrid email?
  • Record samples being played with OpenAL
  • Unity3D & Android: Difference between “UnityMain” and “main” threads?
  • Exception “firebase.functions() takes … no argument …” when specifying a region for a Cloud Function
  • Why HTML5 Canvas with a larger size stretch a drawn line?
  • Spray.io: When (not) to use non-blocking route handling?
  • Sony Xperia Z Tablet not found by adb
  • Modifying destination and filename of gulp-svg-sprite
  • GridView Sorting works once only
  • Hibernate gives error error as “Access to DialectResolutionInfo cannot be null when 'hibernate.
  • Transpose CSV data with awk (pivot transformation)