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


if thinking of production grade rest api, should use non-blocking 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 answer 'yes', guidelines in deciding should , should not blocking code, , why?

spray uses akka underlying platform, recommendations same actors (blocking needs careful management). blocking code may require threads, may:

  • kill actor's lightweightness: millions of actors may operate on 1 thread default. let's 1 non-blocked actor requires 0.001 threads example. 1 blocked actor (which blocking time is, let's say, 100 times more usual) take 1 thread avg (not same thread). first, more threads have - more memory loose - every blocked thread holds full callstack allocated before blocking, including references stack (so gc can't erase them). second, if have more number_of_processors threads - loose performance. third, if use dynamical pool - adding new thread may take significant amount of time.

  • cause thread's starvation - may have pool filled threads, doing nothing - new tasks can't processed before blocking operation complete (0 % cpu load, 100500 messages waiting processed). may cause deadlocks. however, akka uses fork-join-pool default if blocking code managed (surrounded scala.concurrent.blocking - await.result have such surrounding inside ) - prevent starvation cost of creating new thread instead of blocked one, won't compensate other problems.

  • traditionally cause deadlocks, it's bad design

if code blocking outside, can surround future:

 import scala.concurrent._  val f = future {      somenonblockingcode()      blocking { //mark thread "blocked" fork-join-pool may create 1 compensate         someblocking()      }    } 

inside separate actor:

 f pipeto sender //will send result `sender` actor 

inside spray routing:

 oncomplete(f) { .. } 

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

p.s. alternative futures (they may not convinient design perspectvive) may consider akka i/o, continuations/coroutines, actor's pools (also inside separate dispatcher), disruptor etc.


Comments

Popular posts from this blog

tcpdump - How to check if server received packet (acknowledged) -