Есть ли способ предварительно загрузить создание соединения в play-slick? - PullRequest
0 голосов
/ 06 июня 2019

Мой проект sbt 1.1.6 + playframework 2.7 + play-slick 3.0.0 + mysql-connector-java 5.1.34.
Я запустил приложение по

  • 1. sbt/dist
  • 2. unzip {package}
  • 3. ./bin/{project_name} -Dhttp.port=8080
    Я обнаружил, что при первом запросе требуется много времени для запроса данных из базы данных.

После того, как я добавил это <logger name="com.zaxxer.hikari" level="DEBUG"/> для входа в систему, я обнаружил, что Hikari создает соединение с БД после первого выполнения кода db.run().

Требуется некоторое время для инициализации соединения с БД, и иногда возникают ошибки, такие как тайм-аут getConnection или тайм-аут ожидания соединения.

Итак, есть ли способ предварительно загрузить создание соединения вместо создания соединения после первого вызова db.run().

Я создаю энергичный одноэлементный класс, который выполняет db.run() до успеха и запускается до запуска приложения.

Например

class InitialDB @Inject() (areaCodeInfoDAO: AreaCodeInfoDAO) (implicit exec: ExecutionContext) {

  def init(): Future[String] = {
    areaCodeInfoDAO.getProvince("440782")
  }

  RetryUtil.retry4Future(10)(init())

}
class ApiModule extends AbstractModule {

  override def configure(): Unit = {
    bind(classOf[InitialDB]).asEagerSingleton()
  }
}

Исправлена ​​эта проблема, но не элегантно.

Вот мой пример кода:

import javax.inject._

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}

import model.TableQueries
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
import play.api.mvc._
import slick.jdbc.JdbcProfile
import slick.jdbc.MySQLProfile.api._

class HomeController @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
    (cc: ControllerComponents)
    (implicit exec: ExecutionContext)
    extends AbstractController(cc)
    with HasDatabaseConfigProvider[JdbcProfile] {

  def index() = Action { implicit request: Request[AnyContent] =>
    val query = TableQueries.CODE_NAME_MAPPING
        .filter { info =>
          info.code === "test"
        }
        .map(_.name)
        .take(1)

    val result: Future[String] = db
        .run(query.result)
        .map(info => if (info.nonEmpty) info.head else "test")

    Ok(Await.result(result, Duration.Inf))
  }

}
import slick.lifted.TableQuery

object TableQueries {

  val CODE_NAME_MAPPING = TableQuery[CodeNameMapping]
}
import slick.jdbc.MySQLProfile.api._

# model
class CodeNameMapping(tag: Tag)
    extends Table[(String, String)](tag, "nydd_channel_info") {

  def code = column[String]("code", O.PrimaryKey)

  def name = column[String]("name")

  def * = (code, name)
}

Я добавляю <logger name="com.zaxxer.hikari" level="DEBUG"/> к logback.xml.

Журнал [debug] находится сразу после того, как я отправляю запрос на запрос базы данных, поэтому я думаю, что Hikari создает соединение с базой данных при первом выполнении кода db.run().

[info] play.api.Play - Application started (Prod) (no global state)
[info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000
[debug] c.z.h.HikariConfig - db - configuration:
[debug] c.z.h.HikariConfig - allowPoolSuspension.............false
[debug] c.z.h.HikariConfig - autoCommit......................true
[debug] c.z.h.HikariConfig - catalog.........................null
[debug] c.z.h.HikariConfig - connectionInitSql...............null
...