Skip to content

Commit

Permalink
Merge branch 'fail_fast_on_apache_pool_reserve'
Browse files Browse the repository at this point in the history
  • Loading branch information
Ed Ceaser committed Feb 17, 2010
2 parents 2ace809 + 7985fef commit 97fbcd2
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.twitter.querulous.database

import java.util.concurrent.{TimeoutException => JTimeoutException, _}
import com.twitter.xrayspecs.Duration

class TimingOutDatabaseFactory(databaseFactory: DatabaseFactory, poolSize: Int, timeout: Duration) extends DatabaseFactory {
def apply(dbhosts: List[String], dbname: String, username: String, password: String): Database = {
new TimingOutDatabase(databaseFactory(dbhosts, dbname, username, password), poolSize, timeout)
}
}

class TimingOutDatabase(database: Database, poolSize: Int, timeout: Duration) extends Database {
private val executor = new ThreadPoolExecutor(poolSize, poolSize, 0, TimeUnit.SECONDS, new SynchronousQueue[Runnable])

override def open(): Connection = {
val future = new FutureTask(new Callable[Connection] {
def call = {
database.open()
}
})
try {
executor.execute(future)
future.get(timeout.inMillis, TimeUnit.MILLISECONDS)
} catch {
case e: JTimeoutException => throw new TimeoutException
case e: RejectedExecutionException => throw new TimeoutException
}
}

def close(connection: Connection) { database.close(connection) }
}
17 changes: 17 additions & 0 deletions src/test/scala/com/twitter/querulous/TestEvaluator.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.twitter.querulous

import net.lag.configgy.Configgy
import com.twitter.querulous.database.{MemoizingDatabaseFactory, ApachePoolingDatabaseFactory}
import com.twitter.querulous.query.SqlQueryFactory
import com.twitter.querulous.evaluator.StandardQueryEvaluatorFactory
import com.twitter.xrayspecs.Time
import com.twitter.xrayspecs.TimeConversions._

object TestEvaluator {
val testDatabaseFactory = new MemoizingDatabaseFactory(new ApachePoolingDatabaseFactory(10, 10, 1.second, 10.millis, false, 0.seconds))
val testQueryFactory = new SqlQueryFactory
val testEvaluatorFactory = new StandardQueryEvaluatorFactory(testDatabaseFactory, testQueryFactory)
}



Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.twitter.querulous.integration

import java.util.concurrent.CountDownLatch
import org.specs.Specification
import net.lag.configgy.Configgy
import com.twitter.xrayspecs.Time
Expand All @@ -10,12 +9,13 @@ import com.twitter.querulous.query._
import com.twitter.querulous.evaluator.{StandardQueryEvaluatorFactory, QueryEvaluator}

object QuerySpec extends Specification {
import TestEvaluator._
val config = Configgy.config.configMap("db")
val username = config("username")
val password = config("password")

"Query" should {
val queryEvaluator = QueryEvaluator("localhost", null, username, password)
val queryEvaluator = testEvaluatorFactory("localhost", null, username, password)

"with too many arguments" >> {
queryEvaluator.select("SELECT 1 FROM DUAL WHERE 1 IN (?)", 1, 2, 3) { r => 1 } must throwA[TooManyQueryParametersException]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@ import com.twitter.querulous.query.{SqlQueryFactory, TimingOutQueryFactory, SqlT
import com.twitter.querulous.evaluator.{StandardQueryEvaluatorFactory, QueryEvaluator}

object TimeoutSpec extends Specification {
import TestEvaluator._

val config = Configgy.config.configMap("db")
val username = config("username")
val password = config("password")
val timeout = 1.second
val queryFactory = new SqlQueryFactory
val timingOutQueryFactory = new TimingOutQueryFactory(queryFactory, timeout)
val databaseFactory = new ApachePoolingDatabaseFactory(1, 1, 1.second, 20.millis, false, 0.seconds)
val queryEvaluatorFactory = new StandardQueryEvaluatorFactory(databaseFactory, queryFactory)
val timingOutQueryEvaluatorFactory = new StandardQueryEvaluatorFactory(databaseFactory, timingOutQueryFactory)
val timingOutQueryFactory = new TimingOutQueryFactory(testQueryFactory, timeout)
val timingOutQueryEvaluatorFactory = new StandardQueryEvaluatorFactory(testDatabaseFactory, timingOutQueryFactory)

"Timeouts" should {
doBefore {
QueryEvaluator("localhost", null, username, password).execute("CREATE DATABASE IF NOT EXISTS db_test")
testEvaluatorFactory("localhost", null, username, password).execute("CREATE DATABASE IF NOT EXISTS db_test")
}

"honor timeouts" in {
val queryEvaluator1 = QueryEvaluator(List("localhost"), "db_test", username, password)
val queryEvaluator1 = testEvaluatorFactory(List("localhost"), "db_test", username, password)
val latch = new CountDownLatch(1)
val thread = new Thread() {
override def run() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import org.specs.Specification
import org.specs.mock.{ClassMocker, JMocker}

object QueryEvaluatorSpec extends Specification with JMocker with ClassMocker {
import TestEvaluator._

val config = Configgy.config.configMap("db")
val username = config("username")
val password = config("password")

"QueryEvaluator" should {
val queryEvaluator = QueryEvaluator("localhost", "db_test", username, password)
val rootQueryEvaluator = QueryEvaluator("localhost", null, username, password)
val queryEvaluator = testEvaluatorFactory("localhost", "db_test", username, password)
val rootQueryEvaluator = testEvaluatorFactory("localhost", null, username, password)
val queryFactory = new SqlQueryFactory

doBefore {
Expand Down Expand Up @@ -72,7 +74,7 @@ object QueryEvaluatorSpec extends Specification with JMocker with ClassMocker {

"fallback to a read slave" in {
// should always succeed if you have the right mysql driver.
val queryEvaluator = QueryEvaluator(List("localhost:12349", "localhost"), "db_test", username, password)
val queryEvaluator = testEvaluatorFactory(List("localhost:12349", "localhost"), "db_test", username, password)
queryEvaluator.selectOne("SELECT 1") { row => row.getInt(1) }.toList mustEqual List(1)
queryEvaluator.execute("CREATE TABLE foo (id INT)") must throwA[SQLException]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.twitter.querulous.unit

import java.util.concurrent.{CountDownLatch, TimeUnit}
import com.twitter.querulous.TimeoutException
import com.twitter.querulous.database.{Database, TimingOutDatabase}
import com.twitter.xrayspecs.Time
import com.twitter.xrayspecs.TimeConversions._
import org.specs.Specification
import org.specs.mock.{JMocker, ClassMocker}

object TimingOutDatabaseSpec extends Specification with JMocker with ClassMocker {
"TimingOutDatabaseSpec" should {
Time.reset()
val latch = new CountDownLatch(1)
val timeout = 1.second
val connection = mock[Connection]
val database = new Database {
def open() = {
latch.await(100.seconds.inMillis, TimeUnit.MILLISECONDS)
connection
}
def close(connection: Connection) = ()
}
val timingOutDatabase = new TimingOutDatabase(database, 1, timeout)

"timeout" in {
try {
val epsilon = 100.millis
var start = Time.now
timingOutDatabase.open() must throwA[TimeoutException]
var end = Time.now
(end.inMillis - start.inMillis) must beCloseTo(timeout.inMillis, epsilon.inMillis)

// subsequent failures should be instantaneous:
start = Time.now
timingOutDatabase.open() must throwA[TimeoutException]
end = Time.now
(end.inMillis - start.inMillis) must beCloseTo(0L, epsilon.inMillis)
} finally {
latch.countDown()
}
}

}
}


0 comments on commit 97fbcd2

Please sign in to comment.