Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Give connection pool example config * Get hikari logging working on postgres * fix 2.12.x * Bump metrics core to a supported version * Add hikari-logging and hikari-logging-interval configuration options to turn off/on hikari and schedule how often logs appear * Turn off hikari logging in the db-commons/reference.conf and add it to appserver/reference.conf * default the logging to off in scala code * Make sure connection pool is enabled in db-commons, add default database configuration to website * Address code review, fix things that didn't need to be changed
- Loading branch information
1 parent
d5243ad
commit 4a13162
Showing
17 changed files
with
473 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,15 @@ | ||
bitcoin-s { | ||
oracle { | ||
hikari-logging = true | ||
hikari-logging-interval = 1 minute | ||
} | ||
} | ||
|
||
akka { | ||
|
||
# Set these to the defaults instead of the | ||
# appServer's modified ones | ||
http.server.request-timeout = 10s | ||
http.server.parsing.max-content-length = 8m | ||
http.client.parsing.max-content-length = 8m | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,21 @@ | ||
bitcoin-s { | ||
network = mainnet | ||
|
||
chain { | ||
hikari-logging = true | ||
hikari-logging-interval = 1 minute | ||
} | ||
|
||
wallet { | ||
hikari-logging = true | ||
hikari-logging-interval = 1 minute | ||
} | ||
|
||
node { | ||
mode = neutrino # neutrino, spv, bitcoind | ||
peers = ["neutrino.suredbits.com:8333"] | ||
|
||
hikari-logging = true | ||
hikari-logging-interval = 1 minute | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
239 changes: 239 additions & 0 deletions
239
db-commons/src/main/scala/org/bitcoins/db/HikariLogging.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
package org.bitcoins.db | ||
|
||
import com.codahale.metrics.{Histogram, MetricRegistry} | ||
import com.zaxxer.hikari.{HikariDataSource, HikariPoolMXBean} | ||
import org.bitcoins.core.util.{BitcoinSLogger, StartStop} | ||
import slick.jdbc.JdbcDataSource | ||
import slick.jdbc.hikaricp.HikariCPJdbcDataSource | ||
import slick.util.AsyncExecutorMXBean | ||
|
||
import java.lang.management.ManagementFactory | ||
import java.util.concurrent.{Executors, ScheduledFuture, TimeUnit} | ||
import javax.management.{JMX, ObjectName} | ||
import scala.concurrent.duration._ | ||
|
||
case class HikariLogging( | ||
hikariDataSource: HikariDataSource, | ||
moduleName: String, | ||
interval: Duration | ||
) extends BitcoinSLogger | ||
with StartStop[HikariLogging] { | ||
|
||
/** Logs thread activity */ | ||
private case class HikariActivityUpdate( | ||
active: Int, | ||
idle: Int, | ||
waiting: Int, | ||
total: Int, | ||
maxThreads: Int, | ||
activeThreads: Int, | ||
maxQueueSize: Int, | ||
queueSize: Int | ||
) { | ||
|
||
override def toString: String = { | ||
s""" | ||
| "${moduleName}-activity-update" : { | ||
| "active" : ${active}, | ||
| "idle" : ${idle}, | ||
| "waiting" : ${waiting}, | ||
| "total" : ${total}, | ||
| "maxThreads" : ${maxThreads}, | ||
| "activeThreads" : ${activeThreads}, | ||
| "maxQueueSize" : ${maxQueueSize}, | ||
| "queueSize" : ${queueSize} | ||
|} | ||
|""".stripMargin.replaceAll("\\s", "") | ||
} | ||
} | ||
|
||
/** | ||
* From the docs: | ||
* How long each connection is used before being returned to the pool. This is the "out of pool" or "in-use" time. | ||
* @see https://github.com/brettwooldridge/HikariCP/wiki/Dropwizard-Metrics | ||
*/ | ||
private case class HikariPoolUsageUpdate( | ||
`75thPercentile`: Double, | ||
`95thPercentile`: Double, | ||
`98thPercentile`: Double, | ||
`99thPercentile`: Double, | ||
`999thPercentile`: Double, | ||
max: Double, | ||
min: Double, | ||
median: Double, | ||
mean: Double | ||
) { | ||
|
||
override def toString: String = { | ||
s""" | ||
|"${moduleName}-pool-usage" : { | ||
| "max" : ${max}, | ||
| "min" : ${min}, | ||
| "median" : ${median}, | ||
| "mean" : ${mean}, | ||
| "75thPercentile" : ${`75thPercentile`}, | ||
| "95thPercentile" : ${`95thPercentile`}, | ||
| "98thPercentile" : ${`98thPercentile`}, | ||
| "99thPercentile" : ${`99thPercentile`}, | ||
| "999thPercentile" : ${`999thPercentile`} | ||
|} | ||
|""".stripMargin.replaceAll("\\s", "") | ||
} | ||
} | ||
|
||
//this is needed to get the 'AsyncExecutor' bean below to register properly | ||
//dbConfig.database.ioExecutionContext | ||
|
||
private lazy val poolName = hikariDataSource.getPoolName | ||
private lazy val mBeanServer = ManagementFactory.getPlatformMBeanServer | ||
|
||
lazy val aeBeanName = new ObjectName( | ||
s"slick:type=AsyncExecutor,name=$poolName") | ||
|
||
lazy val poolBeanName = new ObjectName( | ||
s"com.zaxxer.hikari:type=Pool ($poolName)") | ||
|
||
lazy val poolConfigBeanName = new ObjectName( | ||
s"com.zaxxer.hikari:type=PoolConfig ($poolName)" | ||
) | ||
|
||
/** | ||
* MBean uses random string incantations for | ||
* accessing attributes :-( | ||
* | ||
* @see [[https://github.com/brettwooldridge/HikariCP/wiki/MBean-(JMX)-Monitoring-and-Management#programmatic-access HikariCP docs]] | ||
*/ | ||
private lazy val objectName = new ObjectName( | ||
s"com.zaxxer.hikari:type=Pool ($poolName)" | ||
) | ||
|
||
/** | ||
* @see https://github.com/brettwooldridge/HikariCP/wiki/MBean-(JMX)-Monitoring-and-Management | ||
*/ | ||
private lazy val hikariMxBean = | ||
JMX.newMXBeanProxy(mBeanServer, objectName, classOf[HikariPoolMXBean]) | ||
|
||
/** | ||
* @see http://slick.lightbend.com/doc/3.3.0/config.html#monitoring | ||
*/ | ||
private lazy val slickMxBean = | ||
JMX.newMXBeanProxy(mBeanServer, aeBeanName, classOf[AsyncExecutorMXBean]) | ||
|
||
// https://github.com/brettwooldridge/HikariCP/wiki/Dropwizard-Metrics#pool-namepoolusage | ||
private lazy val poolUsageMetricName = s"$poolName.pool.Usage" | ||
|
||
private lazy val metricRegistry: MetricRegistry = Option( | ||
hikariDataSource.getMetricRegistry | ||
) match { | ||
case Some(registry: MetricRegistry) => | ||
registry | ||
case Some(other: AnyRef) => | ||
val msg = s"Could not load metric registry, got $other" | ||
logger.error(msg) | ||
throw new RuntimeException(msg) | ||
case None => | ||
val msg = "Could not load metric registry, got null!" | ||
logger.error(msg) | ||
throw new RuntimeException(msg) | ||
} | ||
|
||
private val logHikariStats: Runnable = () => { | ||
|
||
val usageHistogram: Histogram = | ||
metricRegistry.getHistograms().get(poolUsageMetricName) | ||
val usageSnapshot = usageHistogram.getSnapshot() | ||
|
||
val poolUsageUpdate = HikariPoolUsageUpdate( | ||
`75thPercentile` = usageSnapshot.get75thPercentile(), | ||
`95thPercentile` = usageSnapshot.get95thPercentile(), | ||
`98thPercentile` = usageSnapshot.get98thPercentile(), | ||
`99thPercentile` = usageSnapshot.get99thPercentile(), | ||
`999thPercentile` = usageSnapshot.get999thPercentile(), | ||
max = usageSnapshot.getMax().toDouble, | ||
min = usageSnapshot.getMin().toDouble, | ||
median = usageSnapshot.getMedian(), | ||
mean = usageSnapshot.getMean() | ||
) | ||
|
||
val activityUpdate = HikariActivityUpdate( | ||
active = hikariMxBean.getActiveConnections, | ||
idle = hikariMxBean.getIdleConnections, | ||
waiting = hikariMxBean.getThreadsAwaitingConnection, | ||
total = hikariMxBean.getTotalConnections, | ||
maxThreads = slickMxBean.getMaxThreads, | ||
activeThreads = slickMxBean.getActiveThreads, | ||
maxQueueSize = slickMxBean.getMaxQueueSize, | ||
queueSize = slickMxBean.getQueueSize | ||
) | ||
|
||
logger.info(poolUsageUpdate) | ||
logger.info(activityUpdate) | ||
} | ||
|
||
private[this] var started: Boolean = false | ||
private[this] var cancelOpt: Option[ScheduledFuture[_]] = None | ||
|
||
override def start(): HikariLogging = { | ||
if (!started) { | ||
val metricRegistry = new MetricRegistry | ||
|
||
mBeanServer.getMBeanInfo(aeBeanName) | ||
mBeanServer.getMBeanInfo(poolBeanName) | ||
mBeanServer.getMBeanInfo(poolConfigBeanName) | ||
|
||
hikariDataSource.setMetricRegistry(metricRegistry) | ||
val future = HikariLogging.scheduler.scheduleAtFixedRate( | ||
logHikariStats, | ||
interval.toMillis, | ||
interval.toMillis, | ||
TimeUnit.MILLISECONDS) | ||
cancelOpt = Some(future) | ||
started = true | ||
this | ||
} else { | ||
this | ||
} | ||
} | ||
|
||
override def stop(): HikariLogging = { | ||
cancelOpt match { | ||
case Some(cancel) => | ||
if (!cancel.isCancelled) { | ||
val _: Boolean = cancel.cancel(true) | ||
this | ||
} else { | ||
cancelOpt = None | ||
this | ||
} | ||
case None => | ||
this | ||
} | ||
} | ||
} | ||
|
||
object HikariLogging extends BitcoinSLogger { | ||
private[db] val scheduler = Executors.newScheduledThreadPool(1) | ||
|
||
/** Returns a started hikari logger if configuration is correct, else None | ||
* @param jdbcProfileComponent the database component we are logging for | ||
* @param interval how often the hikari logs should be output | ||
*/ | ||
def fromJdbcProfileComponent[T <: DbAppConfig]( | ||
jdbcProfileComponent: JdbcProfileComponent[T], | ||
interval: Duration): Option[HikariLogging] = { | ||
val dataSource = jdbcProfileComponent.database.source | ||
val moduleName = jdbcProfileComponent.appConfig.moduleName | ||
dataSource match { | ||
case hikariSource: HikariCPJdbcDataSource => | ||
val started = HikariLogging(hikariSource.ds, moduleName, interval) | ||
.start() | ||
Some(started) | ||
case _: JdbcDataSource => | ||
val err = { | ||
s"JdbcProfile Component is not a Hikari source=${jdbcProfileComponent.dbConfig.profile}" | ||
} | ||
logger.error(err) | ||
None | ||
} | ||
} | ||
} |
Oops, something went wrong.