Blog

Asynchronous Input Validation with Scala 2.10 – Part 1

Traditionally people have been taught to stay away from multithreading. Unfortunately this poses two problems. The first has been around for a long time: When waiting for I/O the system isn’t free to do meaningful work. The second is that while the clock speed of processors has frozen, the number of cores is ever increasing. Server software can perform much better when making use of all cores instead of just one.

While the decision to execute a piece of code synchronously or asynchronously can be difficult at times, one situation where it almost always pays off to run asynchronously is when executing I/O. Often a web system needs to query various external resources in order to complete. It is of no use, for instance, to wait for a database query to determine whether a username is available before querying DNS to check if an MX record exists.

This article is going to show how the recurring task of validating user input can be done more efficiently using Scala 2.10.

Imagine a signup form where a user supplies her email address and selects a password. There are several validation rules these pieces of data need to adhere to.

def signup(
    email: String,
    password: String,
    passwordRepeated: String): Boolean = {
  val rules = emailDao.isAvailable(email) #:: // Takes 5 ms
    emailDao.isValidEmailDomain(email) #::    // Takes 50 ms
    password.length > 6 #::                   // Takes < 1 ms
    password == passwordRepeated #::          // Takes < 1 ms
    Stream.empty
  // Total at most 55 ms
  !rules.contains(false)
}

This function uses a Stream to assure lazy sequential evaluation of conditions. If any of these rules don’t pass, false is immediately returned.
Scala provides a few libraries that can provide non-blocking I/O. At other times, often with database drivers, you will have to make sure blocking I/O is handled asynchronously yourself. Both of these operations can result in a Future. A Future is Scala’s core representation of a job that might or might not be finished.
First let’s take a look at how we can handle blocking I/O asynchronously. Basically it is as simple as this:

import scala.concurrent.Future
import scala.concurrent.Await
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._

implicit val ec = ExecutionContext.global

val firstFutureResult = Future {
  emailDao.isAvailable(email)
}
val secondFutureResult = Future {
  emailDao.isValidEmailDomain(email)
}
// Takes 5 ms
val firstResult = Await.result(firstFutureResult, 1 second)
// Takes 50 ms - 5 ms = 45 ms
val secondResult = Await.result(secondFutureResult, 1 second)
// Takes 50 ms in total
firstResult && secondResult

If we could schedule multiple operations, the speed would be about as fast as the slowest operation. It’s not even necessary to await the outcome of each operation. Futures can be combined.

for {
  firstResult

Note that this block results in a Future[Boolean] and does not block the current thread at all. If this thread was to handle all incoming http requests this is very important.
That is it for this week. Later I will apply asynchronous handling to the first example and show more ways to combine and work with futures.

Spread the love