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.
ls -la ~/languages/
- drwxr-xr-x
- ▶ PHP
- ▶ Ruby
- ▶ Scala
- ▶ C#
- ▶ JavaScript
- ▶ Objective-C
- ▶ Shell Scripting
ls -la ~/toolchain/
- drwxr-xr-x
- ▶ Typo3
- ▶ Akka
- ▶ Capistrano
- ▶ Git
- ▶ MAMP
- ▶ Adobe Illustrator
- ▶ NSTrackingArea (Cocoa)
uname -a
- 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
Futureor a streaming type from repository methods, rather than database session objects. - Use
flatMapfor dependent queries andzipfor 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
| Key | Value |
|---|---|
| editor | Terminal-first workflow |
| os | Mac OS X / Unix |
| vcs | Git, distributed version control |
| deploy | Capistrano, cron automation |
| graphics | SVG, Adobe Illustrator troubleshooting |
| networking | IP validation, SSH, VPN |
git log --oneline --reverse
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.
Solving NDK build issues on OS X
Troubleshooting native development kit compilation problems on Mac OS X.
Programmatically adding PHP generated TypoScript to the backend configuration
Integrating dynamically generated TypoScript into Typo3 backend setups using PHP.
ArgumentError: Could not parse PKey: no start line
Debugging an SSH key parsing error encountered during deployment.
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.
Cocoa: Using NSTrackingArea
A short tutorial on using Cocoa's NSTrackingArea to capture mouseEntered and mouseExited events.
cat ~/contact.txt