[CORDA-1411]: Prevent MappedSchema caching from leaking memory. (#3042)

This commit is contained in:
Michele Sollecito 2018-05-01 19:32:29 +07:00 committed by GitHub
parent eb0fbb03e3
commit 5565b3e80d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 26 additions and 5 deletions

View File

@ -42,6 +42,26 @@ open class MappedSchema(schemaFamily: Class<*>,
val mappedTypes: Iterable<Class<*>>) {
val name: String = schemaFamily.name
override fun toString(): String = "${this.javaClass.simpleName}(name=$name, version=$version)"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MappedSchema
if (version != other.version) return false
if (mappedTypes != other.mappedTypes) return false
if (name != other.name) return false
return true
}
override fun hashCode(): Int {
var result = version
result = 31 * result + mappedTypes.hashCode()
result = 31 * result + name.hashCode()
return result
}
}
//DOCEND MappedSchema

View File

@ -6,6 +6,7 @@ release, see :doc:`upgrade-notes`.
Unreleased
==========
* Avoided a memory leak deriving from incorrect MappedSchema caching strategy.
* Added program line argument ``on-unknown-config-keys`` to allow specifying behaviour on unknown node configuration property keys.
Values are: [FAIL, WARN, IGNORE], default to FAIL if unspecified.

View File

@ -25,7 +25,8 @@ data class DatabaseConfig(
val initialiseSchema: Boolean = true,
val serverNameTablePrefix: String = "",
val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ,
val exportHibernateJMXStatistics: Boolean = false
val exportHibernateJMXStatistics: Boolean = false,
val mappedSchemaCacheSize: Long = 100
)
// This class forms part of the node config and so any changes to it must be handled with care

View File

@ -1,5 +1,6 @@
package net.corda.nodeapi.internal.persistence
import com.github.benmanes.caffeine.cache.Caffeine
import net.corda.core.internal.castIfPossible
import net.corda.core.schemas.MappedSchema
import net.corda.core.utilities.contextLogger
@ -21,7 +22,6 @@ import org.hibernate.type.descriptor.sql.BlobTypeDescriptor
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor
import java.lang.management.ManagementFactory
import java.sql.Connection
import java.util.concurrent.ConcurrentHashMap
import javax.management.ObjectName
import javax.persistence.AttributeConverter
@ -35,8 +35,7 @@ class HibernateConfiguration(
private val logger = contextLogger()
}
// TODO: make this a guava cache or similar to limit ability for this to grow forever.
private val sessionFactories = ConcurrentHashMap<Set<MappedSchema>, SessionFactory>()
private val sessionFactories = Caffeine.newBuilder().maximumSize(databaseConfig.mappedSchemaCacheSize).build<Set<MappedSchema>, SessionFactory>()
val sessionFactoryForRegisteredSchemas = schemas.let {
logger.info("Init HibernateConfiguration for schemas: $it")
@ -44,7 +43,7 @@ class HibernateConfiguration(
}
/** @param key must be immutable, not just read-only. */
fun sessionFactoryForSchemas(key: Set<MappedSchema>) = sessionFactories.computeIfAbsent(key, { makeSessionFactoryForSchemas(key) })
fun sessionFactoryForSchemas(key: Set<MappedSchema>): SessionFactory = sessionFactories.get(key, ::makeSessionFactoryForSchemas)!!
private fun makeSessionFactoryForSchemas(schemas: Set<MappedSchema>): SessionFactory {
logger.info("Creating session factory for schemas: $schemas")