diff --git a/core/src/main/kotlin/net/corda/core/internal/NamedCache.kt b/core/src/main/kotlin/net/corda/core/internal/NamedCache.kt index 1d6e9e90c9..2a96de4835 100644 --- a/core/src/main/kotlin/net/corda/core/internal/NamedCache.kt +++ b/core/src/main/kotlin/net/corda/core/internal/NamedCache.kt @@ -4,7 +4,6 @@ import com.github.benmanes.caffeine.cache.Cache import com.github.benmanes.caffeine.cache.CacheLoader import com.github.benmanes.caffeine.cache.Caffeine import com.github.benmanes.caffeine.cache.LoadingCache -import net.corda.core.internal.profiling.CacheTracing.Companion.wrap /** * Restrict the allowed characters of a cache name - this ensures that each cache has a name, and that @@ -21,14 +20,16 @@ private val allowedChars = Regex("^[0-9A-Za-z_.]*\$") * This allows to easily add tweaks to all caches built in Corda, and also forces * cache users to give their cache a (meaningful) name that can be used e.g. for * capturing cache traces etc. + * + * Currently it is not used in this version of CORDA, but there are plans to do so. */ fun Caffeine.buildNamed(name: String): Cache { checkCacheName(name) - return wrap(this.build(), name) + return this.build() } fun Caffeine.buildNamed(name: String, loader: CacheLoader): LoadingCache { checkCacheName(name) - return wrap(this.build(loader), name) + return this.build(loader) } diff --git a/core/src/main/kotlin/net/corda/core/internal/profiling/TracingCacheWrapper.kt b/core/src/main/kotlin/net/corda/core/internal/profiling/TracingCacheWrapper.kt index 2e2ece5d06..9231a91afc 100644 --- a/core/src/main/kotlin/net/corda/core/internal/profiling/TracingCacheWrapper.kt +++ b/core/src/main/kotlin/net/corda/core/internal/profiling/TracingCacheWrapper.kt @@ -95,16 +95,6 @@ class CacheTracing { data class CacheTracingConfig(val enabled: Boolean, val targetDir: Path, val converter: (key: Any?) -> Long) companion object { - var cacheTracingConfig: CacheTracingConfig? = null - - fun wrap(cache: Cache, name: String): Cache { - return wrap(cache, cacheTracingConfig, name) - } - - fun wrap(cache: LoadingCache, name: String): LoadingCache { - return wrap(cache, cacheTracingConfig, name) - } - fun wrap(cache: Cache, config: CacheTracingConfig?, traceName: String): Cache { return if (config != null && config.enabled) TracingCacheWrapper(cache, getCollector(config.targetDir, traceName, config.converter)) else cache } diff --git a/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorkerServiceHub.kt b/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorkerServiceHub.kt index d2551d5933..9d5879a5ce 100644 --- a/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorkerServiceHub.kt +++ b/experimental/flow-worker/src/main/kotlin/net/corda/flowworker/FlowWorkerServiceHub.kt @@ -62,7 +62,8 @@ import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.upgrade.ContractUpgradeServiceImpl import net.corda.node.services.vault.NodeVaultService import net.corda.node.utilities.AffinityExecutor -import net.corda.node.utilities.DefaultNamedCacheFactory +import net.corda.node.utilities.EnterpriseNamedCacheFactory +import net.corda.node.utilities.profiling.getTracingConfig import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.isH2Database @@ -118,7 +119,7 @@ class FlowWorkerServiceHub(override val configuration: NodeConfiguration, overri } private val metricRegistry = MetricRegistry() - override val cacheFactory = DefaultNamedCacheFactory().bindWithConfig(configuration).bindWithMetrics(metricRegistry).tokenize() + override val cacheFactory = EnterpriseNamedCacheFactory(configuration.enterpriseConfiguration.getTracingConfig()).bindWithConfig(configuration).bindWithMetrics(metricRegistry).tokenize() override val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, false).tokenize() override val identityService = PersistentIdentityService(cacheFactory).tokenize() @@ -144,7 +145,7 @@ class FlowWorkerServiceHub(override val configuration: NodeConfiguration, overri override val keyManagementService = PersistentKeyManagementService(cacheFactory, identityService, database).tokenize() private val servicesForResolution = ServicesForResolutionImpl(identityService, attachments, cordappProvider, validatedTransactions) @Suppress("LeakingThis") - override val vaultService = NodeVaultService(clock, keyManagementService, servicesForResolution, database, schemaService).tokenize() + override val vaultService = NodeVaultService(clock, keyManagementService, servicesForResolution, database, schemaService, cacheFactory).tokenize() override val nodeProperties = NodePropertiesPersistentStore(StubbedNodeUniqueIdProvider::value, database) val flowLogicRefFactory = FlowLogicRefFactoryImpl(cordappLoader.appClassLoader) override val monitoringService = MonitoringService(metricRegistry).tokenize() diff --git a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorkerServiceHub.kt b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorkerServiceHub.kt index 314827bc1e..05a4927c9a 100644 --- a/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorkerServiceHub.kt +++ b/experimental/rpc-worker/src/main/kotlin/net/corda/rpcWorker/RpcWorkerServiceHub.kt @@ -47,7 +47,8 @@ import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.services.persistence.NodePropertiesPersistentStore import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.vault.NodeVaultService -import net.corda.node.utilities.DefaultNamedCacheFactory +import net.corda.node.utilities.EnterpriseNamedCacheFactory +import net.corda.node.utilities.profiling.getTracingConfig import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.isH2Database @@ -76,7 +77,7 @@ class RpcWorkerServiceHub(override val configuration: NodeConfiguration, overrid private val runOnStop = ArrayList<() -> Any?>() private val metricRegistry = MetricRegistry() - override val cacheFactory = DefaultNamedCacheFactory().bindWithConfig(configuration).bindWithMetrics(metricRegistry) + override val cacheFactory = EnterpriseNamedCacheFactory(configuration.enterpriseConfiguration.getTracingConfig()).bindWithConfig(configuration).bindWithMetrics(metricRegistry) override val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, false) override val identityService = PersistentIdentityService(cacheFactory) @@ -104,7 +105,7 @@ class RpcWorkerServiceHub(override val configuration: NodeConfiguration, overrid override val keyManagementService = PersistentKeyManagementService(cacheFactory, identityService, database) private val servicesForResolution = ServicesForResolutionImpl(identityService, attachments, cordappProvider, validatedTransactions) @Suppress("LeakingThis") - override val vaultService = NodeVaultService(clock, keyManagementService, servicesForResolution, database, schemaService) + override val vaultService = NodeVaultService(clock, keyManagementService, servicesForResolution, database, schemaService, cacheFactory) override val nodeProperties = NodePropertiesPersistentStore(StubbedNodeUniqueIdProvider::value, database) override val monitoringService = MonitoringService(metricRegistry) override val networkMapUpdater = NetworkMapUpdater( diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 313450a928..7cab79c96d 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -943,7 +943,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, services, database, schemaService, - configuration.transactionCacheSizeBytes + cacheFactory ) } diff --git a/node/src/main/kotlin/net/corda/node/internal/EnterpriseNode.kt b/node/src/main/kotlin/net/corda/node/internal/EnterpriseNode.kt index a11784b2d5..1b40d7500f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/EnterpriseNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/EnterpriseNode.kt @@ -10,7 +10,6 @@ import net.corda.core.crypto.newSecureRandom import net.corda.core.identity.CordaX500Name import net.corda.core.internal.Emoji import net.corda.core.internal.concurrent.thenMatch -import net.corda.core.internal.profiling.CacheTracing import net.corda.core.node.NodeInfo import net.corda.core.utilities.loggerFor import net.corda.node.VersionInfo @@ -19,6 +18,7 @@ import net.corda.node.services.config.RelayConfiguration import net.corda.node.services.statemachine.MultiThreadedStateMachineExecutor import net.corda.node.services.statemachine.MultiThreadedStateMachineManager import net.corda.node.services.statemachine.StateMachineManager +import net.corda.node.utilities.EnterpriseNamedCacheFactory import net.corda.node.utilities.profiling.getTracingConfig import org.fusesource.jansi.Ansi import org.fusesource.jansi.AnsiConsole @@ -29,10 +29,8 @@ import java.util.concurrent.TimeUnit open class EnterpriseNode(configuration: NodeConfiguration, versionInfo: VersionInfo, - initialiseSerialization: Boolean = true, - // running this as a constructor default parameter as this is the first thing that will be run when instantiating the class - initHelper: Unit = { CacheTracing.cacheTracingConfig = configuration.enterpriseConfiguration.getTracingConfig() }() -) : Node(configuration, versionInfo, initialiseSerialization) { + initialiseSerialization: Boolean = true +) : Node(configuration, versionInfo, initialiseSerialization, cacheFactoryPrototype = EnterpriseNamedCacheFactory(configuration.enterpriseConfiguration.getTracingConfig())) { companion object { private val logger by lazy { loggerFor() } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessageDeduplicator.kt b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessageDeduplicator.kt index 926130639a..35e6b45cd4 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessageDeduplicator.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessageDeduplicator.kt @@ -10,7 +10,6 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import java.time.Instant import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.TimeUnit import javax.persistence.Column import javax.persistence.Entity import javax.persistence.Id @@ -29,7 +28,7 @@ class P2PMessageDeduplicator(cacheFactory: NamedCacheFactory, private val databa private val processedMessages = createProcessedMessages(cacheFactory) // We add the peer to the key, so other peers cannot attempt malicious meddling with sequence numbers. // Expire after 7 days since we last touched an entry, to avoid infinite growth. - private val senderUUIDSeqNoHWM: MutableMap = Caffeine.newBuilder().expireAfterAccess(7, TimeUnit.DAYS).build().asMap() + private val senderUUIDSeqNoHWM: MutableMap = cacheFactory.buildNamed(Caffeine.newBuilder(), "P2PMessageDeduplicator_senderUUIDSeqNoHWM").asMap() private fun createProcessedMessages(cacheFactory: NamedCacheFactory): AppendOnlyPersistentMap { return AppendOnlyPersistentMap( diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index c23010e8c3..85f6312906 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -21,10 +21,9 @@ import net.corda.core.transactions.* import net.corda.core.utilities.* import net.corda.node.services.api.SchemaService import net.corda.node.services.api.VaultServiceInternal -import net.corda.node.services.config.KB -import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.schema.PersistentStateService import net.corda.node.services.statemachine.FlowStateMachineImpl +import net.corda.node.utilities.NamedCacheFactory import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit import net.corda.nodeapi.internal.persistence.currentDBSession @@ -61,7 +60,7 @@ class NodeVaultService( private val servicesForResolution: ServicesForResolution, private val database: CordaPersistence, private val schemaService: SchemaService, - transactionCacheSizeBytes: Long = NodeConfiguration.defaultTransactionCacheSize + cacheFactory: NamedCacheFactory ) : SingletonSerializeAsToken(), VaultServiceInternal { private companion object { private val log = contextLogger() @@ -87,10 +86,9 @@ class NodeVaultService( private val contractStateTypeMappings = mutableMapOf>() /** - * This caches what states are in the vault for a particular transaction. Size the cache based on one entry per 8KB of transaction cache. - * This size results in minimum of 1024. + * This caches what states are in the vault for a particular transaction. */ - private val producedStatesMapping = Caffeine.newBuilder().maximumSize(transactionCacheSizeBytes / 8.KB).buildNamed("NodeVaultService_producedStates") + private val producedStatesMapping = cacheFactory.buildNamed(Caffeine.newBuilder(), "NodeVaultService_producedStates") override fun start() { criteriaBuilder = database.hibernateConfig.sessionFactoryForRegisteredSchemas.criteriaBuilder diff --git a/node/src/main/kotlin/net/corda/node/utilities/CaffeineStatsCounter.kt b/node/src/main/kotlin/net/corda/node/utilities/CaffeineStatsCounter.kt new file mode 100644 index 0000000000..a0e33d84ff --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/utilities/CaffeineStatsCounter.kt @@ -0,0 +1,152 @@ +package net.corda.node.utilities + +import com.codahale.metrics.Counter +import com.codahale.metrics.Gauge +import com.codahale.metrics.MetricRegistry +import com.codahale.metrics.Timer +import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.CacheLoader +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.LoadingCache +import com.github.benmanes.caffeine.cache.stats.CacheStats +import com.github.benmanes.caffeine.cache.stats.StatsCounter +import net.corda.core.internal.buildNamed +import java.util.concurrent.TimeUnit +import java.util.function.Supplier + +/** + * Helper to export statistics to JMX and finally build the cache. + */ +fun Caffeine.buildNamed(registry: MetricRegistry, metricsPrefix: String): Cache = this.addMetrics(registry, metricsPrefix).buildNamed(metricsPrefix).addExtraMetrics(registry, metricsPrefix) + +fun Caffeine.buildNamed(registry: MetricRegistry, metricsPrefix: String, loader: CacheLoader): LoadingCache = this.recordStats(CaffeineStatsCounter.supplier(registry, "Caches/$metricsPrefix")).buildNamed(metricsPrefix, loader).addExtraMetrics(registry, metricsPrefix) + +private fun Caffeine.addMetrics(registry: MetricRegistry, metricsPrefix: String): Caffeine = this.recordStats(CaffeineStatsCounter.supplier(registry, "Caches/$metricsPrefix")) +private fun > C.addExtraMetrics(registry: MetricRegistry, metricsPrefix: String): C = this.apply { + registry.gauge("Caches/$metricsPrefix.size") { + object : Gauge { + override fun getValue(): Long { + return this@apply.estimatedSize() + } + } + } + this@apply.policy().eviction().ifPresent { + if (it.isWeighted) { + registry.gauge("Caches/$metricsPrefix.maximum-weight") { + object : Gauge { + override fun getValue(): Long { + return it.maximum + } + } + } + registry.gauge("Caches/$metricsPrefix.weight") { + object : Gauge { + override fun getValue(): Long { + return it.weightedSize().asLong + } + } + } + registry.gauge("Caches/$metricsPrefix.weightPercent") { + object : Gauge { + override fun getValue(): Long { + return minOf((it.weightedSize().asLong * 100L) / it.maximum, 100L) + } + } + } + } else { + registry.gauge("Caches/$metricsPrefix.maximum-size") { + object : Gauge { + override fun getValue(): Long { + return it.maximum + } + } + } + registry.gauge("Caches/$metricsPrefix.sizePercent") { + object : Gauge { + override fun getValue(): Long { + return minOf((this@apply.estimatedSize() * 100L) / it.maximum, 100L) + } + } + } + } + } +} + + +/** + * A {@link StatsCounter} backed by Dropwizard Metrics. + */ +class CaffeineStatsCounter : StatsCounter { + private val hitCount: Counter + private val missCount: Counter + private val loadSuccessCount: Counter + private val loadFailureCount: Counter + private val totalLoadTime: Timer + private val evictionCount: Counter + private val evictionWeight: Counter + + companion object { + /** + * Creates a supplier of instances for use by a single cache. + * + * @param registry the registry of metric instances + * @param metricsPrefix the prefix name for the metrics + */ + fun supplier(registry: MetricRegistry, metricsPrefix: String): Supplier { + return Supplier { CaffeineStatsCounter(registry, metricsPrefix) } + } + } + + private constructor(registry: MetricRegistry, metricsPrefix: String) { + hitCount = registry.counter("$metricsPrefix.hits") + missCount = registry.counter("$metricsPrefix.misses") + totalLoadTime = registry.timer("$metricsPrefix.loads") + loadSuccessCount = registry.counter("$metricsPrefix.loads-success") + loadFailureCount = registry.counter("$metricsPrefix.loads-failure") + evictionCount = registry.counter("$metricsPrefix.evictions") + evictionWeight = registry.counter("$metricsPrefix.evictions-weight") + } + + override fun recordHits(count: Int) { + hitCount.inc(count.toLong()) + } + + override fun recordMisses(count: Int) { + missCount.inc(count.toLong()) + } + + override fun recordLoadSuccess(loadTime: Long) { + loadSuccessCount.inc() + totalLoadTime.update(loadTime, TimeUnit.NANOSECONDS) + } + + override fun recordLoadFailure(loadTime: Long) { + loadFailureCount.inc() + totalLoadTime.update(loadTime, TimeUnit.NANOSECONDS) + } + + override fun recordEviction() { + // This method is scheduled for removal in version 3.0 in favor of recordEviction(weight) + recordEviction(1) + } + + override fun recordEviction(weight: Int) { + evictionCount.inc() + evictionWeight.inc(weight.toLong()) + } + + override fun snapshot(): CacheStats { + return CacheStats( + hitCount.getCount(), + missCount.getCount(), + loadSuccessCount.getCount(), + loadFailureCount.getCount(), + totalLoadTime.getCount(), + evictionCount.getCount(), + evictionWeight.getCount()) + } + + override fun toString(): String { + return snapshot().toString() + } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/utilities/EnterpriseNamedCacheFactory.kt b/node/src/main/kotlin/net/corda/node/utilities/EnterpriseNamedCacheFactory.kt new file mode 100644 index 0000000000..d0295bbe2a --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/utilities/EnterpriseNamedCacheFactory.kt @@ -0,0 +1,51 @@ +package net.corda.node.utilities + +import com.codahale.metrics.MetricRegistry +import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.CacheLoader +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.LoadingCache +import net.corda.core.internal.profiling.CacheTracing +import net.corda.core.internal.profiling.CacheTracing.Companion.wrap +import net.corda.core.serialization.SingletonSerializeAsToken +import net.corda.node.services.config.KB +import net.corda.node.services.config.MB +import net.corda.node.services.config.NodeConfiguration +import java.util.concurrent.TimeUnit + + +class EnterpriseNamedCacheFactory private constructor(private val tracingConfig: CacheTracing.CacheTracingConfig, private val metricRegistry: MetricRegistry?, private val nodeConfiguration: NodeConfiguration?) : NamedCacheFactory, SingletonSerializeAsToken() { + constructor(tracingConfig: CacheTracing.CacheTracingConfig) : this(tracingConfig, null, null) + + override fun bindWithMetrics(metricRegistry: MetricRegistry): NamedCacheFactory = EnterpriseNamedCacheFactory(tracingConfig, metricRegistry, this.nodeConfiguration) + override fun bindWithConfig(nodeConfiguration: NodeConfiguration): NamedCacheFactory = EnterpriseNamedCacheFactory(tracingConfig, this.metricRegistry, nodeConfiguration) + + // Scale most caches off the transaction cache size. + private fun defaultBound(nodeConfiguration: NodeConfiguration): Long = nodeConfiguration.transactionCacheSizeBytes / 8.KB + + // This result in the minimum being 10MB as per OS, but it then grows as per the transaction cache. + private fun defaultAttachmentCacheBound(nodeConfiguration: NodeConfiguration): Long = nodeConfiguration.transactionCacheSizeBytes + 2.MB + + // This results in a minium of 1024 entries as per OS, but then grows linearly with attachment cache size. + private fun defaultAttachmentPresenceCacheBound(nodeConfiguration: NodeConfiguration): Long = defaultAttachmentCacheBound(nodeConfiguration) / 10.KB + + override fun buildNamed(caffeine: Caffeine, name: String): Cache { + checkNotNull(metricRegistry) + checkNotNull(nodeConfiguration) + return wrap(caffeine.maximumSize(defaultBound(nodeConfiguration!!)).buildNamed(metricRegistry!!, name), tracingConfig, name) + } + + // TODO: allow a config file override for any named cache. + override fun buildNamed(caffeine: Caffeine, name: String, loader: CacheLoader): LoadingCache { + checkNotNull(metricRegistry) + checkNotNull(nodeConfiguration) + val configuredCaffeine = when (name) { + "DBTransactionStorage_transactions" -> caffeine.maximumWeight(nodeConfiguration!!.transactionCacheSizeBytes) + "NodeAttachmentService_attachmentContent" -> caffeine.maximumWeight(defaultAttachmentCacheBound(nodeConfiguration!!)) + "NodeAttachmentService_attachmentPresence" -> caffeine.maximumSize(defaultAttachmentPresenceCacheBound(nodeConfiguration!!)) + "P2PMessageDeduplicator_senderUUIDSeqNoHWM" -> caffeine.expireAfterAccess(7, TimeUnit.DAYS) + else -> caffeine.maximumSize(defaultBound(nodeConfiguration!!)) + } + return wrap(configuredCaffeine.buildNamed(metricRegistry!!, name, loader), tracingConfig, name) + } +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt index dc1867bf19..ae4fea1eac 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt @@ -37,6 +37,7 @@ import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.schema.PersistentStateService import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSchemaV1 +import net.corda.node.utilities.TestingNamedCacheFactory import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration @@ -121,7 +122,7 @@ class HibernateConfigurationTest { services = object : MockServices(cordappPackages, BOB_NAME, rigorousMock().also { doNothing().whenever(it).justVerifyAndRegisterIdentity(argThat { name == BOB_NAME }, any()) }, generateKeyPair(), dummyNotary.keyPair) { - override val vaultService = NodeVaultService(Clock.systemUTC(), keyManagementService, servicesForResolution, database, schemaService).apply { start() } + override val vaultService = NodeVaultService(Clock.systemUTC(), keyManagementService, servicesForResolution, database, schemaService, TestingNamedCacheFactory()).apply { start() } override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable) { for (stx in txs) { (validatedTransactions as WritableTransactionStorage).addTransaction(stx) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 3fb2881e27..9ead3920c8 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -249,7 +249,7 @@ open class MockServices private constructor( } internal fun makeVaultService(schemaService: SchemaService, database: CordaPersistence): VaultServiceInternal { - return NodeVaultService(clock, keyManagementService, servicesForResolution, database, schemaService).apply { start() } + return NodeVaultService(clock, keyManagementService, servicesForResolution, database, schemaService, EnterpriseMockNamedCachedFactory()).apply { start() } } // This needs to be internal as MutableClassToInstanceMap is a guava type and shouldn't be part of our public API diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/EnterpriseMockNamedCachedFactory.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/EnterpriseMockNamedCachedFactory.kt new file mode 100644 index 0000000000..c6d989af37 --- /dev/null +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/EnterpriseMockNamedCachedFactory.kt @@ -0,0 +1,36 @@ +package net.corda.testing.node.internal + +import com.codahale.metrics.MetricRegistry +import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.CacheLoader +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.LoadingCache +import net.corda.core.internal.buildNamed +import net.corda.core.serialization.SingletonSerializeAsToken +import net.corda.node.services.config.MB +import net.corda.node.services.config.NodeConfiguration +import net.corda.node.utilities.NamedCacheFactory +import java.util.concurrent.TimeUnit + +class EnterpriseMockNamedCachedFactory(private val sizeOverride: Long, private val metricRegistry: MetricRegistry?, private val nodeConfiguration: NodeConfiguration?) : NamedCacheFactory, SingletonSerializeAsToken() { + constructor(sizeOverride: Long = 1024) : this(sizeOverride, null, null) + + override fun bindWithMetrics(metricRegistry: MetricRegistry): NamedCacheFactory = EnterpriseMockNamedCachedFactory(sizeOverride, metricRegistry, this.nodeConfiguration) + override fun bindWithConfig(nodeConfiguration: NodeConfiguration): NamedCacheFactory = EnterpriseMockNamedCachedFactory(sizeOverride, this.metricRegistry, nodeConfiguration) + + override fun buildNamed(caffeine: Caffeine, name: String): Cache { + // Does not check metricRegistry or nodeConfiguration, because for tests we don't care. + return caffeine.maximumSize(sizeOverride).buildNamed(name) + } + + override fun buildNamed(caffeine: Caffeine, name: String, loader: CacheLoader): LoadingCache { + // Does not check metricRegistry or nodeConfiguration, because for tests we don't care. + val configuredCaffeine = when (name) { + "DBTransactionStorage_transactions" -> caffeine.maximumWeight(1.MB) + "NodeAttachmentService_attachmentContent" -> caffeine.maximumWeight(1.MB) + "P2PMessageDeduplicator_senderUUIDSeqNoHWM" -> caffeine.expireAfterAccess(1, TimeUnit.HOURS) + else -> caffeine.maximumSize(sizeOverride) + } + return configuredCaffeine.buildNamed(name, loader) + } +}