Skip to content

Commit

Permalink
Introduce SystemInformation service, hook into UDC.
Browse files Browse the repository at this point in the history
UDC has a complex dependency hierarchy, and because of that it uses reflection
to extract information from system components. This introduces a "SystemInformation"
service where components publish metadata and UDC can access it without reflection.

Also minor improvements to UDC resilience - only store up to 10 latest client
names and urlencode query parameters in UDC ping payload.
  • Loading branch information
jakewins committed Jun 2, 2015
1 parent abb5a69 commit 2564575
Show file tree
Hide file tree
Showing 29 changed files with 895 additions and 687 deletions.
Expand Up @@ -65,6 +65,8 @@
import org.neo4j.kernel.lifecycle.Lifecycle;
import org.neo4j.kernel.lifecycle.LifecycleListener;
import org.neo4j.kernel.lifecycle.LifecycleStatus;
import org.neo4j.udc.UsageDataKeys;
import org.neo4j.udc.UsageData;

import java.io.File;

Expand All @@ -91,7 +93,7 @@ public CommunityEditionModule( PlatformModule platformModule )
idGeneratorFactory = deps.satisfyDependency( createIdGeneratorFactory() );

propertyKeyTokenHolder = life.add( deps.satisfyDependency( new PropertyKeyTokenHolder(
createPropertyKeyCreator( config, dataSourceManager, idGeneratorFactory ) ) ));
createPropertyKeyCreator( config, dataSourceManager, idGeneratorFactory ) ) ) );
labelTokenHolder = life.add( deps.satisfyDependency(new LabelTokenHolder( createLabelIdCreator( config,
dataSourceManager, idGeneratorFactory ) ) ));
relationshipTypeTokenHolder = life.add( deps.satisfyDependency(new RelationshipTypeTokenHolder(
Expand All @@ -110,6 +112,39 @@ public CommunityEditionModule( PlatformModule platformModule )
upgradeConfiguration = new ConfigMapUpgradeConfiguration( config );

registerRecovery( config.get( GraphDatabaseFacadeFactory.Configuration.editionName), life, deps );

publishEditionInfo( deps.resolveDependency( UsageData.class ) );
}

private void publishEditionInfo( UsageData sysInfo )
{
sysInfo.set( UsageDataKeys.edition, determineEdition() );
sysInfo.set( UsageDataKeys.operationalMode, UsageDataKeys.OperationalMode.single );
}

private UsageDataKeys.Edition determineEdition()
{
// Currently, a user can be running enterprise or advanced edition and end up using this module to bootstrap
// So, until we've organized this differently, we use introspection to tell which edition is running
try
{
getClass().getClassLoader().loadClass( "org.neo4j.kernel.ha.HighlyAvailableGraphDatabase" );
return UsageDataKeys.Edition.enterprise;
}
catch ( ClassNotFoundException e )
{
// Not Enterprise
}
try
{
getClass().getClassLoader().loadClass( "org.neo4j.management.Neo4jManager" );
return UsageDataKeys.Edition.advanced;
}
catch ( ClassNotFoundException e )
{
// Not Advanced
}
return UsageDataKeys.Edition.community;
}

public static CommitProcessFactory createCommitProcessFactory()
Expand Down
Expand Up @@ -36,6 +36,7 @@
import org.neo4j.kernel.DefaultFileSystemAbstraction;
import org.neo4j.kernel.StoreLocker;
import org.neo4j.kernel.StoreLockerLifecycleAdapter;
import org.neo4j.kernel.Version;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.extension.KernelExtensionFactory;
import org.neo4j.kernel.extension.KernelExtensions;
Expand All @@ -58,6 +59,8 @@
import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider;
import org.neo4j.logging.NullLogProvider;
import org.neo4j.udc.UsageDataKeys;
import org.neo4j.udc.UsageData;

/**
* Platform module for {@link org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory}. This creates
Expand Down Expand Up @@ -117,6 +120,9 @@ public DependencyResolver get()

this.storeDir = storeDir.getAbsoluteFile();

// Database system information, used by UDC
dependencies.satisfyDependency( new UsageData() );

fileSystem = life.add( dependencies.satisfyDependency( createFileSystemAbstraction() ) );

// Component monitoring
Expand Down Expand Up @@ -175,6 +181,15 @@ public File storeDir()
externalDependencies.kernelExtensions(),
dependencies,
UnsatisfiedDependencyStrategies.fail() ) );

publishPlatformInfo( dependencies.resolveDependency( UsageData.class ) );
}

private void publishPlatformInfo( UsageData sysInfo )
{
sysInfo.set( UsageDataKeys.version, Version.getKernel().getReleaseVersion() );
sysInfo.set( UsageDataKeys.revision, Version.getKernel().getRevision() );
sysInfo.set( UsageDataKeys.operationalMode, UsageDataKeys.OperationalMode.ha );
}

public LifeSupport createLife()
Expand Down
61 changes: 61 additions & 0 deletions community/kernel/src/main/java/org/neo4j/udc/UsageData.java
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2002-2015 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.udc;

import java.util.concurrent.ConcurrentHashMap;

/**
* An in-memory storage location for usage metadata.
* Any component is allowed to publish it's usage date here, and it can be any object,
* including mutable classes. It is up to the usage data publishing code to choose which items from this repository
* to publish.
*
* This service is meant as a diagnostic and informational tool, notably used by UDC.
*/
public class UsageData
{
private final ConcurrentHashMap<UsageDataKey, Object> store = new ConcurrentHashMap<>();

public <T> void set( UsageDataKey<T> key, T value )
{
store.put( key, value );
}

public <T> T get( UsageDataKey<T> key )
{
Object o = store.get( key );
if( o == null )
{
// When items are missing, if there is a default value, we do a get-or-create style operation
// This allows outside actors to get-or-create rich objects and know they will get the same object out
// that other threads would use, which is helpful when we store mutable objects
T value = key.generateDefaultValue();
if(value == null)
{
return null;
}

store.putIfAbsent( key, value );
return get( key );
}
return (T) o;
}

}
87 changes: 87 additions & 0 deletions community/kernel/src/main/java/org/neo4j/udc/UsageDataKey.java
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2002-2015 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.udc;

import org.neo4j.function.Supplier;

import static org.neo4j.function.Suppliers.singleton;

/**
* A lookup key to publish or retrieve data in {@link UsageData}.
* @param <Type>
*/
public class UsageDataKey<Type>
{
private final String name;

/** When key is requested and no value exists, a default value is generated and inserted using this */
private final Supplier<Type> defaultVal;

public static <T> UsageDataKey<T> key(String name)
{
return key(name, null);
}

public static <T> UsageDataKey<T> key(String name, T defaultVal)
{
return new UsageDataKey<>( name, singleton(defaultVal) );
}

public static <T> UsageDataKey<T> key(String name, Supplier<T> defaultVal)
{
return new UsageDataKey<>( name, defaultVal );
}

public UsageDataKey( String name, Supplier<Type> defaultValue )
{
this.name = name;
this.defaultVal = defaultValue;
}

String name()
{
return name;
}

Type generateDefaultValue()
{
return defaultVal == null ? null : defaultVal.get();
}

@Override
public boolean equals( Object o )
{
if ( this == o )
{ return true; }
if ( o == null || getClass() != o.getClass() )
{ return false; }

UsageDataKey<?> key = (UsageDataKey<?>) o;

return name.equals( key.name );

}

@Override
public int hashCode()
{
return name.hashCode();
}
}
75 changes: 75 additions & 0 deletions community/kernel/src/main/java/org/neo4j/udc/UsageDataKeys.java
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2002-2015 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.udc;

import org.neo4j.concurrent.RecentK;
import org.neo4j.function.Supplier;

import static org.neo4j.udc.UsageDataKey.key;

/**
* Inventory of common keys. This list is not exhaustive, and all items listed may not be available.
* Still, this serves as a useful starting point for what you can expect to find, and new items added are
* encouraged to have their keys listed here.
*/
public class UsageDataKeys
{
/** Edition of Neo4j running, eg 'community' or 'enterprise' */
public static final UsageDataKey<Edition> edition = key( "neo4j.edition", Edition.unknown );

/** Version of Neo4j running, eg. 1.2.3-RC1 */
public static final UsageDataKey<String> version = key( "neo4j.version", "N/A" );

/** Revision of Neo4j running, a link back to source control revision ids. */
public static final UsageDataKey<String> revision = key( "neo4j.revision", "N/A" );

/** Operational mode of the database */
public static final UsageDataKey<OperationalMode> operationalMode = key( "neo4j.opMode", OperationalMode.unknown );

/** Self-reported names of clients connecting to us. */
public static final UsageDataKey<RecentK<String>> clientNames = key( "neo4j.clientNames", new Supplier<RecentK<String>>()
{
@Override
public RecentK<String> get()
{
return new RecentK<>( 10 );
}
} );

/** Cluster server ID */
public static final UsageDataKey<String> serverId = key( "neo4j.serverId" );

public enum OperationalMode
{
// Note, these are sent verbatum via UDC if UDC is enabled
unknown,
single,
ha
}

public enum Edition
{
// Note, these are sent verbatum via UDC if UDC is enabled
unknown,
community,
advanced,
enterprise
}
}
Expand Up @@ -19,18 +19,19 @@
*/
package org.neo4j.metatest;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Map;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Map;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.kernel.GraphDatabaseAPI;
import org.neo4j.kernel.impl.annotations.Documented;
import org.neo4j.test.GraphDescription;
import org.neo4j.test.GraphDescription.Graph;
Expand Down
Expand Up @@ -17,12 +17,30 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.ext.udc.impl;
package org.neo4j.sysinfo;

/**
* @author mh
* @since 11.06.12
*/
public enum Edition {
community, advanced, enterprise;
}
import org.junit.Test;

import org.neo4j.udc.UsageData;
import org.neo4j.udc.UsageDataKey;

import static org.junit.Assert.assertEquals;
import static org.neo4j.udc.UsageDataKey.key;

public class UsageDataTest
{
@Test
public void shouldPutAndRetrieve() throws Throwable
{
// Given
UsageData ms = new UsageData();
UsageDataKey<String> key = key( "hello" );

// When
ms.set( key, "Hello!" );

// Then
assertEquals( "Hello!", ms.get( key ) );
assertEquals( null, ms.get( key( "other" ) ) );
}
}

0 comments on commit 2564575

Please sign in to comment.