CORDA-1493: add --clear-network-map-cache command line option (#3272)

CORDA-1493: add --clear-network-map-cache command
This commit is contained in:
Katarzyna Streich 2018-05-30 18:39:27 +01:00 committed by GitHub
parent 5d1cc0bd54
commit 3758dbea34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 13 deletions

View File

@ -55,6 +55,7 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
private val justGenerateNodeInfoArg = optionParser.accepts("just-generate-node-info", private val justGenerateNodeInfoArg = optionParser.accepts("just-generate-node-info",
"Perform the node start-up task necessary to generate its nodeInfo, save it to disk, then quit") "Perform the node start-up task necessary to generate its nodeInfo, save it to disk, then quit")
private val bootstrapRaftClusterArg = optionParser.accepts("bootstrap-raft-cluster", "Bootstraps Raft cluster. The node forms a single node cluster (ignoring otherwise configured peer addresses), acting as a seed for other nodes to join the cluster.") private val bootstrapRaftClusterArg = optionParser.accepts("bootstrap-raft-cluster", "Bootstraps Raft cluster. The node forms a single node cluster (ignoring otherwise configured peer addresses), acting as a seed for other nodes to join the cluster.")
private val clearNetworkMapCache = optionParser.accepts("clear-network-map-cache", "Clears local copy of network map, on node startup it will be restored from server or file system.")
override fun doParse(optionSet: OptionSet): CmdLineOptions { override fun doParse(optionSet: OptionSet): CmdLineOptions {
require(!optionSet.has(baseDirectoryArg) || !optionSet.has(configFileArg)) { require(!optionSet.has(baseDirectoryArg) || !optionSet.has(configFileArg)) {
@ -74,6 +75,7 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
val networkRootTrustStorePassword = optionSet.valueOf(networkRootTrustStorePasswordArg) val networkRootTrustStorePassword = optionSet.valueOf(networkRootTrustStorePasswordArg)
val unknownConfigKeysPolicy = optionSet.valueOf(unknownConfigKeysPolicy) val unknownConfigKeysPolicy = optionSet.valueOf(unknownConfigKeysPolicy)
val devMode = optionSet.has(devModeArg) val devMode = optionSet.has(devModeArg)
val clearNetworkMapCache = optionSet.has(clearNetworkMapCache)
val registrationConfig = if (isRegistration) { val registrationConfig = if (isRegistration) {
requireNotNull(networkRootTrustStorePassword) { "Network root trust store password must be provided in registration mode using --network-root-truststore-password." } requireNotNull(networkRootTrustStorePassword) { "Network root trust store password must be provided in registration mode using --network-root-truststore-password." }
@ -94,7 +96,8 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
justGenerateNodeInfo, justGenerateNodeInfo,
bootstrapRaftCluster, bootstrapRaftCluster,
unknownConfigKeysPolicy, unknownConfigKeysPolicy,
devMode) devMode,
clearNetworkMapCache)
} }
} }
@ -111,7 +114,8 @@ data class CmdLineOptions(val baseDirectory: Path,
val justGenerateNodeInfo: Boolean, val justGenerateNodeInfo: Boolean,
val bootstrapRaftCluster: Boolean, val bootstrapRaftCluster: Boolean,
val unknownConfigKeysPolicy: UnknownConfigKeysPolicy, val unknownConfigKeysPolicy: UnknownConfigKeysPolicy,
val devMode: Boolean) { val devMode: Boolean,
val clearNetworkMapCache: Boolean) {
fun loadConfig(): Pair<Config, Try<NodeConfiguration>> { fun loadConfig(): Pair<Config, Try<NodeConfiguration>> {
val rawConfig = ConfigHelper.loadConfig( val rawConfig = ConfigHelper.loadConfig(
baseDirectory, baseDirectory,

View File

@ -274,6 +274,15 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
} }
} }
fun clearNetworkMapCache() {
Node.printBasicNodeInfo("Clearing network map cache entries")
log.info("Starting clearing of network map cache entries...")
configureDatabase(configuration.dataSourceProperties, configuration.database, { null }, { null }).use {
val networkMapCache = PersistentNetworkMapCache(it, emptyList())
networkMapCache.clearNetworkMapCache()
}
}
open fun start(): StartedNode<AbstractNode> { open fun start(): StartedNode<AbstractNode> {
check(started == null) { "Node has already been started" } check(started == null) { "Node has already been started" }
if (configuration.devMode) { if (configuration.devMode) {

View File

@ -58,7 +58,6 @@ open class NodeStartup(val args: Array<String>) {
return false return false
} }
val cmdlineOptions = NodeArgsParser().parseOrExit(*args) val cmdlineOptions = NodeArgsParser().parseOrExit(*args)
// We do the single node check before we initialise logging so that in case of a double-node start it // We do the single node check before we initialise logging so that in case of a double-node start it
// doesn't mess with the running node's logs. // doesn't mess with the running node's logs.
enforceSingleNodeIsRunning(cmdlineOptions.baseDirectory) enforceSingleNodeIsRunning(cmdlineOptions.baseDirectory)
@ -153,6 +152,10 @@ open class NodeStartup(val args: Array<String>) {
protected open fun startNode(conf: NodeConfiguration, versionInfo: VersionInfo, startTime: Long, cmdlineOptions: CmdLineOptions) { protected open fun startNode(conf: NodeConfiguration, versionInfo: VersionInfo, startTime: Long, cmdlineOptions: CmdLineOptions) {
val node = createNode(conf, versionInfo) val node = createNode(conf, versionInfo)
if (cmdlineOptions.clearNetworkMapCache) {
node.clearNetworkMapCache()
return
}
if (cmdlineOptions.justGenerateNodeInfo) { if (cmdlineOptions.justGenerateNodeInfo) {
// Perform the minimum required start-up logic to be able to write a nodeInfo to disk // Perform the minimum required start-up logic to be able to write a nodeInfo to disk
node.generateAndSaveNodeInfo() node.generateAndSaveNodeInfo()

View File

@ -19,6 +19,7 @@ import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.internal.schemas.NodeInfoSchemaV1 import net.corda.node.internal.schemas.NodeInfoSchemaV1
import net.corda.node.services.api.NetworkMapCacheBaseInternal import net.corda.node.services.api.NetworkMapCacheBaseInternal
@ -339,9 +340,11 @@ open class PersistentNetworkMapCache(
} }
override fun clearNetworkMapCache() { override fun clearNetworkMapCache() {
logger.info("Clearing Network Map Cache entries")
invalidateCaches() invalidateCaches()
database.transaction { database.transaction {
val result = getAllInfos(session) val result = getAllInfos(session)
logger.debug { "Number of node infos to be cleared: ${result.size}" }
for (nodeInfo in result) session.remove(nodeInfo) for (nodeInfo in result) session.remove(nodeInfo)
} }
} }

View File

@ -44,7 +44,8 @@ class NodeArgsParserTest {
justGenerateNodeInfo = false, justGenerateNodeInfo = false,
bootstrapRaftCluster = false, bootstrapRaftCluster = false,
unknownConfigKeysPolicy = UnknownConfigKeysPolicy.FAIL, unknownConfigKeysPolicy = UnknownConfigKeysPolicy.FAIL,
devMode = false)) devMode = false,
clearNetworkMapCache = false))
} }
@Test @Test
@ -158,6 +159,12 @@ class NodeArgsParserTest {
assertThat(cmdLineOptions.justGenerateNodeInfo).isTrue() assertThat(cmdLineOptions.justGenerateNodeInfo).isTrue()
} }
@Test
fun `clear network map cache`() {
val cmdLineOptions = parser.parse("--clear-network-map-cache")
assertThat(cmdLineOptions.clearNetworkMapCache).isTrue()
}
@Test @Test
fun `bootstrap raft cluster`() { fun `bootstrap raft cluster`() {
val cmdLineOptions = parser.parse("--bootstrap-raft-cluster") val cmdLineOptions = parser.parse("--bootstrap-raft-cluster")

View File

@ -7,15 +7,21 @@ import net.corda.core.internal.delete
import net.corda.core.internal.list import net.corda.core.internal.list
import net.corda.core.internal.readObject import net.corda.core.internal.readObject
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.node.internal.schemas.NodeInfoSchemaV1
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier.Companion.NODE_INFO_FILE_NAME_PREFIX import net.corda.nodeapi.internal.network.NodeInfoFilesCopier.Companion.NODE_INFO_FILE_NAME_PREFIX
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.internal.createNodeInfoAndSigned
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
@ -52,12 +58,58 @@ class NodeTest {
@Test @Test
fun `generateAndSaveNodeInfo works`() { fun `generateAndSaveNodeInfo works`() {
val nodeAddress = NetworkHostAndPort("0.1.2.3", 456) val configuration = createConfig()
val nodeName = CordaX500Name("Manx Blockchain Corp", "Douglas", "IM")
val platformVersion = 789 val platformVersion = 789
configureDatabase(configuration.dataSourceProperties, configuration.database, { null }, { null }).use { database ->
val node = Node(configuration, rigorousMock<VersionInfo>().also {
doReturn(platformVersion).whenever(it).platformVersion
}, initialiseSerialization = false)
assertEquals(node.generateNodeInfo(), node.generateNodeInfo()) // Node info doesn't change (including the serial)
}
}
@Test
fun `clear network map cache works`() {
val configuration = createConfig()
val (nodeInfo, _) = createNodeInfoAndSigned(ALICE_NAME)
configureDatabase(configuration.dataSourceProperties, configuration.database, { null }, { null }).use {
it.transaction {
val persistentNodeInfo = NodeInfoSchemaV1.PersistentNodeInfo(
id = 0,
hash = nodeInfo.serialize().hash.toString(),
addresses = nodeInfo.addresses.map { NodeInfoSchemaV1.DBHostAndPort.fromHostAndPort(it) },
legalIdentitiesAndCerts = nodeInfo.legalIdentitiesAndCerts.mapIndexed { idx, elem ->
NodeInfoSchemaV1.DBPartyAndCertificate(elem, isMain = idx == 0)
},
platformVersion = nodeInfo.platformVersion,
serial = nodeInfo.serial
)
// Save some NodeInfo
session.save(persistentNodeInfo)
}
val node = Node(configuration, rigorousMock<VersionInfo>().also {
doReturn(10).whenever(it).platformVersion
}, initialiseSerialization = false)
assertThat(getAllInfos(it)).isNotEmpty
node.clearNetworkMapCache()
assertThat(getAllInfos(it)).isEmpty()
}
}
private fun getAllInfos(database: CordaPersistence): List<NodeInfoSchemaV1.PersistentNodeInfo> {
return database.transaction {
val criteria = session.criteriaBuilder.createQuery(NodeInfoSchemaV1.PersistentNodeInfo::class.java)
criteria.select(criteria.from(NodeInfoSchemaV1.PersistentNodeInfo::class.java))
session.createQuery(criteria).resultList
}
}
private fun createConfig(): NodeConfiguration {
val dataSourceProperties = makeTestDataSourceProperties() val dataSourceProperties = makeTestDataSourceProperties()
val databaseConfig = DatabaseConfig() val databaseConfig = DatabaseConfig()
val configuration = rigorousMock<AbstractNodeConfiguration>().also { val nodeAddress = NetworkHostAndPort("0.1.2.3", 456)
val nodeName = CordaX500Name("Manx Blockchain Corp", "Douglas", "IM")
return rigorousMock<AbstractNodeConfiguration>().also {
doReturn(nodeAddress).whenever(it).p2pAddress doReturn(nodeAddress).whenever(it).p2pAddress
doReturn(nodeName).whenever(it).myLegalName doReturn(nodeName).whenever(it).myLegalName
doReturn(null).whenever(it).notary // Don't add notary identity. doReturn(null).whenever(it).notary // Don't add notary identity.
@ -68,11 +120,5 @@ class NodeTest {
doReturn("tsp").whenever(it).trustStorePassword doReturn("tsp").whenever(it).trustStorePassword
doReturn("ksp").whenever(it).keyStorePassword doReturn("ksp").whenever(it).keyStorePassword
} }
configureDatabase(dataSourceProperties, databaseConfig, { null }, { null }).use { database ->
val node = Node(configuration, rigorousMock<VersionInfo>().also {
doReturn(platformVersion).whenever(it).platformVersion
}, initialiseSerialization = false)
assertEquals(node.generateNodeInfo(), node.generateNodeInfo()) // Node info doesn't change (including the serial)
}
} }
} }