gpt4 book ai didi

concurrency - 将 Hikari 事务器用于 Doobie 和 ZIO 时遇到死锁

转载 作者:行者123 更新时间:2023-12-04 11:22:30 25 4
gpt4 key购买 nike

我在 ZIO 应用程序中使用 Doobie,有时会出现死锁(应用程序完全卡住)。如果我仅在一个内核上运行我的应用程序,或者达到与数据库的最大并行连接数,就会发生这种情况。
我的代码看起来像:

def mkTransactor(cfg: DatabaseConfig): RManaged[Blocking, Transactor[Task]] =
ZIO.runtime[Blocking].toManaged_.flatMap { implicit rt =>
val connectEC = rt.platform.executor.asEC
val transactEC = rt.environment.get.blockingExecutor.asEC

HikariTransactor
.fromHikariConfig[Task](
hikari(cfg),
connectEC,
Blocker.liftExecutionContext(transactEC)
)
.toManaged
}

private def hikari(cfg: DatabaseConfig): HikariConfig = {
val config = new com.zaxxer.hikari.HikariConfig

config.setJdbcUrl(cfg.url)
config.setSchema(cfg.schema)
config.setUsername(cfg.user)
config.setPassword(cfg.pass)

config
}
或者,我在 Hikari ( config.setLeakDetectionThreshold(10000L) ) 上设置了泄漏检测参数,但出现泄漏错误并不是由于处理数据库查询所花费的时间。

最佳答案

Doobie 文档中有关于执行上下文和对每个上下文的期望的很好的解释:https://tpolecat.github.io/doobie/docs/14-Managing-Connections.html#about-transactors
根据文档,“等待连接到数据库的执行上下文”(问题中的 connectEC)应该是有界的。ZIO ,默认情况下,只有两个线程池:

  • zio-default-async – 有界,
  • zio-default-blocking – 无界

  • 所以很自然地相信我们应该使用 zio-default-async因为它是有界的。
    不幸的是, zio-default-async假设它的操作永远不会阻塞。这非常重要,因为它是 ZIO 使用的执行上下文。解释器(它的运行时)运行。如果你阻止它,你实际上可以阻止 ZIO 的评估进程。程序。当只有一个内核可用时,这种情况会更频繁地发生。
    问题是等待 DB 连接的执行上下文意味着阻塞,等待 Hikari 连接池中的可用空间。所以我们不应该使用 zio-default-async对于这个执行上下文。
    下一个问题是:为 connectEC创建一个新的线程池和相应的执行上下文是否有意义? ?没有什么禁止您这样做,但可能没有必要,原因有以下三个:
  • 您希望避免创建线程池,特别是因为您可能已经从 Web 框架、数据库连接池、调度程序等创建了多个线程池。每个线程池都有其成本。一些例子是:
  • 更多管理 jvm JVM
  • 消耗更多操作系统资源
  • 在线程之间切换,这部分在性能方面很昂贵
  • 使您的应用程序运行时更难理解(复杂的线程转储等)

  • ZIO线程池人体工程学开始针对它们的使用进行很好的优化
  • 在一天结束时,您将不得不在某处管理您的超时,并且连接不是系统中最有可能拥有足够信息来知道它应该等待多长时间的部分:不同的交互(即,在应用程序的外部部分,更接近使用点)可能需要不同的超时/重试逻辑。

  • 综上所述,我们发现了一个在生产环境中运行的应用程序中运行良好的配置:
    // zio.interop.catz._ provides a `zioContextShift`

    val xa = (for {
    // our transaction EC: wait for aquire/release connections, must accept blocking operations
    te <- ZIO.access[Blocking](_.get.blockingExecutor.asEC)
    } yield {
    Transactor.fromDataSource[Task](datasource, te, Blocker.liftExecutionContext(te))
    }).provide(ZioRuntime.environment).runNow

    def transactTask[T](query: Transactor[Task] => Task[T]): Task[T] = {
    query(xa)
    }
    我绘制了 Doobie 和 ZIO 执行上下文如何相互映射的图: https://docs.google.com/drawings/d/1aJAkH6VFjX3ENu7gYUDK-qqOf9-AQI971EQ4sqhi2IY
    更新 :我在这里创建了一个存储库,其中包含 3 个该模式使用示例(混合应用程序、纯应用程序、ZLayer 应用程序): https://github.com/fanf/test-zio-doobie
    欢迎任何反馈。

    关于concurrency - 将 Hikari 事务器用于 Doobie 和 ZIO 时遇到死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64371510/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com