2016-02-01 24 views
15

Sto lavorando a un'applicazione Scala con Postgres 9.3 e Slick 3.1.1. Sto ottenendo Null Pointer Exception sul driver slick quando più query vengono eseguite contemporaneamente.NullPointerException sull'esecuzione di query simultanee tramite Slick

Ecco il mio codice semplificato. Sto creando più attori che chiameranno lo stesso metodo per interrogare dal database.

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)) 
} 

Quando eseguire il codice precedente, ottengo l'errore come di seguito:

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) 

L'attore richiamare lo stesso metodo ogni 10 secondi. Tuttavia, Ricevo questo errore solo per la prima volta. Dopo che le query sono state eseguite correttamente. Non sono in grado di capire perché questo sta accadendo. In questo esempio di esempio, ci sono solo alcune semplici operazioni di lettura. Ma nel mio caso reale, poiché la query non funziona, alcuni dati si perdono senza l'elaborazione corretta. Questo errore ha a che fare con il pool di connessioni?

+2

dare uno sguardo qui: https://github.com/slick/slick/issues/1400 – Dima

+2

Grazie @Dima. Ho applicato il pooling HikariCP e il problema sembra essere stato risolto per ora. –

+0

No .. Ancora lo stesso errore anche dopo l'utilizzo di Hikari. :( –

risposta

0

Proprio la condivisione delle informazioni per chiunque altro di fronte a questo problema.

C'è stato un bug con Slick stesso. È stato segnalato here. Git user, mustajavi ha risolto questo problema ed è stato unito all'ultima filiale di Slick. Con l'ultimo aggiornamento 3.1.1, il problema è risolto per me.

Collegamenti in GitHub:

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

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

+0

Sto ancora ricevendo l'errore in 3.1.1 –

3

Penso che tu abbia trovato il problema this. Provare a utilizzare val pigro per db in modo che inizializza solo una volta:

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) 
} 
+0

hmm, dato che sta accedendo a 'db' da più thread, probabilmente anche la creazione del database dovrebbe essere sincronizzata? –

+0

beh, lo stesso slick gestisce la sincronizzazione, in particolare tramite il costrutto db.run() – vitalii

+0

C'è stato un bug con Slick stesso. Questo è stato corretto e unito a Slick 3.1.1 due giorni fa, ecco il link https://github.com/slick/slick/pull/1401 –