CORDA-2646 - Database connection pools leaking memory on every checkpoint (#4773)

* ENT-3053 Database connection pools leaking memory on every checkpoint. Flip in the thread local from the thread into the fiber.

* Back port to OS (needs some gradle changes) and added TODO, ability for it to avoid erroring if not using Hikari.

* Review feedback to remove warning.
This commit is contained in:
Rick Parker
2019-02-20 11:28:32 +00:00
committed by Katelyn Baker
parent 96134c8cfa
commit 6c4433d0b5
6 changed files with 56 additions and 4 deletions

View File

@ -1,6 +1,9 @@
package net.corda.nodeapi.internal.persistence
import co.paralleluniverse.strands.Strand
import com.zaxxer.hikari.HikariDataSource
import com.zaxxer.hikari.pool.HikariPool
import com.zaxxer.hikari.util.ConcurrentBag
import net.corda.core.internal.NamedCacheFactory
import net.corda.core.schemas.MappedSchema
import net.corda.core.utilities.contextLogger
@ -9,9 +12,10 @@ import rx.Observable
import rx.Subscriber
import rx.subjects.UnicastSubject
import java.io.Closeable
import java.lang.reflect.Field
import java.sql.Connection
import java.sql.SQLException
import java.util.UUID
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicInteger
@ -254,6 +258,24 @@ class CordaPersistence(
// DataSource doesn't implement AutoCloseable so we just have to hope that the implementation does so that we can close it
(_dataSource as? AutoCloseable)?.close()
}
val hikariPoolThreadLocal: ThreadLocal<List<Object>>? by lazy(LazyThreadSafetyMode.PUBLICATION) {
val hikariDataSource = dataSource as? HikariDataSource
if (hikariDataSource == null) {
null
} else {
val poolField: Field = HikariDataSource::class.java.getDeclaredField("pool")
poolField.isAccessible = true
val pool: HikariPool = poolField.get(hikariDataSource) as HikariPool
val connectionBagField: Field = HikariPool::class.java.getDeclaredField("connectionBag")
connectionBagField.isAccessible = true
val connectionBag: ConcurrentBag<ConcurrentBag.IConcurrentBagEntry> = connectionBagField.get(pool) as ConcurrentBag<ConcurrentBag.IConcurrentBagEntry>
val threadListField: Field = ConcurrentBag::class.java.getDeclaredField("threadList")
threadListField.isAccessible = true
val threadList: ThreadLocal<List<Object>> = threadListField.get(connectionBag) as ThreadLocal<List<Object>>
threadList
}
}
}
/**