mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 21:35:42 +00:00
Pool Postgres connections behind provider (#8372)
* Pool Postgres connections behind provider * Use smaller production Postgres pool default
This commit is contained in:
@@ -365,6 +365,7 @@ maven.install(
|
||||
"org.xerial:sqlite-jdbc:3.53.2.0",
|
||||
|
||||
# Postgres for production history benchmarking and migration work
|
||||
"com.zaxxer:HikariCP:6.3.3",
|
||||
"org.postgresql:postgresql:42.7.11",
|
||||
],
|
||||
duplicate_version_warning = "error",
|
||||
|
||||
@@ -39,6 +39,13 @@ services:
|
||||
EAGLE_POSTGRES_USER: "${EAGLE_POSTGRES_USER:-}"
|
||||
EAGLE_POSTGRES_PASSWORD: "${EAGLE_POSTGRES_PASSWORD:-}"
|
||||
EAGLE_POSTGRES_SSLMODE: "${EAGLE_POSTGRES_SSLMODE:-require}"
|
||||
EAGLE_POSTGRES_MAX_POOL_SIZE: "${EAGLE_POSTGRES_MAX_POOL_SIZE:-7}"
|
||||
EAGLE_POSTGRES_MIN_IDLE: "${EAGLE_POSTGRES_MIN_IDLE:-0}"
|
||||
EAGLE_POSTGRES_CONNECTION_TIMEOUT_MILLIS: "${EAGLE_POSTGRES_CONNECTION_TIMEOUT_MILLIS:-10000}"
|
||||
EAGLE_POSTGRES_IDLE_TIMEOUT_MILLIS: "${EAGLE_POSTGRES_IDLE_TIMEOUT_MILLIS:-120000}"
|
||||
EAGLE_POSTGRES_MAX_LIFETIME_MILLIS: "${EAGLE_POSTGRES_MAX_LIFETIME_MILLIS:-1500000}"
|
||||
EAGLE_POSTGRES_KEEPALIVE_TIME_MILLIS: "${EAGLE_POSTGRES_KEEPALIVE_TIME_MILLIS:-300000}"
|
||||
EAGLE_POSTGRES_LEAK_DETECTION_THRESHOLD_MILLIS: "${EAGLE_POSTGRES_LEAK_DETECTION_THRESHOLD_MILLIS:-60000}"
|
||||
# JWT public key for token validation (auth service handles signing)
|
||||
# Reads from /etc/eagle0/keys/public.pem via shared volume
|
||||
# Auth token for Shardok on Hetzner (required)
|
||||
@@ -95,6 +102,13 @@ services:
|
||||
EAGLE_POSTGRES_USER: "${EAGLE_POSTGRES_USER:-}"
|
||||
EAGLE_POSTGRES_PASSWORD: "${EAGLE_POSTGRES_PASSWORD:-}"
|
||||
EAGLE_POSTGRES_SSLMODE: "${EAGLE_POSTGRES_SSLMODE:-require}"
|
||||
EAGLE_POSTGRES_MAX_POOL_SIZE: "${EAGLE_POSTGRES_MAX_POOL_SIZE:-7}"
|
||||
EAGLE_POSTGRES_MIN_IDLE: "${EAGLE_POSTGRES_MIN_IDLE:-0}"
|
||||
EAGLE_POSTGRES_CONNECTION_TIMEOUT_MILLIS: "${EAGLE_POSTGRES_CONNECTION_TIMEOUT_MILLIS:-10000}"
|
||||
EAGLE_POSTGRES_IDLE_TIMEOUT_MILLIS: "${EAGLE_POSTGRES_IDLE_TIMEOUT_MILLIS:-120000}"
|
||||
EAGLE_POSTGRES_MAX_LIFETIME_MILLIS: "${EAGLE_POSTGRES_MAX_LIFETIME_MILLIS:-1500000}"
|
||||
EAGLE_POSTGRES_KEEPALIVE_TIME_MILLIS: "${EAGLE_POSTGRES_KEEPALIVE_TIME_MILLIS:-300000}"
|
||||
EAGLE_POSTGRES_LEAK_DETECTION_THRESHOLD_MILLIS: "${EAGLE_POSTGRES_LEAK_DETECTION_THRESHOLD_MILLIS:-60000}"
|
||||
# JWT public key for token validation (auth service handles signing)
|
||||
# Reads from /etc/eagle0/keys/public.pem via shared volume
|
||||
SHARDOK_AUTH_TOKEN: "${SHARDOK_AUTH_TOKEN:-}"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"com.thesamet.scalapb:scalapb-json4s_3": -772830803,
|
||||
"com.thesamet.scalapb:scalapb-runtime-grpc_3": -2026114331,
|
||||
"com.thesamet.scalapb:scalapb-runtime_3": -1281968352,
|
||||
"com.zaxxer:HikariCP": 1858991878,
|
||||
"io.netty:netty-buffer": 732437843,
|
||||
"io.netty:netty-codec": -1199307927,
|
||||
"io.netty:netty-codec-http": -1456497900,
|
||||
@@ -117,6 +118,7 @@
|
||||
"com.thesamet.scalapb:scalapb-runtime-grpc_3": 97373115,
|
||||
"com.thesamet.scalapb:scalapb-runtime_3": 818778125,
|
||||
"com.typesafe:config": -1791448454,
|
||||
"com.zaxxer:HikariCP": -704610647,
|
||||
"commons-codec:commons-codec": 1377992676,
|
||||
"commons-io:commons-io": 669567296,
|
||||
"dev.dirs:directories": 1716880629,
|
||||
@@ -550,6 +552,12 @@
|
||||
},
|
||||
"version": "1.4.6"
|
||||
},
|
||||
"com.zaxxer:HikariCP": {
|
||||
"shasums": {
|
||||
"jar": "709f378c05756280939ce50fc1b1f1a53bb8e1899dc1b249f21f12703640b48b"
|
||||
},
|
||||
"version": "6.3.3"
|
||||
},
|
||||
"commons-codec:commons-codec": {
|
||||
"shasums": {
|
||||
"jar": "f700de80ac270d0344fdea7468201d8b9c805e5c648331c3619f2ee067ccfc59"
|
||||
@@ -1581,6 +1589,9 @@
|
||||
"org.scala-lang.modules:scala-collection-compat_3",
|
||||
"org.scala-lang:scala3-library_3"
|
||||
],
|
||||
"com.zaxxer:HikariCP": [
|
||||
"org.slf4j:slf4j-api"
|
||||
],
|
||||
"io.get-coursier:coursier-cache_2.13": [
|
||||
"io.get-coursier.jniutils:windows-jni-utils",
|
||||
"io.get-coursier:cache-util",
|
||||
@@ -2557,6 +2568,16 @@
|
||||
"com.typesafe.config.impl",
|
||||
"com.typesafe.config.parser"
|
||||
],
|
||||
"com.zaxxer:HikariCP": [
|
||||
"com.zaxxer.hikari",
|
||||
"com.zaxxer.hikari.hibernate",
|
||||
"com.zaxxer.hikari.metrics",
|
||||
"com.zaxxer.hikari.metrics.dropwizard",
|
||||
"com.zaxxer.hikari.metrics.micrometer",
|
||||
"com.zaxxer.hikari.metrics.prometheus",
|
||||
"com.zaxxer.hikari.pool",
|
||||
"com.zaxxer.hikari.util"
|
||||
],
|
||||
"commons-codec:commons-codec": [
|
||||
"org.apache.commons.codec",
|
||||
"org.apache.commons.codec.binary",
|
||||
@@ -4100,6 +4121,7 @@
|
||||
"com.thesamet.scalapb:scalapb-runtime-grpc_3",
|
||||
"com.thesamet.scalapb:scalapb-runtime_3",
|
||||
"com.typesafe:config",
|
||||
"com.zaxxer:HikariCP",
|
||||
"commons-codec:commons-codec",
|
||||
"commons-io:commons-io",
|
||||
"dev.dirs:directories",
|
||||
|
||||
@@ -107,6 +107,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:eagle_require",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result/generated_text_request",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:generated_text_request_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/postgres:postgres_connection_provider",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package net.eagle0.eagle.client_text
|
||||
|
||||
import java.sql.{Connection, DriverManager, ResultSet}
|
||||
import java.sql.{Connection, ResultSet}
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
import scala.util.Try
|
||||
@@ -11,6 +11,7 @@ import net.eagle0.eagle.library.util.EagleRequire.internalRequire
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
import net.eagle0.eagle.model.action_result.generated_text_request.GeneratedTextRequestT
|
||||
import net.eagle0.eagle.model.proto_converters.GeneratedTextRequestConverter
|
||||
import net.eagle0.eagle.postgres.PostgresConnectionProvider
|
||||
|
||||
class PostgresClientTextStore private[client_text] (
|
||||
initialConnection: Connection,
|
||||
@@ -945,15 +946,14 @@ object PostgresClientTextStore {
|
||||
loaded(gameId, pregenerated).get
|
||||
|
||||
private def openConnection(): Connection = {
|
||||
val cfg = config.getOrElse(
|
||||
val provider = PostgresConnectionProvider.shared.getOrElse(
|
||||
throw new EagleInternalException(
|
||||
"PostgresClientTextStore requires EAGLE_POSTGRES_HOST, DATABASE, USER, and PASSWORD"
|
||||
)
|
||||
)
|
||||
Class.forName("org.postgresql.Driver")
|
||||
val connection = DriverManager.getConnection(cfg.jdbcUrl, cfg.user, cfg.password)
|
||||
val connection = provider.getConnection("PostgresClientTextStore")
|
||||
val count = openConnections.incrementAndGet()
|
||||
SimpleTimedLogger.printLogger.logLine(s"PostgresClientTextStore opened connection; openTextConnections=$count")
|
||||
SimpleTimedLogger.printLogger.logLine(s"PostgresClientTextStore borrowed connection; openTextConnections=$count")
|
||||
connection
|
||||
}
|
||||
|
||||
@@ -967,28 +967,10 @@ object PostgresClientTextStore {
|
||||
private def recordClosed(description: String): Unit = {
|
||||
val count = openConnections.decrementAndGet()
|
||||
SimpleTimedLogger.printLogger.logLine(
|
||||
s"PostgresClientTextStore closed connection for $description; openTextConnections=$count"
|
||||
s"PostgresClientTextStore returned connection for $description; openTextConnections=$count"
|
||||
)
|
||||
}
|
||||
|
||||
private def config: Option[PostgresConfig] =
|
||||
for
|
||||
host <- envNonEmpty("EAGLE_POSTGRES_HOST")
|
||||
database <- envNonEmpty("EAGLE_POSTGRES_DATABASE")
|
||||
user <- envNonEmpty("EAGLE_POSTGRES_USER")
|
||||
password <- envNonEmpty("EAGLE_POSTGRES_PASSWORD")
|
||||
yield PostgresConfig(
|
||||
host = host,
|
||||
port = sys.env.get("EAGLE_POSTGRES_PORT").flatMap(_.toIntOption).getOrElse(5432),
|
||||
database = database,
|
||||
user = user,
|
||||
password = password,
|
||||
sslmode = sys.env.get("EAGLE_POSTGRES_SSLMODE").filter(_.nonEmpty).getOrElse("require")
|
||||
)
|
||||
|
||||
private def envNonEmpty(name: String): Option[String] =
|
||||
sys.env.get(name).map(_.trim).filter(_.nonEmpty)
|
||||
|
||||
private def schemaNameForGame(gameId: GameId): String = {
|
||||
val schema = "eagle_game_" + java.lang.Long.toHexString(gameId)
|
||||
if !schema.matches("[a-z][a-z0-9_]*") then
|
||||
@@ -1099,15 +1081,4 @@ object PostgresClientTextStore {
|
||||
|)""".stripMargin
|
||||
)
|
||||
|
||||
private case class PostgresConfig(
|
||||
host: String,
|
||||
port: Int,
|
||||
database: String,
|
||||
user: String,
|
||||
password: String,
|
||||
sslmode: String
|
||||
) {
|
||||
def jdbcUrl: String =
|
||||
s"jdbc:postgresql://$host:$port/$database?sslmode=$sslmode"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
load("@rules_scala//scala:scala.bzl", "scala_library")
|
||||
|
||||
scala_library(
|
||||
name = "postgres_connection_provider",
|
||||
srcs = ["PostgresConnectionProvider.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle:__subpackages__",
|
||||
],
|
||||
runtime_deps = [
|
||||
"@maven//:com_zaxxer_HikariCP",
|
||||
"@maven//:org_postgresql_postgresql",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/scala/net/eagle0/common:simple_timed_logger",
|
||||
"//src/main/scala/net/eagle0/eagle/library:eagle_internal_exception",
|
||||
"@maven//:com_zaxxer_HikariCP",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,126 @@
|
||||
package net.eagle0.eagle.postgres
|
||||
|
||||
import java.sql.Connection
|
||||
|
||||
import com.zaxxer.hikari.{HikariConfig, HikariDataSource}
|
||||
import net.eagle0.common.SimpleTimedLogger
|
||||
import net.eagle0.eagle.library.EagleInternalException
|
||||
|
||||
trait PostgresConnectionProvider extends AutoCloseable {
|
||||
def getConnection(description: String): Connection
|
||||
|
||||
def close(): Unit
|
||||
}
|
||||
|
||||
object PostgresConnectionProvider {
|
||||
lazy val shared: Option[PostgresConnectionProvider] =
|
||||
PostgresConfig.fromEnvironment.map(config => new HikariPostgresConnectionProvider(config))
|
||||
|
||||
def requireConfigured(): Unit =
|
||||
if shared.isEmpty then
|
||||
throw new EagleInternalException(
|
||||
"Postgres requires EAGLE_POSTGRES_HOST, EAGLE_POSTGRES_DATABASE, " +
|
||||
"EAGLE_POSTGRES_USER, and EAGLE_POSTGRES_PASSWORD"
|
||||
)
|
||||
|
||||
def envNonEmpty(name: String): Option[String] =
|
||||
sys.env.get(name).map(_.trim).filter(_.nonEmpty)
|
||||
|
||||
def envLong(name: String, defaultValue: Long): Long =
|
||||
envNonEmpty(name).flatMap(_.toLongOption).getOrElse(defaultValue)
|
||||
|
||||
def envInt(name: String, defaultValue: Int): Int =
|
||||
envNonEmpty(name).flatMap(_.toIntOption).getOrElse(defaultValue)
|
||||
}
|
||||
|
||||
case class PostgresConfig(
|
||||
host: String,
|
||||
port: Int,
|
||||
database: String,
|
||||
user: String,
|
||||
password: String,
|
||||
sslmode: String,
|
||||
maxPoolSize: Int,
|
||||
minimumIdle: Int,
|
||||
connectionTimeoutMillis: Long,
|
||||
idleTimeoutMillis: Long,
|
||||
maxLifetimeMillis: Long,
|
||||
keepaliveTimeMillis: Long,
|
||||
leakDetectionThresholdMillis: Long
|
||||
) {
|
||||
def jdbcUrl: String =
|
||||
s"jdbc:postgresql://$host:$port/$database?sslmode=$sslmode"
|
||||
}
|
||||
|
||||
object PostgresConfig {
|
||||
def fromEnvironment: Option[PostgresConfig] =
|
||||
for
|
||||
host <- PostgresConnectionProvider.envNonEmpty("EAGLE_POSTGRES_HOST")
|
||||
database <- PostgresConnectionProvider.envNonEmpty("EAGLE_POSTGRES_DATABASE")
|
||||
user <- PostgresConnectionProvider.envNonEmpty("EAGLE_POSTGRES_USER")
|
||||
password <- PostgresConnectionProvider.envNonEmpty("EAGLE_POSTGRES_PASSWORD")
|
||||
yield PostgresConfig(
|
||||
host = host,
|
||||
port = PostgresConnectionProvider.envInt("EAGLE_POSTGRES_PORT", 5432),
|
||||
database = database,
|
||||
user = user,
|
||||
password = password,
|
||||
sslmode = PostgresConnectionProvider.envNonEmpty("EAGLE_POSTGRES_SSLMODE").getOrElse("require"),
|
||||
maxPoolSize = PostgresConnectionProvider.envInt("EAGLE_POSTGRES_MAX_POOL_SIZE", 10),
|
||||
minimumIdle = PostgresConnectionProvider.envInt("EAGLE_POSTGRES_MIN_IDLE", 0),
|
||||
connectionTimeoutMillis = PostgresConnectionProvider.envLong("EAGLE_POSTGRES_CONNECTION_TIMEOUT_MILLIS", 10000L),
|
||||
idleTimeoutMillis = PostgresConnectionProvider.envLong("EAGLE_POSTGRES_IDLE_TIMEOUT_MILLIS", 120000L),
|
||||
maxLifetimeMillis = PostgresConnectionProvider.envLong("EAGLE_POSTGRES_MAX_LIFETIME_MILLIS", 1500000L),
|
||||
keepaliveTimeMillis = PostgresConnectionProvider.envLong("EAGLE_POSTGRES_KEEPALIVE_TIME_MILLIS", 300000L),
|
||||
leakDetectionThresholdMillis =
|
||||
PostgresConnectionProvider.envLong("EAGLE_POSTGRES_LEAK_DETECTION_THRESHOLD_MILLIS", 60000L)
|
||||
)
|
||||
}
|
||||
|
||||
private class HikariPostgresConnectionProvider(config: PostgresConfig) extends PostgresConnectionProvider {
|
||||
private val hikariConfig = {
|
||||
val cfg = new HikariConfig()
|
||||
cfg.setJdbcUrl(config.jdbcUrl)
|
||||
cfg.setUsername(config.user)
|
||||
cfg.setPassword(config.password)
|
||||
cfg.setPoolName("eagle-postgres")
|
||||
cfg.setMaximumPoolSize(config.maxPoolSize)
|
||||
cfg.setMinimumIdle(config.minimumIdle)
|
||||
cfg.setConnectionTimeout(config.connectionTimeoutMillis)
|
||||
cfg.setIdleTimeout(config.idleTimeoutMillis)
|
||||
cfg.setMaxLifetime(config.maxLifetimeMillis)
|
||||
cfg.setKeepaliveTime(config.keepaliveTimeMillis)
|
||||
cfg.setLeakDetectionThreshold(config.leakDetectionThresholdMillis)
|
||||
cfg
|
||||
}
|
||||
|
||||
private val dataSource = new HikariDataSource(hikariConfig)
|
||||
Runtime.getRuntime.addShutdownHook(new Thread(() => close()))
|
||||
|
||||
SimpleTimedLogger.printLogger.logLine(
|
||||
s"Initialized Hikari Postgres connection provider: " +
|
||||
s"maxPoolSize=${config.maxPoolSize}, minimumIdle=${config.minimumIdle}, " +
|
||||
s"connectionTimeoutMillis=${config.connectionTimeoutMillis}, idleTimeoutMillis=${config.idleTimeoutMillis}, " +
|
||||
s"maxLifetimeMillis=${config.maxLifetimeMillis}, keepaliveTimeMillis=${config.keepaliveTimeMillis}, " +
|
||||
s"leakDetectionThresholdMillis=${config.leakDetectionThresholdMillis}"
|
||||
)
|
||||
|
||||
override def getConnection(description: String): Connection = {
|
||||
val start = System.currentTimeMillis()
|
||||
val connection = dataSource.getConnection()
|
||||
val duration = System.currentTimeMillis() - start
|
||||
if duration > 500 then
|
||||
SimpleTimedLogger.printLogger.logLine(
|
||||
s"Waited ${duration}ms for Postgres connection: $description; " +
|
||||
s"active=${dataSource.getHikariPoolMXBean.getActiveConnections}, " +
|
||||
s"idle=${dataSource.getHikariPoolMXBean.getIdleConnections}, " +
|
||||
s"total=${dataSource.getHikariPoolMXBean.getTotalConnections}, " +
|
||||
s"waiting=${dataSource.getHikariPoolMXBean.getThreadsAwaitingConnection}"
|
||||
)
|
||||
connection
|
||||
}
|
||||
|
||||
override def close(): Unit =
|
||||
SimpleTimedLogger.printLogger.logLine("Closing Hikari Postgres connection provider")
|
||||
dataSource.close()
|
||||
}
|
||||
@@ -692,6 +692,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/run_status",
|
||||
"//src/main/scala/net/eagle0/eagle/postgres:postgres_connection_provider",
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence:persister",
|
||||
"//src/main/scala/net/eagle0/eagle/service/persistence:save_directory",
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package net.eagle0.eagle.service
|
||||
|
||||
import java.sql.{Connection, DriverManager, Types}
|
||||
import java.sql.{Connection, Types}
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
import scala.collection.mutable
|
||||
@@ -14,6 +14,7 @@ import net.eagle0.eagle.model.action_result.ActionResultT
|
||||
import net.eagle0.eagle.model.proto_converters.game_state.GameStateConverter
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.game_state.GameState
|
||||
import net.eagle0.eagle.postgres.PostgresConnectionProvider
|
||||
import net.eagle0.shardok.api.action_result_view.ActionResultView as ShardokActionResultView
|
||||
import net.eagle0.shardok.api.command_descriptor.AvailableCommands as ShardokAvailableCommands
|
||||
import net.eagle0.shardok.storage.action_result.ActionResult as ShardokActionResult
|
||||
@@ -676,7 +677,7 @@ object PostgresHistory {
|
||||
}
|
||||
|
||||
def loaded(gameId: GameId): Option[PostgresHistory] =
|
||||
config.map { _ =>
|
||||
PostgresConnectionProvider.shared.map { _ =>
|
||||
val connection = openConnection()
|
||||
try {
|
||||
val schemaName = schemaNameForGame(gameId)
|
||||
@@ -705,37 +706,17 @@ object PostgresHistory {
|
||||
}.getOrElse(None)
|
||||
|
||||
def requireConfigured(): Unit =
|
||||
if config.isEmpty then
|
||||
throw new EagleInternalException(
|
||||
"HistoryBackend=postgres requires EAGLE_POSTGRES_HOST, EAGLE_POSTGRES_DATABASE, " +
|
||||
"EAGLE_POSTGRES_USER, and EAGLE_POSTGRES_PASSWORD"
|
||||
)
|
||||
|
||||
private def config: Option[PostgresConfig] =
|
||||
for
|
||||
host <- envNonEmpty("EAGLE_POSTGRES_HOST")
|
||||
database <- envNonEmpty("EAGLE_POSTGRES_DATABASE")
|
||||
user <- envNonEmpty("EAGLE_POSTGRES_USER")
|
||||
password <- envNonEmpty("EAGLE_POSTGRES_PASSWORD")
|
||||
yield PostgresConfig(
|
||||
host = host,
|
||||
port = sys.env.get("EAGLE_POSTGRES_PORT").flatMap(_.toIntOption).getOrElse(5432),
|
||||
database = database,
|
||||
user = user,
|
||||
password = password,
|
||||
sslmode = sys.env.get("EAGLE_POSTGRES_SSLMODE").filter(_.nonEmpty).getOrElse("require")
|
||||
)
|
||||
PostgresConnectionProvider.requireConfigured()
|
||||
|
||||
private def openConnection(): Connection = {
|
||||
val cfg = config.getOrElse(
|
||||
val provider = PostgresConnectionProvider.shared.getOrElse(
|
||||
throw new EagleInternalException(
|
||||
"EAGLE_HISTORY_BACKEND=postgres requires EAGLE_POSTGRES_HOST, DATABASE, USER, and PASSWORD"
|
||||
)
|
||||
)
|
||||
Class.forName("org.postgresql.Driver")
|
||||
val connection = DriverManager.getConnection(cfg.jdbcUrl, cfg.user, cfg.password)
|
||||
val connection = provider.getConnection("PostgresHistory")
|
||||
val count = openConnections.incrementAndGet()
|
||||
SimpleTimedLogger.printLogger.logLine(s"PostgresHistory opened connection; openHistoryConnections=$count")
|
||||
SimpleTimedLogger.printLogger.logLine(s"PostgresHistory borrowed connection; openHistoryConnections=$count")
|
||||
connection
|
||||
}
|
||||
|
||||
@@ -746,13 +727,10 @@ object PostgresHistory {
|
||||
private def recordClosed(description: String): Unit = {
|
||||
val count = openConnections.decrementAndGet()
|
||||
SimpleTimedLogger.printLogger.logLine(
|
||||
s"PostgresHistory closed connection for $description; openHistoryConnections=$count"
|
||||
s"PostgresHistory returned connection for $description; openHistoryConnections=$count"
|
||||
)
|
||||
}
|
||||
|
||||
private def envNonEmpty(name: String): Option[String] =
|
||||
sys.env.get(name).map(_.trim).filter(_.nonEmpty)
|
||||
|
||||
private def schemaNameForGame(gameId: GameId): String = {
|
||||
val schema = "eagle_game_" + java.lang.Long.toHexString(gameId)
|
||||
if !schema.matches("[a-z][a-z0-9_]*") then
|
||||
@@ -761,7 +739,7 @@ object PostgresHistory {
|
||||
}
|
||||
|
||||
private[service] def dropGameSchema(gameId: GameId): Unit =
|
||||
config.foreach { _ =>
|
||||
PostgresConnectionProvider.shared.foreach { _ =>
|
||||
val connection = openConnection()
|
||||
val schemaName = schemaNameForGame(gameId)
|
||||
try {
|
||||
@@ -772,7 +750,7 @@ object PostgresHistory {
|
||||
}
|
||||
|
||||
private[service] def exists(gameId: GameId): Boolean =
|
||||
config.exists { _ =>
|
||||
PostgresConnectionProvider.shared.exists { _ =>
|
||||
val connection = openConnection()
|
||||
try schemaExists(connection, schemaNameForGame(gameId))
|
||||
finally closeOpenedConnection(connection, s"exists check for game ${gameId.toHexString}")
|
||||
@@ -1073,18 +1051,6 @@ object PostgresHistory {
|
||||
} finally stmt.close()
|
||||
}
|
||||
|
||||
private case class PostgresConfig(
|
||||
host: String,
|
||||
port: Int,
|
||||
database: String,
|
||||
user: String,
|
||||
password: String,
|
||||
sslmode: String
|
||||
) {
|
||||
def jdbcUrl: String =
|
||||
s"jdbc:postgresql://$host:$port/$database?sslmode=$sslmode"
|
||||
}
|
||||
|
||||
extension (history: PostgresHistory)
|
||||
private def writeStartingSnapshot(startingState: GameState): Unit = {
|
||||
val stmt = history.connection.prepareStatement(
|
||||
|
||||
Reference in New Issue
Block a user