Knows Not Much
Knows Not Much

Reputation: 31546

Race condition in Slick Code

I have written this slick DAO and its unit test in specs2.

My code has race conditions. When I run the same tests, I get different outputs.

The race conditions exist even though in both the functions I do Await.result(future, Duration.Inf)

DAO

package com.example
import slick.backend.DatabasePublisher
import slick.driver.H2Driver.api._

import scala.concurrent.ExecutionContext.Implicits.global
import slick.jdbc.meta._
import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.concurrent.duration._

case class Person(id: Int, firstname: String, lastname: String)

class People(tag: Tag) extends Table[Person](tag, "PEOPLE") {
  def id = column[Int]("PERSON_ID", O.PrimaryKey)
  def firstname = column[String]("PERSON_FIRST_NAME")
  def lastname = column[String]("PERSON_LAST_NAME")
  def * = (id, firstname, lastname) <> (Person.tupled, Person.unapply _)
}

object PersonDAO {

  private def createList(numRows: Int) : List[Person] = {
    def recFunc(counter: Int, result: List[Person]) : List[Person] = {
      counter match {
        case x if x <= numRows => recFunc(counter + 1, Person(counter, "test" + counter, "user" + counter) :: result)
        case _ => result
      }
    }
    recFunc(1, List[Person]())
  }

  val db = Database.forConfig("test1")
  val people = TableQuery[People]

  def createAndPopulate(numRows: Int) = {
    val action1 = people.schema.create
    val action2 = people ++= Seq(createList(numRows) : _* )
    val combined = db.run(action1 andThen action2)

    val future1 = combined.map { result =>
      result map {x => 
        println(s"number of rows inserted $x")
        x
      }
    }
    Await.result(future1, Duration.Inf).getOrElse(0)
  }

  def printAll() = {
    val a = people.result
    val b = db.run(a)
    val y = b map { result => 
      result map {x => x}
    }
    val z = Await.result(y, Duration.Inf)
    println(z)
    println(z.length)
    z
  }
}

Unit Test

import org.specs2.mutable._
import com.example._

class HelloSpec extends Specification {
  "This usecase " should {
    "should insert rows " in {
      val x = PersonDAO.createAndPopulate(100)
      x === 100
    }
  }

  "This usecase " should {
    "return 100 rows" in {
      val x = PersonDAO.printAll()
      val y = PersonDAO.printAll()      
      y.length === 100
    }
  }
}

When I run this same code using activator test I see 2 different types of outputs on different runs

  1. sometimes the code gets exception

    number of rows inserted 100 [info] HelloSpec [info] [info] This usecase should [info] + should insert rows [info] [info] This usecase should [info] ! return 100 rows [error] JdbcSQLException: : Table PEOPLE not found; SQL statement: [error] select x2."PERSON_ID", x2."PERSON_FIRST_NAME", x2."PERSON_LAST_NAME" from "PEOPLE" x2 [42S02-60] (Message.java:84) [error] org.h2.message.Message.getSQLException(Message.java:84) [error] org.h2.message.Message.getSQLException(Message.java:88) [error] org.h2.message.Message.getSQLException(Message.java:66)

  2. Sometimes the 1st function call returns 0 rows and the 2nd function call returns 100 values

    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. number of rows inserted 100 Vector() 0 Vector(Person(100,test100,user100), Person(99,test99,user99), Person(98,test98,user98), Person(97,test97,user97), Person(96,test96,user96), Person(95,test95,user95), Person(94,test94,user94), Person(93,test93,user93), Person(92,test92,user92), Person(91,test91,user91), Person(90,test90,user90), Person(89,test89,user89), Person(88,test88,user88), Person(87,test87,user87), Person

I don't understand why does my code have these race conditions because I block on future in each method.

Upvotes: 0

Views: 349

Answers (1)

Biswanath
Biswanath

Reputation: 9185

Your assumption that two test cases should run serial, one after the other is not right. The test cases are running parallel. Just use sequential to verify that thats the case.

Upvotes: 1

Related Questions