通过 cat.effect.IO 从 flatMapping 请求超时

问题描述

我正在尝试使用同样在 IO monad 中的 Map 来转换一些封装在 cat.effect.IO 中的数据。我在 blaze 服务器上使用 http4s,当我使用以下代码时,请求超时:

  def getScoresByUserId(userId: Int): IO[Response[IO]] = {
    implicit val formats = DefaultFormats + ShiftJsonSerializer() + RawShiftSerializer()
    implicit val shiftJsonReader = new Reader[ShiftJson] {
      def read(value: JValue): ShiftJson = value.extract[ShiftJson]
    }
    implicit val shiftJsonDec = jsonOf[IO,ShiftJson]

    // get the shifts
    var getDbShifts: IO[List[Shift]] = shiftModel.findByUserId(userId)

    // use the userRoleId to get the RoleId then get the tasks for this role
    val taskMap : IO[Map[String,Double]] = taskModel.findByUserId(userId).flatMap {
      case tskLst: List[Task] => IO(tskLst.map((task: Task) => (task.name -> task.standard)).toMap)
    }

    val traversed: IO[List[Shift]] = for {
      shifts <- getDbShifts
      traversed <- shifts.traverse((shift: Shift) => {
        val lstShiftJson: IO[List[ShiftJson]] = read[List[ShiftJson]](shift.roleTasks)
          .map((sj: ShiftJson) =>
            taskMap.flatMap((tm: Map[String,Double]) =>
              IO(ShiftJson(sj.name,sj.taskType,sj.label,sj.value.toString.toDouble / tm.get(sj.name).get)))
          ).sequence

        //TODO: this flatMap is bricking my request
        lstShiftJson.flatMap((sjLst: List[ShiftJson]) => {
          IO(Shift(shift.id,shift.shiftDate,shift.shiftStart,shift.shiftEnd,shift.lunchDuration,shift.shiftDuration,shift.breakOffProd,shift.systemDownOffProd,shift.meetingOffProd,shift.trainingOffProd,shift.projectOffProd,shift.miscOffProd,write[List[ShiftJson]](sjLst),shift.userRoleId,shift.isApproved,shift.score,shift.comments
          ))
        })
      })

    } yield traversed

  traversed.flatMap((sLst: List[Shift]) => Ok(write[List[Shift]](sLst)))
}

正如您所看到的 TODO 评论。我已将此方法缩小到 TODO 注释下方的平面图。如果我删除该 flatMap 并仅将“IO(shift)”返回给遍历的变量,则请求不会超时;但是,这对我没有多大帮助,因为我需要使用 lstShiftJson 变量,它具有我转换后的 json。

我的直觉告诉我我正在以某种方式滥用 IO monad,但我不太确定如何。

感谢您花时间阅读本文!

解决方法

因此,在 Luis 评论的指导下,我将代码重构为以下内容。我不认为它是最佳的(即最后的 flatMap 似乎没有必要,但我无法弄清楚如何删除它。但它是我所拥有的最好的。

  def getScoresByUserId(userId: Int): IO[Response[IO]] = {
    implicit val formats = DefaultFormats + ShiftJsonSerializer() + RawShiftSerializer()
    implicit val shiftJsonReader = new Reader[ShiftJson] {
      def read(value: JValue): ShiftJson = value.extract[ShiftJson]
    }
    implicit val shiftJsonDec = jsonOf[IO,ShiftJson]

    // FOR EACH SHIFT
    // - read the shift.roleTasks into a ShiftJson object
    // - divide each task value by the task.standard where task.name = shiftJson.name
    // - write the list of shiftJson back to a string
    val traversed = for {
      taskMap <- taskModel.findByUserId(userId).map((tList: List[Task]) => tList.map((task: Task) => (task.name -> task.standard)).toMap)
      shifts <- shiftModel.findByUserId(userId)
      traversed <- shifts.traverse((shift: Shift) => {
        val lstShiftJson: List[ShiftJson] = read[List[ShiftJson]](shift.roleTasks)
          .map((sj: ShiftJson) => ShiftJson(sj.name,sj.taskType,sj.label,sj.value.toString.toDouble / taskMap.get(sj.name).get ))
        shift.roleTasks = write[List[ShiftJson]](lstShiftJson)
        IO(shift)
      })
    } yield traversed

    traversed.flatMap((t: List[Shift]) => Ok(write[List[Shift]](t)))
  }
  1. Luis 提到将我的 List[Shift] 映射到 Map[String,Double] 是一个纯粹的操作,所以我们想使用 map 而不是 flatMap。
  2. 他提到我将来自数据库的每个操作都包装在 IO 中,这会导致大量的重新计算。 (包括数据库事务)
  3. 为了解决这个问题,我将所有数据库操作移到了 for 循环中,使用“

我确实认为必须有更好的方法来返回我的返回值。平面映射“遍历”变量以返回 IO monad 似乎是不必要的重新计算,所以请任何人纠正我。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...