Search code examples
scalaakkaslickakka-grpc

Akka gRPC + Slick application causes "IllegalStateException: Cannot initialize ExecutionContext; AsyncExecutor already shut down"


I try to develop gRPC server with Akka-gRPC and Slick. I also use Airframe for DI.

Source code is here

The issue is that it cause failure if it receive request when execute as gRPC server. If it doesn't start as a gRPC server, but just reads resources from the database, the process succeeds.

What is the difference?

At Follows, It read object from database with slick. ...Component is airframe object. It will use by main module.

trait UserRepository {
  def getUser: Future[Seq[Tables.UsersRow]]
}

class UserRepositoryImpl(val profile: JdbcProfile, val db: JdbcProfile#Backend#Database) extends UserRepository {
  import profile.api._
  def getUser: Future[Seq[Tables.UsersRow]] = db.run(Tables.Users.result)
}

trait UserResolveService {
  private val repository = bind[UserRepository]
  def getAll: Future[Seq[Tables.UsersRow]] =
    repository.getUser
}


object userServiceComponent {
  val design = newDesign
    .bind[UserResolveService]
    .toSingleton
}

Follows is gRPC Server source code.

trait UserServiceImpl extends UserService {

  private val userResolveService            = bind[UserResolveService]
  private val system: ActorSystem           = bind[ActorSystem]
  implicit val ec: ExecutionContextExecutor = system.dispatcher

  override def getAll(in: GetUserListRequest): Future[GetUserListResponse] = {

    userResolveService.getAll.map(us =>
      GetUserListResponse(
        us.map(u =>
          myapp.proto.user.User(
            1,
            "[email protected]",
            "t_horikoshi",
            myapp.proto.user.User.UserRole.Admin
          )
        )
      )
    )
  }

}

trait GRPCServer {

  private val userServiceImpl      = bind[UserServiceImpl]
  implicit val system: ActorSystem = bind[ActorSystem]

  def run(): Future[Http.ServerBinding] = {

    implicit def ec: ExecutionContext = system.dispatcher
    val service: PartialFunction[HttpRequest, Future[HttpResponse]] =
      UserServiceHandler.partial(userServiceImpl)

    val reflection: PartialFunction[HttpRequest, Future[HttpResponse]] =
      ServerReflection.partial(List(UserService))

    // Akka HTTP 10.1 requires adapters to accept the new actors APIs
    val bound = Http().bindAndHandleAsync(
      ServiceHandler.concatOrNotFound(service, reflection),
      interface = "127.0.0.1",
      port = 8080,
      settings = ServerSettings(system)
    )

    bound.onComplete {
      case Success(binding) =>
        system.log.info(
          s"gRPC Server online at http://${binding.localAddress.getHostName}:${binding.localAddress.getPort}/"
        )
      case Failure(ex) =>
        system.log.error(ex, "occurred error")
    }

    bound
  }
}

object grpcComponent {
  val design = newDesign
    .bind[UserServiceImpl]
    .toSingleton
    .bind[GRPCServer]
    .toSingleton
}

Follows is main module.

object Main extends App {

  val conf = ConfigFactory
    .parseString("akka.http.server.preview.enable-http2 = on")
    .withFallback(ConfigFactory.defaultApplication())
  val system = ActorSystem("GRPCServer", conf)

  val dbConfig: DatabaseConfig[JdbcProfile] =
    DatabaseConfig.forConfig[JdbcProfile](path = "mydb")

  val design = newDesign
    .bind[JdbcProfile]
    .toInstance(dbConfig.profile)
    .bind[JdbcProfile#Backend#Database]
    .toInstance(dbConfig.db)
    .bind[UserRepository]
    .to[UserRepositoryImpl]
    .bind[ActorSystem]
    .toInstance(system)
    .add(userServiceComponent.design)
    .add(grpcComponent.design)

  design.withSession(s =>
    // Await.result(s.build[UserResolveService].getUser, Duration.Inf)) // success
    // Await.result(s.build[UserServiceImpl].getAll(GetUserListRequest()), Duration.Inf)) // success
    s.build[GRPCServer].run() // cause IllegalStateException when reciece request.

  )
}

When UserResolveService and UserServiceImpl are called directly, the process of loading an object from the database is successful.

However, when running the application as a gRPC Server, an error occurs when a request is received.

enter image description here

Though I was thinking all day, I couldn't resolve... Will you please help me to resolve.


Solution

  • It resolved. if execute async process, It has to start gRPC server with newSession. I fix like that.