NullPointerException при выполнении параллельных запросов с использованием Slick

Я работаю над приложением Scala с Postgres 9.3 и Slick 3.1.1. Я получаю исключение Null Pointer на slick-драйвере, когда несколько запросов выполняются одновременно.

Вот мой упрощенный код. Я создаю несколько участников, которые будут вызывать тот же метод для запроса из базы данных.

package com.app.repo

import java.sql.Timestamp

import akka.actor.{Actor, ActorSystem, Props}
import slick.driver.PostgresDriver
import slick.driver.PostgresDriver.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.FiniteDuration
import scala.util.{Failure, Success}

case class SampleData(id: Long, name: String, createDate: java.sql.Timestamp)

object Tables extends {
  val profile = PostgresDriver
} with Tables

trait Tables {
  val profile: PostgresDriver

  import profile.api._

  class SampleDataTable(_tableTag: Tag) extends Table[SampleData](_tableTag, Some("processing"), "SampleData") {
    def * = (id, name, createDate) <>(SampleData.tupled, SampleData.unapply)

    def ? = (Rep.Some(id), Rep.Some(name), Rep.Some(createDate)).shaped.<>({ r => import r._; _1.map(_ => SampleData.tupled((_1.get, _2.get, _3.get))) }, (_: Any) => throw new Exception("Inserting into ? projection not supported."))

    val id: Rep[Long] = column[Long]("SampleId", O.AutoInc, O.PrimaryKey)
    val name: Rep[String] = column[String]("Name")
    val createDate: Rep[java.sql.Timestamp] = column[java.sql.Timestamp]("CreateDate")
  }

  lazy val sampleDataTable = new TableQuery(tag => new SampleDataTable(tag))
}

class SampleQueryingActor(delay: FiniteDuration, duration: FiniteDuration) extends Actor {

  import scala.concurrent.duration._

  override def preStart() = {
    context.system.scheduler.schedule(0.second, duration, self, "tick")
  }

  override def receive: Receive = {
    case "tick" => {
      println("tick received.. ")
      //val range = 1 until 1000
      RepositoryImpl.reader.onComplete({
        case Success(r)  => println(s"got sum as ${r.getOrElse(0)}")
        case Failure(ex) => ex.printStackTrace()
      })

    }
  }
}

object DriverHelper {
  val user = "postgres"
  val url = "jdbc:postgresql://192.168.1.50:5432/MyDatabase"
  val password = "password"
  val jdbcDriver = "org.postgresql.Driver"
  val db: PostgresDriver.backend.DatabaseDef = Database.forURL(url, user = user, password = password, driver = jdbcDriver)
}

object RepositoryImpl {
  val db: PostgresDriver.backend.DatabaseDef = DriverHelper.db

  val now = new Timestamp(System.currentTimeMillis())

  def reader = {
    db.run(Tables.sampleDataTable.filter(_.createDate > now).map(_.id).sum.result)
  }

  def insertBatchRecords(list: List[SampleData]) = {
    db.run(Tables.sampleDataTable ++= list)
  }

}

object PGConnectionTester extends App {

  import scala.concurrent.duration._

  val sys = ActorSystem("sys")
  sys.actorOf(Props(classOf[SampleQueryingActor], 1.seconds, 10.seconds))
  sys.actorOf(Props(classOf[SampleQueryingActor], 1.seconds, 10.seconds))
  sys.actorOf(Props(classOf[SampleQueryingActor], 1.seconds, 10.seconds))
}

Когда я выполняю вышеуказанный код, я получаю ошибку, как показано ниже:

java.lang.NullPointerException
    at slick.jdbc.DriverDataSource.getConnection(DriverDataSource.scala:98)
    at slick.jdbc.DataSourceJdbcDataSource.createConnection(JdbcDataSource.scala:64)
    at slick.jdbc.JdbcBackend$BaseSession.conn$lzycompute(JdbcBackend.scala:415)
    at slick.jdbc.JdbcBackend$BaseSession.conn(JdbcBackend.scala:414)
    at slick.jdbc.JdbcBackend$SessionDef$class.prepareStatement(JdbcBackend.scala:297)
    at slick.jdbc.JdbcBackend$BaseSession.prepareStatement(JdbcBackend.scala:407)
    at slick.jdbc.StatementInvoker.results(StatementInvoker.scala:33)
    at slick.jdbc.StatementInvoker.iteratorTo(StatementInvoker.scala:22)
    at slick.jdbc.Invoker$class.first(Invoker.scala:31)
    at slick.jdbc.StatementInvoker.first(StatementInvoker.scala:16)
    at slick.driver.JdbcActionComponent$QueryActionExtensionMethodsImpl$$anon$3.run(JdbcActionComponent.scala:228)
    at slick.driver.JdbcActionComponent$SimpleJdbcDriverAction.run(JdbcActionComponent.scala:32)
    at slick.driver.JdbcActionComponent$SimpleJdbcDriverAction.run(JdbcActionComponent.scala:29)
    at slick.backend.DatabaseComponent$DatabaseDef$$anon$2.liftedTree1$1(DatabaseComponent.scala:237)
    at slick.backend.DatabaseComponent$DatabaseDef$$anon$2.run(DatabaseComponent.scala:237)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Актер будет вызывать один и тот же метод каждые 10 секунд. Однако Я получаю эту ошибку только в первый раз. После этого запросы выполняются правильно. Я не могу понять, почему это происходит. В этом примере есть только некоторые простые операции чтения. Но в моем фактическом случае, поскольку запрос терпит неудачу, некоторые данные теряются без правильной обработки. Является ли эта ошибка чем-то связана с пулом соединений?

Ответы

Ответ 1

Просто поделитесь информацией для всех, кто сталкивается с этой проблемой.

Была ошибка с самим Сликом. Сообщено здесь. Git пользователь, mustajavi исправил это и был объединен с последней ветвью Slick. С последним обновлением 3.1.1 проблема решена для меня.

Ссылки по теме в GitHub:

https://github.com/slick/slick/pull/1401

https://github.com/slick/slick/pull/1445

Ответ 2

Я думаю, вы нашли этот вопрос. Попытайтесь использовать lazy val для db, чтобы он только инициализировался один раз:

object DriverHelper {
  val user = "postgres"
  val url = "jdbc:postgresql://192.168.1.50:5432/MyDatabase"
  val password = "password"
  val jdbcDriver = "org.postgresql.Driver"
  lazy val db: PostgresDriver.backend.DatabaseDef = Database.forURL(url, user = user, password = password, driver = jdbcDriver)
}