2

I am aware that in Java 8 it is not a good idea to start long-running tasks in stream framework's filter, map, etc. methods, as there is no way to tune the underlying fork-join pool and it can cause latency problems and starvings.

Now, my question would be, is there any problem like that with Scala? I tried to google it, but I guess I just can't put this question in a google-able sentence.

Let's say I have a list of objects and I want to save them into a database using a forEach, would that cause any issues? I guess this would not be an issue in Scala, as functional transformations are fundamental building blocks of the language, but anyway...

tharindu_DG
  • 8,900
  • 6
  • 52
  • 64
TamasGyorfi
  • 587
  • 1
  • 6
  • 14

1 Answers1

2
  • If you don't see any kind of I/O operations then using futures can be an overhead.

    def add(x: Int, y: Int) = Future { x + y }

  • Executing purely CPU-bound operations in a Future constructor will make your logic slower to execute, not faster. Mapping and flatmapping over them, can increase add fuel to this problem.

  • In case you want to initialize a Future with a constant/simple calculation, you can use Future.successful().

  • But all blocking I/O, including SQL queries makes sence be wrapped inside a Future with blocking

    E.g :

    Future { DB.withConnection { implicit connection => val query = SQL("select * from bar") query() } } Should be done as,

    import scala.concurrent.blocking Future { blocking { DB.withConnection { implicit connection => val query = SQL("select * from bar") query() } }

  • This blocking notifies the thread pool that this task is blocking. This allows the pool to temporarily spawn new workers as needed. This is done to prevent starvation in blocking applications.

  • The thread pool(by default the scala.concurrent.ExecutionContext.global pool) knows when the code in a blocking is completed.(Since it's a fork join thread pool)

  • Therefore it will remove the spare worker threads as they completes, and the pool will shrink back down to its expected size with time(Number of cores by default).

  • But this scenario can also backfire if there is not enough memory to expand the thread pool.

  • So for your scenario, you can use

    images.foreach(i => {
    
      import scala.concurrent.blocking
       Future {
        blocking {
          DB.withConnection { implicit connection =>
          val query = SQL("insert into .........")
          query()
        }
      }
    })
    
  • If you're doing a lot of blocking I/O, then it's a good practice to create a separate thread-pool/execution context and execute all blocking calls in that pool.

References :

Hope this helps.

tharindu_DG
  • 8,900
  • 6
  • 52
  • 64
  • Please note that `blocking` is but a recommendation: https://stackoverflow.com/questions/29068064/scala-concurrent-blocking-what-does-it-actually-do and https://stackoverflow.com/questions/19681389/use-case-of-scala-concurrent-blocking – Yaneeve Jun 28 '17 at 07:37