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",
"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 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 {
require(!optionSet.has(baseDirectoryArg) || !optionSet.has(configFileArg)) {
@ -74,6 +75,7 @@ class NodeArgsParser : AbstractArgsParser<CmdLineOptions>() {
val networkRootTrustStorePassword = optionSet.valueOf(networkRootTrustStorePasswordArg)
val unknownConfigKeysPolicy = optionSet.valueOf(unknownConfigKeysPolicy)
val devMode = optionSet.has(devModeArg)
val clearNetworkMapCache = optionSet.has(clearNetworkMapCache)
val registrationConfig = if (isRegistration) {
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,
bootstrapRaftCluster,
unknownConfigKeysPolicy,
devMode)
devMode,
clearNetworkMapCache)
}
}
@ -111,7 +114,8 @@ data class CmdLineOptions(val baseDirectory: Path,
val justGenerateNodeInfo: Boolean,
val bootstrapRaftCluster: Boolean,
val unknownConfigKeysPolicy: UnknownConfigKeysPolicy,
val devMode: Boolean) {
val devMode: Boolean,
val clearNetworkMapCache: Boolean) {
fun loadConfig(): Pair<Config, Try<NodeConfiguration>> {
val rawConfig = ConfigHelper.loadConfig(
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> {
check(started == null) { "Node has already been started" }
if (configuration.devMode) {

View File

@ -58,7 +58,6 @@ open class NodeStartup(val args: Array<String>) {
return false
}
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
// doesn't mess with the running node's logs.
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) {
val node = createNode(conf, versionInfo)
if (cmdlineOptions.clearNetworkMapCache) {
node.clearNetworkMapCache()
return
}
if (cmdlineOptions.justGenerateNodeInfo) {
// Perform the minimum required start-up logic to be able to write a nodeInfo to disk
node.generateAndSaveNodeInfo()

View File

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

View File

@ -44,7 +44,8 @@ class NodeArgsParserTest {
justGenerateNodeInfo = false,
bootstrapRaftCluster = false,
unknownConfigKeysPolicy = UnknownConfigKeysPolicy.FAIL,
devMode = false))
devMode = false,
clearNetworkMapCache = false))
}
@Test
@ -158,6 +159,12 @@ class NodeArgsParserTest {
assertThat(cmdLineOptions.justGenerateNodeInfo).isTrue()
}
@Test
fun `clear network map cache`() {
val cmdLineOptions = parser.parse("--clear-network-map-cache")
assertThat(cmdLineOptions.clearNetworkMapCache).isTrue()
}
@Test
fun `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.readObject
import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.VersionInfo
import net.corda.node.internal.schemas.NodeInfoSchemaV1
import net.corda.node.services.config.NodeConfiguration
import net.corda.nodeapi.internal.SignedNodeInfo
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.testing.core.ALICE_NAME
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.internal.createNodeInfoAndSigned
import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@ -52,12 +58,58 @@ class NodeTest {
@Test
fun `generateAndSaveNodeInfo works`() {
val nodeAddress = NetworkHostAndPort("0.1.2.3", 456)
val nodeName = CordaX500Name("Manx Blockchain Corp", "Douglas", "IM")
val configuration = createConfig()
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 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(nodeName).whenever(it).myLegalName
doReturn(null).whenever(it).notary // Don't add notary identity.
@ -68,11 +120,5 @@ class NodeTest {
doReturn("tsp").whenever(it).trustStorePassword
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)
}
}
}