Kotlin + JUnit + postgres for integration testing: leaked connections - postgresql

I set up a test class that runs 13 integration tests. All of the tests involve 1 database, 1 schema and 3 tables in the schema. The test class is set up so that I create the test database with #BeforeClass and delete the test database with #AfterClass. In the setUp phase for each test, I migrate the schema and populate the tables with artificial data. Then in the tearDown phase I remove the schema. In each test, I run my test logic against the test database:
class MyTest {
companion object {
private val helper = MyHelper()
#BeforeClass #JvmStatic
fun createTestDatabase() {
helper.createTestDatabase()
}
#AfterClass #JvmStatic
fun removeTestDatabase() {
helper.deleteTestDatabase()
}
}
#Before
fun setUp(){
// migrate the schema and populate the tables with artificial data
helper.migrate()
helper.populate()
}
#After
fun tearDown() {
// remove the schema
helper.removeSchema()
}
#Test MyFirstTest() {
// populate the tables with artificial data
... // test logic
}
...
}
The database manipulations are all implemented in MyHelper class. In particular, I have one function in MyHelper that executes a query string:
class MyHelper {
protected val POSTGRES_USER = "postgres"
protected val POSTGRES_HOST = "localhost"
protected val POSTGRES_PORT = 5432
protected val POSTGRES_TEST_DATA_DATABASE = "my_database_test"
protected val POSTGRES_TEST_DATA_SCHEMA = "my_schema"
protected val POSTGRES_DEFAULT_DATABASE = "postgres"
protected val POSTGRES_DEFAULT_SCHEMA = "postgres"
protected val graphDataTables = listOf("my_table_1", "my_table_2", "my_table_3")
val databases = listOf(POSTGRES_DEFAULT_DATABASE, POSTGRES_TEST_DATA_DATABASE)
val dataSources = databases.associateBy({it}, { getDataSource(it)})
fun getDataSource(database: String): PGSimpleDataSource {
val ds = PGSimpleDataSource()
val url = "jdbc:postgresql://$POSTGRES_HOST:$POSTGRES_PORT/$database"
ds.setUrl(url)
ds.setUser(POSTGRES_USER)
return ds
}
fun executeQuery(sqlQuery: String,
database: String = this.POSTGRES_DEFAULT_DATABASE,
schema: String = this.POSTGRES_DEFAULT_SCHEMA
): Unit {
val ds = dataSources[database]!!
ds.currentSchema = schema
ds.connection.use {
it.createStatement().use {
it.execute(sqlQuery)
}
}
}
...
}
The function MyHelper.populate() uses the executeQuery function to insert data into the tables.
My problem is that it seems that I have many unclosed connections (leaked connections). If I keep the # of tests under 10, then all tests pass without a problem. As soon as I pass 10, I get errors like the following for tests 11 and up:
Nov 09, 2017 11:31:55 AM org.postgresql.Driver connect
SEVERE: Connection error:
org.postgresql.util.PSQLException: FATAL: sorry, too many clients already
at org.postgresql.Driver$ConnectThread.getResult(Driver.java:401)
at org.postgresql.Driver.connect(Driver.java:259)
...
for the tests that got run after the 10th. And the last stack in the log points to the ds.connection.use { line in the function executeQuery.
However, if I move the helper.createTestDatabase() out of the companion object, and put it in the init block for MyTest, like so:
class MyTest {
companion object {
private val helper = MyHelper()
}
init {
helper.createTestDatabase()
}
...
}
then I don't get any error even if the # of my tests are over 10. The problem I see with this is that now I have a test database created in my postgres docker container and it still exists after I'm done with the integration tests. I have no idea how to call removeTestDatabase now.
I run my tests in debug mode and stopped in the middle of the 10th test, then with
select * from pg_stat_activity;
I can see that there are many idle connections (almost 100) in the first scenario. In the 2nd scenario, I have only 10 idle connections in the middle of the 10th test (I'd like the number of idle connections to be 0, of course). My understanding is that my default postgres setting is that max_connections is set to 100. That's why I get the error when I have more than 10 tests in the first scenario. But I have no idea how I am creating idle connections.

Related

Is there a way to avoid cold start with Cloud SQL and Cloud Functions (using JVM/Scala)? [duplicate]

This question already has answers here:
How can I keep Google Cloud Functions warm?
(8 answers)
Closed 7 months ago.
I have implemented a cloud function that accesses a postgres DB per the documentation like this...
import java.util.Properties
import javax.sql.DataSource
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import io.github.cdimascio.dotenv.Dotenv
import java.sql.Connection
class CoreDataSource {
def getConnection = {
println("Getting the connection")
CoreDataSource.getConnection
}
}
object CoreDataSource {
var pool : Option[DataSource] = None
def getConnection: Option[Connection] = {
if(pool.isEmpty) {
println("Getting the datasource")
pool = getDataSource
}
if(pool.isEmpty){
None
} else {
println("Reusing the connection")
Some(pool.get.getConnection)
}
}
def getDataSource: Option[DataSource] = {
Class.forName("org.postgresql.Driver")
var dbName,dbUser,dbPassword,dbUseIAM,ssoMode, instanceConnectionName = ""
val dotenv = Dotenv
.configure()
.ignoreIfMissing()
.load()
dbName = dotenv.get("DB_NAME")
println("DB Name "+ dbName)
dbUser= dotenv.get("DB_USER")
println("DB User "+ dbUser)
dbPassword = Option(
dotenv.get("DB_PASS")
).getOrElse("ignored")
dbUseIAM = Option(
dotenv.get("DB_IAM")
).getOrElse("true")
println("dbUseIAM "+ dbUseIAM)
ssoMode = Option(
dotenv.get("DB_SSL")
).getOrElse("disable") // TODO: Should this be enabled by default?
println("ssoMode "+ ssoMode)
instanceConnectionName = dotenv.get("DB_INSTANCE")
println("instanceConnectionName "+ instanceConnectionName)
val jdbcURL: String = String.format("jdbc:postgresql:///%s", dbName)
val connProps = new Properties
connProps.setProperty("user", dbUser)
// Note: a non-empty string value for the password property must be set. While this property will be ignored when connecting with the Cloud SQL Connector using IAM auth, leaving it empty will cause driver-level validations to fail.
if( dbUseIAM.equals("true") ){
println("Using IAM password is ignored")
connProps.setProperty("password", "ignored")
} else {
println("Using manual, password must be provided")
connProps.setProperty("password", dbPassword)
}
connProps.setProperty("sslmode", ssoMode)
connProps.setProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory")
connProps.setProperty("cloudSqlInstance", instanceConnectionName)
connProps.setProperty("enableIamAuth", dbUseIAM)
// Initialize connection pool
val config = new HikariConfig
config.setJdbcUrl(jdbcURL)
config.setDataSourceProperties(connProps)
config.setMaximumPoolSize(10)
config.setMinimumIdle(4)
config.addDataSourceProperty("ipTypes", "PUBLIC,PRIVATE") // TODO: Make configureable
println("Config created")
val pool : DataSource = new HikariDataSource(config) // Do we really need Hikari here if it doesn't need pooling?
println("Returning the datasource")
Some(pool)
}
}
class DoSomething() {
val ds = new CoreDataSource
def getUserInformation(): String = {
println("Getting user information")
connOpt = ds.getConnection
if(connOpt.isEmpty) throw new Error("No Connection Found")
...
}
}
class SomeClass extends HttpFunction {
override def service(httpRequest: HttpRequest, httpResponse: HttpResponse): Unit = {
httpResponse.setContentType("application/json")
httpResponse.getWriter.write(
GetCorporateInformation.corp.getUserInformation( )
)
}
}
object GetCorporateInformation {
val corp = new CorporateInformation()
}
And I deploy like this...
gcloud functions deploy identity-corporate --entry-point ... --min-instances 2 --runtime java17 --trigger-http --no-allow-unauthenticated --set-secrets '...'
But when first deployed (and after sitting idle for a while) the function takes 25 secs to return causing all kinds of issues with SLAs. After the "cold start" it returns quickly but at least in dev I can't really make sure someone is always hitting it.
Is there a way to mitigate this or do I need to use a VM to make sure it isn't destroyed? Or is there a way to do this without the overhead of pooling?
Since functions are stateless, your function sometimes initializes the execution environment from scratch, which is called a cold start. However, you can minimize the impact of cold start by setting a minimum number of instances (Note that this can help reduce but not eliminate) or you could create a scheduled function warmer that runs every few minutes and calls your high priority function ensuring they are kept warm.

How to dispose database connections when flink retarts

I use dbcp2.BasicDataSource as database-connection-pool. The database query is used in some map function to get additional info of sensors; I found out that, when the flink job restarts due to exceptions, the old DB connections are still active on the server side.
flink version 1.7
BasicDataSource construct code here
object DbHelper extends Lazing with Logging {
private lazy val connectionPool: BasicDataSource = createDataSource()
private def createDataSource(): BasicDataSource = {
val conn_str = props.getProperty("db.url")
val conn_user = props.getProperty("db.user")
val conn_pwd = props.getProperty("db.pwd")
val initialSize = props.getProperty("db.initial.size", "3").toInt
val bds = new BasicDataSource
bds.setDriverClassName("org.postgresql.Driver")
bds.setUrl(conn_str)
bds.setUsername(conn_user)
bds.setPassword(conn_pwd)
bds.setInitialSize(initialSize)
bds
}
}
Change your map function to a RichMapFunction. Override the close() method of the RichMapFunction and put the code to close your database connection there. You should likely be putting the code to open the connection in the open() method as well.

Play evolution not applied in custom Slick environment configuration

DESCRIPTION:
Hi. I am using Play framework and Slick and PostgreSQL for my application. So I design CI_Pipelines and configure them in my application.conf.When we set slick configuration like this:
play.evolutions.db.default {
enabled = true
autoApply=true
}
slick.dbs.default {
driver="slick.driver.PostgresDriver$"
db {
driver=org.postgresql.Driver
dbName=dbName
url="jdbc:postgresql://127.0.0.1/dbName"
user=***
password=***
}
}
and in codes (dao files):
#Singleton
class UserDao #Inject()(
protected val dbConfigProvider: DatabaseConfigProvider
)(implicit val ex: ExecutionContext) extends HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
val userTableQuery = TableQuery[UserTable]
everything works all write such as EVOLUTION that play provided for us.
But if you want to setup other environments such as staging or production you will fail :D.
I read this documentation of Slick you can read it from here that is perfect for writing a successful config file. so I write it like this:
com.my.org {
env = "development"
env = ${?MY_ENV}
development {
db {
dataSourceClass = "slick.jdbc.DatabaseUrlDataSource"
properties = {
driver = "slick.driver.PostgresDriver$"
user = "myuser"
password = "*****"
url = "jdbc:postgresql://myIP/dbName"
}
numThreads = 10
}
}
staging {
db {
ip=186.14.*.*
...
}
}
production {
db {
ip=196.82.*.*
...
}
}
}
** The important thing that you must attention to it, is my PostgreSQL is outside of my (docker container) so I must connect to it remotely.
and in code we have :
class UserDao #Inject()(
)(implicit val ex: ExecutionContext) {
import driver.api._
val db = Database.forConfig(s"$prefix.db")
val userTableQuery = TableQuery[UserTable]
PROBLEM:
Problem is now play evolution does not applied.
QUESTION:
I need to know how to implement one of this (to solve my problems):
how to apply play evolution in this way described before (in problem part) ?
how to setup my environments in better way ?
A friend of mine consulted me about the problem [over the phone] and here is the solution we came up with:
slick.dbs.default.driver = "slick.driver.PostgresDriver$"
slick.dbs.default.db {
driver = org.postgresql.Driver
ip = localhost
dbName = ***
user = ***
password = "***"
url="jdbc:postgresql://postgresql/"${slick.dbs.default.db.dbName}
}
You can also use Docker to create a docker network and set your PostgreSQL container name instead of your IP address.
Also, if you want to be be able to configure the IP address, say from jenkins or Play_Runtime_Guice, you can use this:
url="jdbc:postgresql://"${?POSTGRESQL_IP}"/dbName"

orientdb: how to fully shutdown down a memory database (Java/Scala API)

I'm trying to write some unit test utilities for an orientDB client in scala.
The following is intended to take a function to operate on a DB, and it should wrap the function with code to create and destroy the DB for a single unit test.
However, there doesn't see to be much good documentation on how to clean up a memory DB (and looking at many open source projects, people seem to simply just leak databases and create new ones on a new port).
Simply calling db.close leaves the DB listening to a port and subsequent tests fail. Calling db.drop seems to work, but only if the func succeeded in adding data to the DB.
So, what cleanup is required in the finally clause?
#Test
def fTest2(): Unit = {
def withJSONDBLoan(func: ODatabaseDocumentTx => Unit) : Unit = {
val db: ODatabaseDocumentTx = new ODatabaseDocumentTx("memory:jsondb")
db.create()
try {
func(db)
} finally {
if (!db.isClosed){
db.close // Nope. DB is leaked.
}
// db.drop seems to close the DB but can't
// see when to safely call this.
}
}
val query1 = "insert into ouser set name='test',password='test', status='ACTIVE'"
withJSONDBLoan { db =>
db.command(new OCommandSQL(query1)).execute[ODocument]()
}
// Fails at create because DB already exists.
val query2 = "insert into ouser set name='test2',password='test2', status='ACTIVE'"
withJSONDBLoan { db =>
db.command(new OCommandSQL(query2)).execute[ODocument]()
}
}
I tried your code and it worked for me.
Hope it helps.

Spray, Slick, Spark - OutOfMemoryError: PermGen space

I have successfully implemented a simple web service using Spray and Slick that passes an incoming request through a Spark ML Prediction Pipeline. Everything was working fine until I tried to add a data layer. I have chosen Slick it seems to be popular.
However, I can't quite get it to work right. I have been basing most of my code on the Hello-Slick Activator Template. I use a DAO object like so:
object dataDAO {
val datum = TableQuery[Datum]
def dbInit = {
val db = Database.forConfig("h2mem1")
try {
Await.result(db.run(DBIO.seq(
datum.schema.create
)), Duration.Inf)
} finally db.close
}
def insertData(data: Data) = {
val db = Database.forConfig("h2mem1")
try {
Await.result(db.run(DBIO.seq(
datum += data,
datum.result.map(println)
)), Duration.Inf)
} finally db.close
}
}
case class Data(data1: String, data2: String)
class Datum(tag: Tag) extends Table[Data](tag, "DATUM") {
def data1 = column[String]("DATA_ONE", O.PrimaryKey)
def data2 = column[String]("DATA_TWO")
def * = (data1, data2) <> (Data.tupled, Data.unapply)
}
I initialize my database in my Boot object
object Boot extends App {
implicit val system = ActorSystem("raatl-demo")
Classifier.initializeData
PredictionDAO.dbInit
// More service initialization code ...
}
I try to add a record to my database before completing the service request
val predictionRoute = {
path("data") {
get {
parameter('q) { query =>
// do Spark stuff to get prediction
DataDAO.insertData(data)
respondWithMediaType(`application/json`) {
complete {
DataJson(data1, data2)
}
}
}
}
}
When I send a request to my service my application crashes
java.lang.OutOfMemoryError: PermGen space
I suspect I'm implementing the Slick API incorrectly. its hard to tell from the documentation, because it stuffs all the operations into a main method.
Finally, my conf is the same as the activator ui
h2mem1 = {
url = "jdbc:h2:mem:raatl"
driver = org.h2.Driver
connectionPool = disabled
keepAliveConnection = true
}
Has anyone encountered this before? I'm using Slick 3.1
java.lang.OutOfMemoryError: PermGen space is normally not a problem with your usage, here is what oracle says about this.
The detail message PermGen space indicates that the permanent generation is full. The permanent generation is the area of the heap where class and method objects are stored. If an application loads a very large number of classes, then the size of the permanent generation might need to be increased using the -XX:MaxPermSize option.
I do not think this is because of incorrect implementation of the Slick API. This probably happens because you are using multiple frameworks that loads many classes.
Your options are:
Increase the size of perm gen size -XX:MaxPermSize
Upgrade to Java 8. The perm gen space is now replaced with MetaSpace which is tuned automatically