In the first post in this series, we discussed some of the basic concepts of asynchronous programming and how they can be applied in Scala. In the second post, we explored Execution Contexts and a few advantages and disadvantages of some examples. In the third post, we explored concurrent programming in Scala using the Akka framework and “actor model”. In this post, we won’t actually cover any new async or concurrency patterns (though I do hope to cover Akka Streams soon). Instead, we will look at Extensions inside the Actor System.

What is an “Extension”?

Extensions, in the context of Akka, are essentially singleton objects which get saved/cached internally by an ActorSystem. An Extension is initialized once (per ActorSystem), and subsequent calls for it will use the saved object.

In the Scala world, you may declare top-level “objects” using syntax like:

object MySingleton {
  def computeValue(thing: String): Int = ...
}

And that is great for declaring pure functions which need to be re-used and don’t need any class-level state. They can even be used for defining functions which return futures:

object MySingleton {
  def computeValue(thing: String)(implicit ec: ExecutionContext): Future[Int] = ...
}

Using implicit parameters, you can accept any dependencies you need for that function to run. That’s fine for smaller use-cases where you only have one or two implicit parameters. Things start to get a bit messy when you require a bunch of implicits though, such as an ExecutionContext, a Config, a Materializer, an ActorSystem, a LoggingAdapter, etc. Wouldn’t it be easier if you could just keep all of those implicit dependencies stored at the top-level of the class instead of requiring them as parameters for each of the object’s functions?

Using the ActorSystem, you can register an “extension” (basically, an instance of a class), so long as that extension’s dependencies can all be derived from the ActorSystem. This allows you to store those dependencies inside of the extension, and the interface/methods for that extension become far easier to work with (generally not requiring any implicit parameters).

How do I create an Extension?

The general pattern for creating an Extension in Scala is to create an “extension” class, and a companion object.  The “extension” class is what ends up being the actual object that gets saved in the ActorSystem and is where you should declare all of the actual functionality. The companion object provides the “extension ID” which ensures only one instance of that extension’s ID gets registered. The code looks as follows:

import akka.actor.ActorSystem
import akka.actor.Extension
import akka.actor.ExtensionId
import akka.actor.ExtensionIdProvider
import akka.actor.ExtendedActorSystem
import scala.concurrent.ExecutionContext
class MyExtension(implicit system: ActorSystem) extends Extension 
{
  // Declare your extension’s functionality/interface here
  def computeValue(thing: String): Future[Int] = ...
}

object MyExtension extends ExtensionId[MyExtension] with ExtensionIdProvider {
  def lookup: MyExtension.type =
    this
  def createExtension(system: ExtendedActorSystem): MyExtension =
new MyExtension(system)
}

How do I use my Extension?

Using an Extension is quite simple; all you need is an ActorSystem. The `akka.actor.ExtensionId` trait provides a method `def apply(system: ActorSystem): {Extension}` which produces the instance of that Extension.  So in our previous example, we would simply do:

MyExtension(system).computeValue(“abcd”)

And that’s all it takes! The ActorSystem will handle registering `MyExtension` if it has not already been registered. Registering for the first time may incur a performance penalty, particularly for lengthy or blocking computations required during its initialization. Subsequent lookups should be very fast though.

What are some use-cases?

Certain Akka modules already use Akka Extensions under-the-hood (i.e. Akka-HTTP).

In my experience so far, I tend to use them in the following cases:

  • When my class or method interfaces require a long list of implicit parameters
  • When I only want one instance of that class to exist, and I want it to be easy to access

In Dropsource’s code-base, we use extensions in several different areas, but some common cases are:

  • AWS SDK Clients
    • Creating a client in the AWS SDK generally involves a lot of internal setup (setting up its own HTTP Client)
    • You ideally want to re-use the client multiple times.
    • The client needs to be shutdown before the JVM terminates
  • Kafka Interface
    • There are a variety of ways to connect to Kafka, but in general you only want one client
  • Couchbase/Database Interface
    • Like with the AWS SDKs, the Couchbase SDK sets up its own internal thread pool and needs to maintain active connections to the buckets
    • As such, you only want one client

Doesn’t Dependency Injection already solve this?

Yes, but for the listed use-cases, DI is a bit of a nightmare. Akka Extensions simplify the matter greatly.

Using Akka Extensions, you can “inject” your dependencies into the ActorSystem and feel confident that those extensions will live for the duration of the System.

Creating Akka Extensions is easy; just extend a few traits on top of your existing code and you’re most of the way there. Using Akka Extensions is even easier; you just need an ActorSystem somewhere in-scope.

There are no messy annotations with strange syntax and bizarre rules. It’s all just straightforward Scala code which accomplishes what boils down to the same goal.

It is worth noting, however, that Akka Extensions do not fulfill all of the duties of a Dependency Injection framework. A main duty of Dependency Injection is to provide the ability for one entity to provide an instance of some interface to another entity without the other having any idea what the internals of the instance are. Put another way: Akka Extensions isolate the control of the implementation of the Extension to the Extension itself, whereas a DI framework pulls the implementation from some external source.

Conclusion

Akka Extensions provide a simple mechanism for sharing singleton objects within members of an ActorSystem.  If your application is built on top of Akka (or Play or some other derivative), then consider using Akka Extensions for sharing singleton resources such as database clients, SDK clients, or even internal services.

Other posts in the Scala Series:
Gentle Introduction to Async Scala
Async Scala Part II
Async Scala Part III