whoami

Lucas Jenß

cat /etc/motd

The Coding Journal ツ — Notes taken on an epic coding journey. Technical solutions, debugging notes, and practical guides from the trenches of software development.

Close-up of white dominoes with black dots standing on green felt surface, shallow depth of field, focused mood

ls -la ~/languages/

total 8
drwxr-xr-x
▶ PHP
▶ Ruby
▶ Scala
▶ C#
▶ JavaScript
▶ Objective-C
▶ Shell Scripting

ls -la ~/toolchain/

total 7
drwxr-xr-x
▶ Typo3
▶ Akka
▶ Capistrano
▶ Git
▶ MAMP
▶ Adobe Illustrator
▶ NSTrackingArea (Cocoa)

uname -a

platforms
drwxr-xr-x
▶ Mac OS X
▶ Unix

Using Scala Future Chaining In Akka For Non-Blocking Database Queries

Akka applications often need to load a record, use its result to fetch related data, and then assemble a response without tying up an actor thread. Scala’s Future API provides a clear way to express that workflow, provided each database operation is genuinely asynchronous or isolated from the actor dispatcher. Learn more about Analisi De La Banda Sonora De Ghost In The Shell Vint Anys Despres.

The important distinction is between asynchronous control flow and non-blocking I/O. Wrapping JDBC code in Future changes when the work runs, but it does not make the JDBC call non-blocking. A reliable design uses an asynchronous database client, or places unavoidable blocking operations on a dedicated dispatcher.

This approach is useful for services running across Australian time zones, where a Melbourne customer may call an API while a Sydney operations team is inspecting logs or a Brisbane-based process is handling scheduled work. Predictable concurrency, timeouts, and failure handling matter more than simply adding more threads.

Why Future Chaining Matters

A Future[A] represents a value that may become available later. With map, you transform a successful result, while flatMap starts another asynchronous operation that depends on the first result. This creates a sequential data dependency without blocking the current thread.

For example, loading an account and then its orders can be expressed as:

def accountWithOrders(id: Long): Future[AccountWithOrders] =
  accountRepository.find(id).flatMap {
    case Some(account) =>
      orderRepository.findForAccount(id).map { orders =>
        AccountWithOrders(account, orders)
      }
    case None =>
      Future.failed(new NoSuchElementException(s"Account $id not found"))
  }

The first query completes before the second begins, yet no actor needs to call Await.result. The callback registered by flatMap is scheduled when the first future completes, allowing the thread to process other work in the meantime.

This style also keeps business rules visible. A missing account becomes a deliberate failure, rather than an accidental null value passed into a later query. For independent queries, use Future.zip or a for comprehension carefully, rather than creating an artificial sequence.

Build An Async Database Boundary

The repository should expose futures instead of leaking database sessions, statements, or driver-specific callbacks into actors. A simplified interface might look like this:

trait AccountRepository {
  def find(id: Long): Future[Option[Account]]
  def findWithOrders(id: Long): Future[AccountWithOrders]
}

With a genuinely asynchronous driver, the returned Future completes when the database response arrives. Libraries such as Slick can model database actions as DBIO, while an execution method returns a future. Other systems provide native asynchronous APIs based on Netty or reactive streams.

A common mistake is to write:

Future {
  blockingJdbcCall()
}

This is still blocking database work. It can be acceptable as a containment strategy when the Future uses a dedicated database dispatcher, but it should not run on Akka’s default dispatcher. The distinction should be documented in the repository implementation so callers know what concurrency guarantees they receive.

If a service stores uploaded vector assets, database work may sit beside image processing and metadata validation. Keeping those operations in separate asynchronous boundaries prevents an expensive SVG transformation from delaying unrelated account queries; practical techniques such as removing unused groups can also reduce the payload handled by downstream services.

Chain Queries Safely

A for comprehension often makes a longer chain easier to read:

def loadDashboard(userId: Long): Future[Dashboard] =
  for {
    user    <- userRepository.find(userId)
    profile <- profileRepository.find(user.profileId)
    alerts  <- alertRepository.recentFor(user.id)
  } yield Dashboard(user, profile, alerts)

Each line waits asynchronously for the previous future. If any future fails, the remaining steps are skipped and the resulting future is failed. This behaviour is useful when later queries have no meaning without earlier data.

When a missing record is expected, represent it explicitly with Option or a domain error. Avoid calling .get inside a future chain. A repository can convert an empty result into a typed failure:

userRepository.find(userId).flatMap {
  case Some(user) => Future.successful(user)
  case None       => Future.failed(UserNotFound(userId))
}

For independent reads, parallel execution can reduce latency:

val settingsF = settingsRepository.findFor(userId)
val messagesF = messageRepository.unreadFor(userId)

settingsF.zip(messagesF).map {
  case (settings, messages) => DashboardData(settings, messages)
}

Parallel queries should still respect database connection-pool limits. Starting hundreds of futures at once does not create hundreds of useful database connections; it creates a queue that may increase latency.

Keep Akka Dispatchers Healthy

Akka actors should receive messages, initiate asynchronous work, and handle completed results. They should not wait synchronously for a database response. In a typed Akka actor, a command can trigger a future and map its result back into a self-message:

case LoadAccount(id, replyTo) =>
  context.pipeToSelf(repository.find(id)) {
    case Success(result) => AccountLoaded(result, replyTo)
    case Failure(error)  => AccountLoadFailed(error, replyTo)
  }

pipeToSelf keeps the actor’s state changes inside its normal message-processing model. In classic Akka, pipeTo(actorRef) offers a similar pattern, although care is needed to preserve the original sender and avoid unsafe mutable state in callbacks.

Configure a dedicated dispatcher for blocking JDBC or file operations:

database-dispatcher {
  type = Dispatcher
  executor = "thread-pool-executor"
  thread-pool-executor {
    fixed-pool-size = 16
  }
}

The pool size should reflect the database’s capacity, not the number of CPU cores alone. A Sydney service deployed on modest cloud instances may need fewer threads than a large Melbourne deployment, while connection limits, query duration, and traffic patterns should guide the final setting.

Handle Errors And Timeouts

Failures should be handled at the boundary where the application can make a useful decision. recover can translate a technical exception into a domain response, while recoverWith can perform a fallback operation:

repository.find(id)
  .map(AccountView.from)
  .recover {
    case _: TimeoutException => AccountView.unavailable(id)
  }

Do not use broad recovery that turns every programming error into a successful response. A malformed query, serialisation bug, and temporary database outage have different operational meanings. Log the exception with a correlation ID, retain useful context, and expose a safe error to the caller.

Timeouts should exist at several levels: the database client, the future composition, and the HTTP or message protocol. Akka’s after can provide a deadline, although cancellation is not automatic:

val timed = Future.firstCompletedOf(
  Seq(repository.find(id), timeoutFuture)
)

A timeout future may stop the caller from waiting, but the database operation could still be running. Prefer driver-level cancellation where available, and ensure connection pools are not exhausted by abandoned work. Australian services also need to consider the Privacy Act 1988 when logs contain personal information; error messages should avoid dumping customer records into shared log platforms.

Compare Async Database Approaches

The right approach depends on the database driver, legacy constraints, and expected load. “Non-blocking” should describe the full path from the actor to the database response, rather than only the syntax used by the caller.

Approach Actor dispatcher impact Best use Main risk
Native async database driver Low when configured correctly High-concurrency services Driver complexity
Slick DBIO executed to Future Controlled through database execution context Scala applications using relational databases Misconfigured execution context
JDBC wrapped in a dedicated dispatcher Default dispatcher remains responsive Legacy systems and gradual migrations Threads remain blocked
Await.result inside an actor Severe Almost never appropriate Starvation and cascading timeouts
Akka Streams database stage Backpressure-aware Large result sets and pipelines More moving parts

For large result sets, streaming is usually safer than returning one large future containing every row. Akka Streams can apply backpressure and control how quickly records are read, transformed, and written. The same principle applies to asset pipelines where precise SVG sizing matters; a guide to using SVG viewBox illustrates how explicit boundaries prevent unexpected output dimensions.

Recommendations For Production Code

Treat future composition as part of the service’s architecture, not merely a convenient syntax. Establish clear ownership for execution contexts, connection pools, retries, and timeouts before production traffic exposes hidden coupling.

  • Return Future or a streaming type from repository methods, rather than database session objects.
  • Use flatMap for dependent queries and zip for independent queries.
  • Keep blocking JDBC calls on a dedicated, bounded dispatcher.
  • Add database, request, and overall operation timeouts with observable metrics.
  • Avoid automatic retries for non-idempotent writes unless the operation has a safe idempotency key.
  • Preserve typed domain errors and redact personal information from logs.
  • Test dispatcher starvation, connection-pool exhaustion, and partial database outages.

Retries deserve particular care in Australian online retail and payment integrations, where a customer may refresh after a slow mobile connection and accidentally submit the same action twice. A future chain can be technically correct while the business operation remains unsafe to repeat.

Operational tests should measure queue time, query latency, active connections, and failed futures. A service that responds quickly during a quiet local morning may behave very differently during a national campaign or evening traffic peak across Perth, Adelaide, and the eastern states.

Use these patterns to refactor one repository method first, then trace its dispatcher, connection pool, timeout, and failure path under load. Once that path is observable and predictable, extend the same Future-chaining discipline across the rest of the Akka service.


cat ~/interests.json

KeyValue
editorTerminal-first workflow
osMac OS X / Unix
vcsGit, distributed version control
deployCapistrano, cron automation
graphicsSVG, Adobe Illustrator troubleshooting
networkingIP validation, SSH, VPN

git log --oneline --reverse

2013-10-30

Solving SVG import issues in Adobe Illustrator CS6 and CC

When importing an SVG into Illustrator, the operation fails with an unknown error [CANT]. A workaround for this Adobe-side bug.

2013

Solving NDK build issues on OS X

Troubleshooting native development kit compilation problems on Mac OS X.

2013

Programmatically adding PHP generated TypoScript to the backend configuration

Integrating dynamically generated TypoScript into Typo3 backend setups using PHP.

2013

ArgumentError: Could not parse PKey: no start line

Debugging an SSH key parsing error encountered during deployment.

2011-08-04

Validating IP-Addresses in PHP

Using PHP filter functions with flags like FILTER_FLAG_IPV4 and FILTER_FLAG_IPV6, and understanding how filter_var handles reserved IP addresses.

2011-07-09

Cocoa: Using NSTrackingArea

A short tutorial on using Cocoa's NSTrackingArea to capture mouseEntered and mouseExited events.


cat ~/contact.txt

github: github.com/x3ro
stackoverflow: x3ro
coderwall: coderwall.com/x3ro
twitter: @x3rames