org.neo4j.dbms.database.SystemGraphComponent Maven / Gradle / Ivy
Show all versions of neo4j-kernel Show documentation
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.dbms.database;
import org.neo4j.function.ThrowingConsumer;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.kernel.api.security.SecurityContext;
import org.neo4j.kernel.api.KernelTransaction;
import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.kernel.impl.coreapi.TransactionImpl;
/**
* The system database is used to store information of interest to many parts of the DBMS. Each sub-graph can be thought of as being of primary interest to (or
* managed by) a particular component of the DBMS. For example, the security sub-graph composed of User, Role, Privilege and various other related nodes and
* relationships is primarily initialized, upgraded and read by the SecurityModule.
*
* This interface defines an external API to each component whereby external commands can ask about the current status of the component's sub-graph within the
* system database, and also trigger upgrades (or migrations) of the sub-graph from an older version to the current version.
*
* It is up to each component to define the rules for which older versions would be concurrently supported within any running DBMS. However, being supported
* also means it is possible to upgrade to the current version. This means that if the detect
method return REQUIRES_UPGRADE
, then it
* should be possible to call the upgradeToCurrent
method and achieve the upgrade.
*/
public interface SystemGraphComponent
{
Label VERSION_LABEL = Label.label( "Version" );
/**
* This string should be a unique identifier for the component. It will be used in two places:
*
* - A key in a map of components for managing upgrades of the entire DBMS
* - A property key for the
Version
node used to store versioning information for this component
*
*/
String componentName();
/**
* Return the current status of this component. This involves reading the current contents of the system database and detecting whether the sub-graph
* managed by this component is missing, old or up-to-date. Older sub-graphs could be supported for upgrade, or unsupported (in which case the server cannot
* be started).
*/
Status detect( Transaction tx );
default Status detect( GraphDatabaseService system )
{
try ( Transaction tx = system.beginTx() )
{
SystemGraphComponent.Status status = detect( tx );
tx.commit();
return status;
}
}
/**
* If the component-specific sub-graph of the system database is not initialized yet (empty), this method should populate it with the default contents for
* the current version of the component.
*
* @throws Exception on any possible error raised by the initialization process
*/
void initializeSystemGraph( GraphDatabaseService system, boolean firstInitialization ) throws Exception;
/**
* If the component-specific sub-graph of the system database is an older, but still supported, version, this method should upgrade it to the latest
* supported version.
*
* @throws Exception on any possible error raised by the upgrade process
*/
void upgradeToCurrent( GraphDatabaseService system ) throws Exception;
/**
* Delete unused parts of the system graph.
*/
default void cleanup( GraphDatabaseService system ) throws Exception
{
}
static void executeWithFullAccess( GraphDatabaseService system, ThrowingConsumer consumer ) throws Exception
{
try ( TransactionImpl tx = (TransactionImpl) system.beginTx();
KernelTransaction.Revertable ignore = tx.kernelTransaction().overrideWith( SecurityContext.AUTH_DISABLED ) )
{
consumer.accept( tx );
tx.commit();
}
}
default Integer getVersion( Transaction tx, String componentVersionProperty )
{
return getVersionNumber( tx, componentVersionProperty );
}
/**
* Get the version number of a component from the system graph.
*
* @param tx an open transaction
* @param componentVersionProperty name of the property describing the version for the component
* @return The version of the component or null if there is no stored information.
*/
static Integer getVersionNumber( Transaction tx, String componentVersionProperty )
{
Integer result = null;
try ( ResourceIterator nodes = tx.findNodes( VERSION_LABEL ) )
{
if ( nodes.hasNext() )
{
Node versionNode = nodes.next();
result = (Integer) versionNode.getProperty( componentVersionProperty, null );
}
}
return result;
}
enum Status
{
UNINITIALIZED( "No sub-graph detected for this component", "requires initialization" ),
CURRENT( "The sub-graph is already at the current version", "nothing to do" ),
REQUIRES_UPGRADE( "The sub-graph is supported, but is an older version and requires upgrade", "CALL dbms.upgrade()" ),
UNSUPPORTED_BUT_CAN_UPGRADE( "The sub-graph is unsupported, this component cannot function",
"Restart Neo4j in single-instance mode to upgrade this component at startup" ),
UNSUPPORTED( "The sub-graph is unsupported because it is too old, this component cannot function",
"Downgrade Neo4j and then upgrade this component before upgrading Neo4j again" ),
UNSUPPORTED_FUTURE( "The sub-graph is unsupported because it is a newer version, this component cannot function", "Upgrade Neo4j" );
private final String description;
private final String resolution;
Status( String description, String resolution )
{
this.description = description;
this.resolution = resolution;
}
/**
* Combine status's to present an overall status for the entire DBMS. Primarily we care about two cases:
*
* - If any component is unsupported, the entire DBMS is unsupported
* - If any component requires an upgrade, the entire DBMS requires an upgrade
*
* Initialization is handled through a different code path and so does not require handling here.
*/
public Status with( Status other )
{
Status[] precedence = new Status[]{UNSUPPORTED_FUTURE, UNSUPPORTED, UNSUPPORTED_BUT_CAN_UPGRADE, REQUIRES_UPGRADE, UNINITIALIZED, CURRENT};
for ( Status status : precedence )
{
if ( other == status || this == status )
{
return status;
}
}
return this;
}
public String description()
{
return description;
}
public String resolution()
{
return resolution;
}
}
}