Extending Akka

Akka extensions can be used for almost anything, they provide a way to create an instance of a class only once for the whole ActorSystem and be able to access it from anywhere. Akka features such as Cluster, Serialization and Sharding are all Akka extensions. Below is the use-case of managing an expensive database connection pool and accessing it from various places in your application.

You can choose to have your Extension loaded on-demand or at ActorSystem creation time through the Akka configuration. Details on how to make that happens are below, in the Loading from Configuration section.

Warning

Since an extension is a way to hook into Akka itself, the implementor of the extension needs to ensure the thread safety and that it is non-blocking.

Building an extension

Let’s build an extension to manage a shared database connection pool.

Scala
sourceclass ExpensiveDatabaseConnection {
  def executeQuery(query: String): Future[Any] = ???
}
Java
sourcepublic class ExpensiveDatabaseConnection {
  public CompletionStage<Object> executeQuery(String query) {
    throw new RuntimeException("I should do a database query");
  }
  // ...
}

First create an ExtensionExtension, this will be created only once per ActorSystem:

Scala
sourceclass DatabasePool(system: ActorSystem[_]) extends Extension {
  // database configuration can be loaded from config
  // from the actor system
  private val _connection = new ExpensiveDatabaseConnection()

  def connection(): ExpensiveDatabaseConnection = _connection
}
Java
sourcepublic class DatabaseConnectionPool implements Extension {

  private final ExpensiveDatabaseConnection _connection;

  private DatabaseConnectionPool(ActorSystem<?> system) {
    // database configuration can be loaded from config
    // from the actor system
    _connection = new ExpensiveDatabaseConnection();
  }

  public ExpensiveDatabaseConnection connection() {
    return _connection;
  }
}

This is the public API of your extension. Internally in this example we instantiate our expensive database connection.

Then create an ExtensionIdExtensionId to identify the extension. A good convention is to let the companion object of the Extension be the ExtensionId.A good convention is to define the ExtensionId as a static inner class of the Extension.

Scala
sourceobject DatabasePool extends ExtensionId[DatabasePool] {
  // will only be called once
  def createExtension(system: ActorSystem[_]): DatabasePool = new DatabasePool(system)

  // Java API
  def get(system: ActorSystem[_]): DatabasePool = apply(system)
}
Java
sourcepublic static class Id extends ExtensionId<DatabaseConnectionPool> {

  private static final Id instance = new Id();

  private Id() {}

  // called once per ActorSystem
  @Override
  public DatabaseConnectionPool createExtension(ActorSystem<?> system) {
    return new DatabaseConnectionPool(system);
  }

  public static DatabaseConnectionPool get(ActorSystem<?> system) {
    return instance.apply(system);
  }
}

Then finally to use the extension it can be looked up:

Scala
sourceBehaviors.setup[Any] { ctx =>
  DatabasePool(ctx.system).connection().executeQuery("insert into...")
  initialBehavior
}
Java
sourceBehaviors.setup(
    (context) -> {
      DatabaseConnectionPool.Id.get(context.getSystem())
          .connection()
          .executeQuery("insert into...");
      return initialBehavior();
    });

The DatabaseConnectionPool can be looked up in this way any number of times and it will return the same instance.

Loading from configuration

Loading an extension from configuration is optional. It is an optimization and can be used to eagerly load the extension when the ActorSystem is started. If not done from configuration, the extension is instantiated and registered the first time it is accessed.

To be able to load extensions from your Akka configuration you must add FQCNs of implementations of the ExtensionId in the akka.actor.typed.extensions section of the config you provide to your ActorSystem.

Scala
sourceakka.actor.typed.extensions = ["docs.akka.extensions.DatabasePool"]
Java
ruby akka.actor.typed { extensions = ["jdocs.akka.extensions.ExtensionDocTest$DatabaseConnectionPool$Id"] }
Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.