[JSON-API] Configurable Hikari connection pool props (#11621)

* Changes to make certain hikari cp connection pool properties configurable via jdbc conf string

CHANGELOG_BEGIN
[JSON-API] Make certain Hikari cp connection pool properties configurable via jdbc conf string, the properties are listed below
poolSize -- specifies the max pool size for the database connection pool
minIdle -- specifies the min idle connections for database connection pool
connectionTimeout -- long value, specifies the connection timeout for database connection pool
idleTimeout -- long value, specifies the idle timeout for the database connection pool
CHANGELOG_END

* some missed changes for DbTriggerDao

* remove defaults for poolSize on JdbcConfig

* add constants for test defaults
This commit is contained in:
akshayshirahatti-da 2021-11-11 05:43:56 +00:00 committed by GitHub
parent cb7099b22d
commit e69a871e2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 121 additions and 41 deletions

View File

@ -5,6 +5,7 @@ package com.daml.http
import com.daml.dbutils
import OracleIntTest.defaultJdbcConfig
import com.daml.dbutils.ConnectionPool
import dbbackend.{DbStartupMode, JdbcConfig}
import dbbackend.OracleQueries.DisableContractPayloadIndexing
import com.daml.testing.oracle.OracleAroundAll
@ -33,6 +34,7 @@ object OracleIntTest {
user = user,
password = pwd,
tablePrefix = "some_nice_prefix_",
poolSize = ConnectionPool.PoolSize.Integration,
),
dbStartupMode = DbStartupMode.CreateOnly,
backendSpecificConf =

View File

@ -29,6 +29,7 @@ import scalaz.syntax.tag._
import scalaz.{-\/, EitherT, \/, \/-}
import Config.QueryStoreIndex
import com.daml.dbutils.ConnectionPool
import scala.concurrent.duration.{Duration, _}
import scala.concurrent.{Await, ExecutionContext, Future, Promise, TimeoutException}
@ -220,7 +221,13 @@ object Main extends StrictLogging {
import DbStartupMode._
val startupMode: DbStartupMode = if (retainData) CreateIfNeededAndStart else CreateAndStart
JdbcConfig(
dbutils.JdbcConfig("oracle.jdbc.OracleDriver", oracleJdbcUrl, user.name, user.pwd),
dbutils.JdbcConfig(
"oracle.jdbc.OracleDriver",
oracleJdbcUrl,
user.name,
user.pwd,
ConnectionPool.PoolSize.Production,
),
dbStartupMode = startupMode,
)
}
@ -240,7 +247,13 @@ object Main extends StrictLogging {
private def jsonApiJdbcConfig(c: PostgresDatabase): JdbcConfig =
JdbcConfig(
dbutils
.JdbcConfig(driver = "org.postgresql.Driver", url = c.url, user = "test", password = ""),
.JdbcConfig(
driver = "org.postgresql.Driver",
url = c.url,
user = "test",
password = "",
ConnectionPool.PoolSize.Production,
),
dbStartupMode = DbStartupMode.CreateOnly,
)

View File

@ -63,7 +63,6 @@ hj_scalacopts = lf_scalacopts + [
"//ledger/sandbox-common",
"//ledger/sandbox-common:sandbox-common-scala-tests-lib",
"//libs-scala/contextualized-logging",
"//libs-scala/db-utils",
"//libs-scala/ports",
"//libs-scala/resources",
"@maven//:io_dropwizard_metrics_metrics_core",

View File

@ -17,7 +17,6 @@ import com.daml.bazeltools.BazelRunfiles.rlocation
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.http.HttpService.doLoad
import com.daml.http.dbbackend.{ContractDao, JdbcConfig}
import com.daml.dbutils.ConnectionPool.PoolSize
import com.daml.http.json.{DomainJsonDecoder, DomainJsonEncoder}
import com.daml.http.util.ClientUtil.boxedRecord
import com.daml.http.util.Logging.{InstanceUUID, instanceUUIDLogCtx}
@ -266,7 +265,7 @@ object HttpServiceTestFixture extends LazyLogging with Assertions with Inside {
metrics: Metrics,
): Future[ContractDao] =
for {
dao <- Future(ContractDao(c, poolSize = PoolSize.Integration))
dao <- Future(ContractDao(c))
isSuccess <- DbStartupOps
.fromStartupMode(dao, c.dbStartupMode)
.unsafeToFuture()

View File

@ -7,13 +7,16 @@ import cats.instances.list._
import com.codahale.metrics.MetricRegistry
import doobie.util.log.LogHandler
import com.daml.dbutils
import com.daml.dbutils.ConnectionPool
import com.daml.doobie.logging.Slf4jLogHandler
import com.daml.http.dbbackend.Queries.{DBContract, SurrogateTpId}
import com.daml.http.domain.TemplateId
import com.daml.http.util.Logging.instanceUUIDLogCtx
import com.daml.metrics.Metrics
import com.daml.testing.oracle, oracle.{OracleAround, User}
import com.daml.testing.oracle
import oracle.{OracleAround, User}
import org.openjdk.jmh.annotations._
import scala.concurrent.ExecutionContext
import scalaz.std.list._
import spray.json._
@ -39,7 +42,13 @@ abstract class ContractDaoBenchmark extends OracleAround {
connectToOracle()
user = createNewRandomUser()
val cfg = JdbcConfig(
dbutils.JdbcConfig("oracle.jdbc.OracleDriver", oracleJdbcUrl, user.name, user.pwd)
dbutils.JdbcConfig(
"oracle.jdbc.OracleDriver",
oracleJdbcUrl,
user.name,
user.pwd,
poolSize = ConnectionPool.PoolSize.Integration,
)
)
val oracleDao = ContractDao(cfg)
dao = oracleDao

View File

@ -8,6 +8,7 @@ import akka.http.scaladsl.model.{StatusCodes, Uri}
import akka.stream.{KillSwitches, UniqueKillSwitch}
import akka.stream.scaladsl.{Keep, Sink}
import com.codahale.metrics.MetricRegistry
import com.daml.dbutils.ConnectionPool
import scala.concurrent.{Future, Promise}
import scala.util.{Failure, Success}
@ -381,7 +382,13 @@ final class FailureTests
val dao = dbbackend.ContractDao(
JdbcConfig(
// discarding other settings
dbutils.JdbcConfig(driver = bc.driver, url = bc.url, user = bc.user, password = bc.password)
dbutils.JdbcConfig(
driver = bc.driver,
url = bc.url,
user = bc.user,
password = bc.password,
poolSize = ConnectionPool.PoolSize.Integration,
)
)
)
util.Logging

View File

@ -6,6 +6,7 @@ package com.daml.http
import akka.http.scaladsl.model.Uri
import com.daml.bazeltools.BazelRunfiles
import com.daml.dbutils
import com.daml.dbutils.ConnectionPool
import dbbackend.{DbStartupMode, JdbcConfig}
import com.daml.http.json.{DomainJsonDecoder, DomainJsonEncoder}
import com.daml.ledger.client.withoutledgerid.{LedgerClient => DamlLedgerClient}
@ -41,6 +42,7 @@ trait HttpFailureTestFixture extends ToxicSandboxFixture with PostgresAroundAll
s"jdbc:postgresql://${postgresDatabase.hostName}:$dbProxyPort/${postgresDatabase.databaseName}?user=${postgresDatabase.userName}&password=${postgresDatabase.password}",
user = "test",
password = "",
poolSize = ConnectionPool.PoolSize.Integration,
),
dbStartupMode = DbStartupMode.CreateOnly,
)

View File

@ -4,7 +4,6 @@
package com.daml.http
import com.codahale.metrics.MetricRegistry
import com.daml.dbutils.ConnectionPool.PoolSize
import com.daml.http.dbbackend.JdbcConfig
import com.daml.metrics.Metrics
import com.daml.testing.postgresql.PostgresAroundAll
@ -19,7 +18,7 @@ trait HttpServicePostgresInt extends AbstractHttpServiceIntegrationTestFuns with
protected implicit val metics = new Metrics(new MetricRegistry)
// has to be lazy because jdbcConfig_ is NOT initialized yet
protected lazy val dao = dbbackend.ContractDao(jdbcConfig_, poolSize = PoolSize.Integration)
protected lazy val dao = dbbackend.ContractDao(jdbcConfig_)
// has to be lazy because postgresFixture is NOT initialized yet
protected[this] def jdbcConfig_ = PostgresIntTest.defaultJdbcConfig(postgresDatabase.url)

View File

@ -4,6 +4,7 @@
package com.daml.http
import com.daml.dbutils
import com.daml.dbutils.ConnectionPool
import com.daml.http.PostgresIntTest.defaultJdbcConfig
import com.daml.http.dbbackend.{DbStartupMode, JdbcConfig}
import com.daml.testing.postgresql.PostgresAroundAll
@ -26,6 +27,7 @@ object PostgresIntTest {
user = "test",
password = "",
tablePrefix = "some_nice_prefix_",
poolSize = ConnectionPool.PoolSize.Integration,
),
dbStartupMode = DbStartupMode.CreateOnly,
)

View File

@ -5,7 +5,6 @@ package com.daml.http
import cats.effect.IO
import com.codahale.metrics.MetricRegistry
import com.daml.dbutils.ConnectionPool.PoolSize
import com.daml.http.dbbackend.{ContractDao, JdbcConfig}
import com.daml.http.domain.TemplateId
import com.daml.http.util.Logging.{InstanceUUID, instanceUUIDLogCtx}
@ -29,14 +28,12 @@ abstract class AbstractDatabaseIntegrationTest extends AsyncFreeSpecLike with Be
// has to be lazy because jdbcConfig is NOT initialized yet
protected lazy val dao = dbbackend.ContractDao(
jdbcConfig.copy(baseConfig = jdbcConfig.baseConfig.copy(tablePrefix = "some_fancy_prefix_")),
poolSize = PoolSize.Integration,
jdbcConfig.copy(baseConfig = jdbcConfig.baseConfig.copy(tablePrefix = "some_fancy_prefix_"))
)
protected lazy val daoWithoutPrefix =
dbbackend.ContractDao(
jdbcConfig.copy(baseConfig = jdbcConfig.baseConfig.copy(tablePrefix = "")),
poolSize = PoolSize.Integration,
jdbcConfig.copy(baseConfig = jdbcConfig.baseConfig.copy(tablePrefix = ""))
)
override protected def afterAll(): Unit = {

View File

@ -75,7 +75,7 @@ class ContractDao private (
}
object ContractDao {
import ConnectionPool.PoolSize, SurrogateTemplateIdCache.MaxEntries
import SurrogateTemplateIdCache.MaxEntries
private[this] val supportedJdbcDrivers = Map[String, SupportedJdbcDriver.Available](
"org.postgresql.Driver" -> SupportedJdbcDriver.Postgres,
"oracle.jdbc.OracleDriver" -> SupportedJdbcDriver.Oracle,
@ -92,7 +92,6 @@ object ContractDao {
def apply(
cfg: JdbcConfig,
tpIdCacheMaxEntries: Option[Long] = None,
poolSize: PoolSize = PoolSize.Production,
)(implicit
ec: ExecutionContext,
metrics: Metrics,
@ -108,9 +107,9 @@ object ContractDao {
} yield {
implicit val sjd: SupportedJdbcDriver.TC = sjdc
//pool for connections awaiting database access
val es = Executors.newWorkStealingPool(poolSize)
val es = Executors.newWorkStealingPool(cfg.baseConfig.poolSize)
val (ds, conn) =
ConnectionPool.connect(cfg.baseConfig, poolSize)(ExecutionContext.fromExecutor(es), cs)
ConnectionPool.connect(cfg.baseConfig)(ExecutionContext.fromExecutor(es), cs)
new ContractDao(ds, conn, es)
}
setup.fold(msg => throw new IllegalArgumentException(msg), identity)

View File

@ -6,9 +6,17 @@ package com.daml.http
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers
import com.daml.dbutils
import com.daml.http.dbbackend.{JdbcConfig, DbStartupMode}
import com.daml.http.dbbackend.{DbStartupMode, JdbcConfig}
object CliSpec {
private val poolSize = 10
private val minIdle = 4
private val connectionTimeout = 5000L
private val idleTimeout = 1000L
private val tablePrefix = "foo"
}
final class CliSpec extends AnyFreeSpec with Matchers {
import CliSpec._
private def configParser(
parameters: Seq[String],
@ -22,11 +30,17 @@ final class CliSpec extends AnyFreeSpec with Matchers {
"jdbc:postgresql://localhost:5432/test?&ssl=true",
"postgres",
"password",
poolSize,
minIdle,
connectionTimeout,
idleTimeout,
tablePrefix,
),
dbStartupMode = DbStartupMode.StartOnly,
)
val jdbcConfigString =
"driver=org.postgresql.Driver,url=jdbc:postgresql://localhost:5432/test?&ssl=true,user=postgres,password=password,createSchema=false"
"driver=org.postgresql.Driver,url=jdbc:postgresql://localhost:5432/test?&ssl=true,user=postgres,password=password," +
s"poolSize=$poolSize,minIdle=$minIdle,connectionTimeout=$connectionTimeout,idleTimeout=$idleTimeout,tablePrefix=$tablePrefix"
val sharedOptions =
Seq("--ledger-host", "localhost", "--ledger-port", "6865", "--http-port", "7500")
@ -106,7 +120,8 @@ final class CliSpec extends AnyFreeSpec with Matchers {
"DbStartupMode" - {
val jdbcConfigShared =
"driver=org.postgresql.Driver,url=jdbc:postgresql://localhost:5432/test?&ssl=true,user=postgres,password=password"
"driver=org.postgresql.Driver,url=jdbc:postgresql://localhost:5432/test?&ssl=true,user=postgres,password=password," +
s"poolSize=$poolSize,minIdle=$minIdle,connectionTimeout=$connectionTimeout,idleTimeout=$idleTimeout,tablePrefix=$tablePrefix"
"should get the CreateOnly startup mode from the string" in {
val jdbcConfigString = s"$jdbcConfigShared,start-mode=create-only"

View File

@ -26,9 +26,6 @@ object Connection {
)
}
/*
TODO below values are hardcoded for now, refactor to be picked up as cli flags/ props later.
*/
object ConnectionPool {
type PoolSize = Int
@ -40,27 +37,25 @@ object ConnectionPool {
type T = Transactor.Aux[IO, _ <: DataSource with Closeable]
def connect(
c: JdbcConfig,
poolSize: PoolSize,
c: JdbcConfig
)(implicit
ec: ExecutionContext,
cs: ContextShift[IO],
): (DataSource with Closeable, T) = {
val ds = dataSource(c, poolSize)
val ds = dataSource(c)
(
ds,
Transactor
.fromDataSource[IO](
ds,
connectEC = ec,
blocker = Blocker liftExecutorService newWorkStealingPool(poolSize),
blocker = Blocker liftExecutorService newWorkStealingPool(c.poolSize),
)(IO.ioConcurrentEffect(cs), cs),
)
}
private[this] def dataSource(
jc: JdbcConfig,
poolSize: PoolSize,
jc: JdbcConfig
) = {
import jc._
val c = new HikariConfig

View File

@ -3,6 +3,7 @@
package com.daml.dbutils
import ConnectionPool.PoolSize
import com.typesafe.scalalogging.StrictLogging
import scalaz.std.either._
import scalaz.std.option._
@ -25,6 +26,7 @@ final case class JdbcConfig(
url: String,
user: String,
password: String,
poolSize: Int,
minIdle: Int = JdbcConfig.MinIdle,
connectionTimeout: Long = JdbcConfig.ConnectionTimeout,
idleTimeout: Long = JdbcConfig.IdleTimeout,
@ -69,6 +71,9 @@ abstract class ConfigCompanion[A, ReadCtx](name: String) {
): Fields[Option[Boolean]] =
optionalStringField(k).flatMap(ov => StateT liftM (ov traverse parseBoolean(k)).toEither)
protected def optionalIntField(k: String): Fields[Option[Int]] =
optionalStringField(k).flatMap(ov => StateT liftM (ov traverse parseInt(k)).toEither)
protected def optionalLongField(k: String): Fields[Option[Long]] =
optionalStringField(k).flatMap(ov => StateT liftM (ov traverse parseLong(k)).toEither)
@ -80,6 +85,9 @@ abstract class ConfigCompanion[A, ReadCtx](name: String) {
protected def parseLong(k: String)(v: String): String \/ Long =
v.parseLong.leftMap(e => s"$k=$v must be a int value: ${e.description}").disjunction
protected def parseInt(k: String)(v: String): String \/ Int =
v.parseInt.leftMap(e => s"$k=$v must be an int value: ${e.description}").disjunction
protected def requiredDirectoryField(k: String): Fields[File] =
requiredField(k).flatMap(s => StateT liftM directory(s))
@ -102,7 +110,10 @@ object JdbcConfig
@scala.deprecated("do I need this?", since = "SC")
implicit val showInstance: Show[JdbcConfig] =
Show.shows(a => s"JdbcConfig(driver=${a.driver}, url=${a.url}, user=${a.user})")
Show.shows(a =>
s"JdbcConfig(driver=${a.driver}, url=${a.url}, user=${a.user}, poolSize=${a.poolSize}, " +
s"minIdle=${a.minIdle}, connectionTimeout=${a.connectionTimeout}, idleTimeout=${a.idleTimeout}"
)
def help(otherOptions: String = "")(implicit jcd: DBConfig.JdbcConfigDefaults): String =
"Contains comma-separated key-value pairs. Where:\n" +
@ -111,6 +122,10 @@ object JdbcConfig
s"${indent}user -- database user name,\n" +
s"${indent}password -- database user password,\n" +
s"${indent}tablePrefix -- prefix for table names to avoid collisions, empty by default,\n" +
s"${indent}poolSize -- int value, specifies the max pool size for the database connection pool.\n" +
s"${indent}minIdle -- int value, specifies the min idle connections for database connection pool.\n" +
s"${indent}connectionTimeout -- long value, specifies the connection timeout for database connection pool.\n" +
s"${indent}idleTimeout -- long value, specifies the idle timeout for the database connection pool.\n" +
otherOptions +
s"${indent}Example: " + helpString(
"org.postgresql.Driver",
@ -118,6 +133,10 @@ object JdbcConfig
"postgres",
"password",
"table_prefix_",
PoolSize.Production.toString,
MinIdle.toString,
ConnectionTimeout.toString,
IdleTimeout.toString,
)
private[daml] def create(x: Map[String, String])(implicit
@ -142,12 +161,20 @@ object JdbcConfig
user <- requiredField("user")
password <- requiredField("password")
tablePrefix <- optionalStringField("tablePrefix").map(_ getOrElse "")
maxPoolSize <- optionalIntField("poolSize").map(_ getOrElse PoolSize.Production)
minIdle <- optionalIntField("minIdle").map(_ getOrElse MinIdle)
connTimeout <- optionalLongField("connectionTimeout").map(_ getOrElse ConnectionTimeout)
idleTimeout <- optionalLongField("idleTimeout").map(_ getOrElse IdleTimeout)
} yield JdbcConfig(
driver = driver,
url = url,
user = user,
password = password,
tablePrefix = tablePrefix,
poolSize = maxPoolSize,
minIdle = minIdle,
connectionTimeout = connTimeout,
idleTimeout = idleTimeout,
)
private def helpString(
@ -156,6 +183,11 @@ object JdbcConfig
user: String,
password: String,
tablePrefix: String,
poolSize: String,
minIdle: String,
connectionTimeout: String,
idleTimeout: String,
): String =
s"""\"driver=$driver,url=$url,user=$user,password=$password,tablePrefix=$tablePrefix\""""
s"""\"driver=$driver,url=$url,user=$user,password=$password,tablePrefix=$tablePrefix,poolSize=$poolSize,
|minIdle=$minIdle, connectionTimeout=$connectionTimeout,idleTimeout=$idleTimeout\"""".stripMargin
}

View File

@ -8,7 +8,6 @@ import cats.effect.{ContextShift, IO}
import cats.syntax.functor._
import com.daml.daml_lf_dev.DamlLf
import com.daml.dbutils.{ConnectionPool, JdbcConfig}
import ConnectionPool.PoolSize, PoolSize._
import com.daml.ledger.api.refinements.ApiTypes.{ApplicationId, Party}
import com.daml.lf.archive.{ArchivePayloadParser, Dar}
import com.daml.lf.data.Ref.{Identifier, PackageId}
@ -315,11 +314,11 @@ object DbTriggerDao {
def supportedJdbcDriverNames(available: Set[String]): Set[String] =
supportedJdbcDrivers.keySet intersect available
def apply(c: JdbcConfig, poolSize: PoolSize = Production)(implicit
def apply(c: JdbcConfig)(implicit
ec: ExecutionContext
): DbTriggerDao = {
implicit val cs: ContextShift[IO] = IO.contextShift(ec)
val (ds, conn) = ConnectionPool.connect(c, poolSize)
val (ds, conn) = ConnectionPool.connect(c)
val driver = supportedJdbcDrivers
.get(c.driver)
.getOrElse(throw new IllegalArgumentException(s"Unsupported JDBC driver ${c.driver}"))

View File

@ -416,9 +416,14 @@ trait TriggerDaoPostgresFixture
// Lazy because the postgresDatabase is only available once the tests start
private lazy val jdbcConfig_ =
JdbcConfig("org.postgresql.Driver", postgresDatabase.url, "operator", "password")
private lazy val triggerDao =
DbTriggerDao(jdbcConfig_, poolSize = ConnectionPool.PoolSize.Integration)
JdbcConfig(
"org.postgresql.Driver",
postgresDatabase.url,
"operator",
"password",
ConnectionPool.PoolSize.Integration,
)
private lazy val triggerDao = DbTriggerDao(jdbcConfig_)
private lazy implicit val executionContext: ExecutionContext = system.getDispatcher
override protected def beforeEach(): Unit = {
@ -448,11 +453,17 @@ trait TriggerDaoOracleFixture
// Lazy because the oracleDatabase is only available once the tests start
private lazy val jdbcConfig_ =
JdbcConfig("oracle.jdbc.OracleDriver", oracleJdbcUrl, oracleUser, oraclePwd)
JdbcConfig(
"oracle.jdbc.OracleDriver",
oracleJdbcUrl,
oracleUser,
oraclePwd,
ConnectionPool.PoolSize.Production,
)
// TODO For whatever reason we need a larger pool here, otherwise
// the connection deadlocks. I have no idea why :(
private lazy val triggerDao =
DbTriggerDao(jdbcConfig_, poolSize = ConnectionPool.PoolSize.Production)
DbTriggerDao(jdbcConfig_)
private lazy implicit val executionContext: ExecutionContext = system.getDispatcher
override protected def beforeEach(): Unit = {