Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacement for deprecated `WrappingNeoServerBootstrapper`

Tags:

neo4j

I am trying to convert some Neo4J Java code code to 2.2+, the code runs an embedded Neo4J server with web frontend.

// configure embedded DB,
// but this doesn't start a server at port 12345 ?
final GraphDatabaseService db = new GraphDatabaseFactory()
    .newEmbeddedDatabaseBuilder("/path/to/db")
    .setConfig(ServerSettings.webserver_address, "localhost")
    .setConfig(ServerSettings.webserver_port, 12345)
    .newGraphDatabase();

// add shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread(){
    @Override public void run() {
        graphDb.shutdown();
    }
});

The deprecated code that starts a server reads:

final GraphDatabaseAPI api = (GraphDatabaseAPI)db;

final ServerConfigurator c = new ServerConfigurator(api);
c.configuration().addProperty(
    Configurator.WEBSERVER_ADDRESS_PROPERTY_KEY, "localhost");
c.configuration().addProperty(
    Configurator.WEBSERVER_PORT_PROPERTY_KEY, 12345);

new WrappingNeoServerBootstrapper(api, c).start();

Leaving this out doesn't start the server. It is unclear to me how to get an embedded server running without using the deprecated methods. Any thoughts?

UPDATE:

So indeed the correct answer seems: change your architecture.

One shouldn't use the embedded server anymore, instead use the server and RESTful API. For the bits that you need the Java API you can write an unmanaged extension (see docs).

Here is a simple example of such an extension https://github.com/histograph/neo4j-plugin

Thanks!

like image 675
wires Avatar asked May 06 '15 10:05

wires


4 Answers

You can always do that the other way round. Instead of passing instance of GraphDatabaseService you created to the server try start server first and retrieve instance created by it :) Checked with 3.0.6

    String homeDir = "./";
    String configFile = "./conf/neo4j.conf";
    ServerBootstrapper serverBootstrapper = new CommunityBootstrapper();
    int i = serverBootstrapper.start(new File(homeDir), Optional.of(new File(configFile)), Pair.of("dbms.connector.http.address","0.0.0.0:7575"));
    NeoServer neoServer = serverBootstrapper.getServer();
    GraphDatabaseService graph = neoServer.getDatabase().getGraph();
    System.out.println(graph);
like image 197
czajah Avatar answered Nov 03 '22 20:11

czajah


UPDATE January 2016

In the comments, you'll notice that certain things about neo4j are changing. The rest of this answer I think is still valid, but if your'e a time traveler coming to this post in mid-2016, look into whether or not the new neo4j binary protocol (AFAIK, called "bolt") is available.

/ENDUPDATE

The bad news is that the wrapping neo server boostrapper is deprecated, and I don't think there is a way of doing this without using deprecated methods. So the simple answer to your question is "no".

Maybe a year ago, I was looking for the same thing as you, but let me share something I've learned in the meantime that might change your question.

So when you use an embedded server, you get certain advantages like the ability to use the java API, but you get some big disadvantages. Putting the DB in the same memory space as your application means you have to juggle memory between the two. And it means you can't upgrade your application without stopping the database.

When you use a server, many things are better like the ability to run cypher without going through your app layer, the use of RESTful services, and so on.

The trouble with the WrappingNeoServerBootstrapper (and why it's probably good that it's deprecated) is that it has the disadvantages of both approaches. Sure, you get REST services and cypher, but you're still in the same memory space as the application.

Architecturally, what we've found with neo4j is that you're just usually going to want to use an external separate server, and then communicate with it via REST services. I think neo4j is working on a binary protocol (ala JDBC but of course not the same thing) so that perhaps more of the java API might be opened up and performance would improve. In the meantime, all of the good libraries for neo4j including spring-data and others can already talk to an HTTP endpoint, so whatever features they provide can generally be done with a separate server. For maintainability of the application, and tweakability of neo4j itself, you're almost always going to be better off running a separate server.

Note that this recommendation mimics what you see with just about every other kind of database (mongo, oracle, whatever). Some offer embedded options for small/specialized applications, but just about all of them assume you'll run a separate server process and have your application talk to it. So this isn't about graphs at all, just about good application design, evolvability, and maintainability.

So for these reasons, it's OK that WrappingNeoServerBootstrapper is deprecated (and probably isn't coming back). My experience suggests this isn't something you should really do anyway.

like image 20
FrobberOfBits Avatar answered Nov 03 '22 18:11

FrobberOfBits


With Neo4J 3.1.1 and czajah's approach, "embedded" mode really can be started just fine (no deprecated calls), together with the advantage of manipulating the graph data from the same JVM instance.

Sample code below requires dependency artifacts neo4j and neo4j-server from org.neo4j group. Server config file is omitted, while working storage folder and suitable config options are specified so that neo4j console and neo4j-shell (which is becoming deprecated in favour of cypher-shell) can be used against the "embedded" instance.

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.helpers.collection.Pair;
import org.neo4j.server.CommunityBootstrapper;
import org.neo4j.server.NeoServer;
import org.neo4j.server.ServerBootstrapper;

import java.io.File;
import java.io.IOException;
import java.util.Optional;

public class RunNeoRun {
    public static void main(String[] args) throws IOException {
        // Wherever the Neo4J storage location is.
        File storeDir = new File("/tmp/tmp4j");

        ServerBootstrapper serverBootstrapper = new CommunityBootstrapper();
        serverBootstrapper.start(
            storeDir,
            Optional.empty(), // omit configfile, properties follow
            Pair.of("dbms.connector.http.address","127.0.0.1:7474"),
            Pair.of("dbms.connector.http.enabled", "true"),
            Pair.of("dbms.connector.bolt.enabled", "true"),

            // allow the shell connections via port 1337 (default)
            Pair.of("dbms.shell.enabled", "true"),
            Pair.of("dbms.shell.host", "127.0.0.1"),
            Pair.of("dbms.shell.port", "1337")
        );
        // ^^ serverBootstrapper.start() also registered shutdown hook!

        NeoServer neoServer = serverBootstrapper.getServer();
        GraphDatabaseService gdb = neoServer.getDatabase().getGraph();

        /* Some transactional code */
        try(Transaction tx = gdb.beginTx()) {
            gdb.getAllNodes().forEach(
                n -> System.out.println(n)
            );
            tx.success();
        }

        System.out.println("Press ENTER to quit.");
        System.in.read();

        System.exit(0);
    }
}
like image 28
unserializable Avatar answered Nov 03 '22 19:11

unserializable


Czajah, Hi, your workaround for 3.0.6 also works in a production embedded-mode (older paxos HA) 3.1.0 - thank you.

Source

github project (Maven, Tomcat 8 WAR) at https://github.com/obrienlabs/nbi-neo4j-embedded-aws-war

Endpoints

Add a node to the embedded graph db http://neo4j.ca-central-1.elasticbeanstalk.com/FrontController?action=graph

The browser is on port 7575

With a few modifications I was able to wrap a HighlyAvailableGraphDatabase with a EnterpriseBootstrapper.

There are some non-fatal exceptions around JMX reporting in debug.log likely related to my 8.0.28 tomcat version but the graph db running embedded in Tomcat is OK.

    2016-12-21 16:20:00.574+0000 INFO  Bolt enabled on 0.0.0.0:7688.
    2016-12-21 16:20:09.554+0000 INFO  Attempting to join cluster of [127.0.0.1:5001]
    2016-12-21 16:20:11.566+0000 INFO  Creating new cluster with name [neo4j.ha]...
    2016-12-21 16:20:11.607+0000 INFO  Instance 1 (this server)  entered the cluster
    2016-12-21 16:20:12.164+0000 INFO  I am 1, moving to master
    2016-12-21 16:20:12.293+0000 INFO  Instance 1 (this server)  was elected as coordinator
    2016-12-21 16:20:12.462+0000 INFO  I am 1, successfully moved to master
    2016-12-21 16:20:12.513+0000 INFO  Instance 1 (this server)  is available as master at ha://127.0.0.1:6001?serverId=1 with StoreId{creationTime=1482199697648, randomId=7800059877674392627, storeVersion=15531981201765894, upgradeTime=1482199697648, upgradeId=1}
    2016-12-21 16:20:14.495+0000 INFO  Database available for write transactions
    2016-12-21 16:20:31.917+0000 INFO  Mounted REST API at: /db/manage
    2016-12-21 16:20:53.264+0000 INFO  Remote interface available at http://localhost:7575/
    register: org.obrienlabs.nbi.graph.service.HaMonitor@1c0f80c9


public class ExtendedHighlyAvailableGraphDatabaseFactory extends HighlyAvailableGraphDatabaseFactory {
    private Log log = LogFactory.getLog(ExtendedHighlyAvailableGraphDatabaseFactory.class);
    private HaMonitor haMonitor;

            @Override
            public GraphDatabaseService newDatabase( final Map<String, String> config ) {
                EnterpriseBootstrapper serverBootstrapper = new EnterpriseBootstrapper();
                List<Pair> pairs = new ArrayList<>();
                for(Entry<String, String> entry : config.entrySet()) {
                    pairs.add(Pair.of(entry.getKey(), entry.getValue()));
                }   
                Pair pairArray[] = new Pair[pairs.size()];
                // will resolve to /dir/data/databases/graph.db 
                serverBootstrapper.start(storeDir, Optional.empty(), pairs.toArray(pairArray));
                GraphDatabaseService graph = serverBootstrapper.getServer().getDatabase().getGraph(); 
                // set the paxos HA listener only when dbms.mode=HA
                if(graph instanceof HighlyAvailableGraphDatabase) {
                    haMonitor.setDb((HighlyAvailableGraphDatabase) graph);
                    HighAvailabilityMemberStateMachine memberStateMachine = 
                            ((HighlyAvailableGraphDatabase)graph).getDependencyResolver()
                                .resolveDependency(HighAvailabilityMemberStateMachine.class);
                    if ( memberStateMachine != null ) {
                        memberStateMachine.addHighAvailabilityMemberListener(haMonitor);
                        log.info("register: " +  haMonitor);
                    }
                }
                return graph;
                } };
    }

Spring config

   <util:map id="config">
    <entry key="ha.server_id" value="1"/>
    <entry key="ha.initial_hosts" value="127.0.0.1:5001"/>
    <entry key="dbms.mode" value="HA"/>
    <entry key="browser.allow_outgoing_connection" value="true" />
    <entry key="unsupported.dbms.ephemerall" value="false" />   
    <entry key="dbms.connector.http.address" value="0.0.0.0:7575" />
    <entry key="dbms.connector.bolt.address" value="0.0.0.0:7688" />
    <entry key="dbms.connector.http.enabled" value="true" />
    <entry key="dbms.connector.bolt.enabled" value="true" />
    <entry key="dbms.connector.http.type" value="HTTP" />
    <entry key="dbms.connector.bolt.type" value="BOLT" />
    <entry key="dbms.connector.http.tls_level" value="DISABLED" />
    <entry key="dbms.connector.bolt.tls_level" value="DISABLED" />
    <entry key="dbms.security.auth_enabled" value="true"/>
    <entry key="dbms.logs.debug.level" value="DEBUG"/>
    <entry key="dbms.logs.http.enabled" value="true" />
    <entry key="dbms.logs.query.enabled" value="true"/> 
    <entry key="dbms.shell.enabled" value="true"/> 
    </util:map>

    <bean id="haMonitor" class="org.obrienlabs.nbi.graph.service.HaMonitor"/>  
    <bean id="graphDbFactory"  class="org.obrienlabs.nbi.graph.service.ExtendedHighlyAvailableGraphDatabaseFactory">
        <constructor-arg ref="haMonitor" />
    </bean>
    <bean id="graphDbBuilder" 
        factory-bean="graphDbFactory" 
        factory-method="newEmbeddedDatabaseBuilder">
        <constructor-arg value="/ec2-user"/>
    </bean>
    <bean id="graphDbBuilderFinal" 
        factory-bean="graphDbBuilder" 
        factory-method="setConfig">
        <constructor-arg ref="config"/>
    </bean>
    <!-- HighlyAvailableGraphDatabase wrapped by an EnterpriseBootstrapper NeoServer created in this constructor -->
    <bean id="graphDatabaseService" 
        factory-bean="graphDbBuilderFinal" 
        factory-method="newGraphDatabase" 
        destroy-method="shutdown" />

Transactions go through

2016-12-21 20:51:07.478+0000 INFO  [o.n.k.i.s.c.CountsTracker] About to rotate counts store at transaction 9 to [/ec2-user/data/databases/graph.db/neostore.counts.db.b], from [/ec2-user/data/databases/graph.db/neostore.counts.db.a].
2016-12-21 20:51:07.480+0000 INFO  [o.n.k.i.s.c.CountsTracker] Successfully rotated counts store at transaction 9 to [/ec2-user/data/databases/graph.db/neostore.counts.db.b], from [/ec2-user/data/databases/graph.db/neostore.counts.db.a].
2016-12-21 20:51:07.483+0000 INFO  [o.n.k.i.t.l.c.CheckPointerImpl] Check Pointing triggered by scheduler for time threshold [9]:  Store flush completed

Settings from the mbean in jconsole are

2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] Kernel version: 3.1.0,16a782b42d76ca37db72958eb2565cf6aa671a29
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] Neo4j Kernel properties:
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.logs.query.enabled=true
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.security.auth_enabled=true
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.connector.bolt.tls_level=DISABLED
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.shell.enabled=true
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] ha.server_id=1
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.logs.http.enabled=true
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] ha.initial_hosts=127.0.0.1:5001
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.allow_format_migration=false
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.connector.http.address=0.0.0.0:7575
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.connector.bolt.enabled=true
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.connector.http.tls_level=DISABLED
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.connector.http.enabled=true
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.mode=HA
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] unsupported.dbms.block_size.labels=56
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.connector.bolt.type=BOLT
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] unsupported.dbms.directories.neo4j_home=/ec2-user
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] unsupported.dbms.ephemerall=false
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] browser.allow_outgoing_connection=true
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.logs.debug.level=DEBUG
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.connector.http.type=HTTP
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] unsupported.dbms.block_size.strings=120
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] unsupported.dbms.block_size.array_properties=120
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] unsupported.dbms.edition=enterprise
2016-12-23 13:28:12.708+0000 INFO  [o.n.k.i.DiagnosticsManager] dbms.connector.bolt.address=0.0.0.0:7688

7575(jetty server) and 7688 (bolt) ports are open

obrienlabs-mbp15:_deployment michaelobrien$ netstat -vatn | grep 7575
tcp46      0      0  *.7575                 *.*                    LISTEN      131072 131072  49013      0
tcp4       0      0  127.0.0.1.7575         127.0.0.1.60685        TIME_WAIT   407296 146988  49013      0
tcp6       0      0  ::1.7575               ::1.60699              TIME_WAIT   407284 146808  49013      0
tcp6       0      0  ::1.7575               ::1.60700              TIME_WAIT   407284 146808  49013      0
obrienlabs-mbp15:_deployment michaelobrien$ netstat -vatn | grep 7688
tcp6       0      0  ::1.7688               ::1.60704              ESTABLISHED 406582 146808  49013      0
tcp6       0      0  ::1.60704              ::1.7688               ESTABLISHED 398196 146808  48165      0
tcp6       0      0  ::1.7688               ::1.60702              ESTABLISHED 406570 146808  49013      0
tcp6       0      0  ::1.60702              ::1.7688               ESTABLISHED 398185 146808  48165      0
tcp6       0      0  ::1.7688               ::1.60701              ESTABLISHED 407255 146808  49013      0
tcp6       0      0  ::1.60701              ::1.7688               ESTABLISHED 407628 146808  48165      0
tcp46      0      0  *.7688                 *.*                    LISTEN      131072 131072  49013      0
obrienlabs-mbp15:_deployment michaelobrien$ netstat -vatn | grep 8080
tcp4       0      0  127.0.0.1.8080         127.0.0.1.60584        FIN_WAIT_2  408104 146988  49013      0
tcp4     994      0  127.0.0.1.60584        127.0.0.1.8080         CLOSE_WAIT  408128 146988  42992      0
tcp46      0      0  *.8080                 *.*                    LISTEN      131072 131072  49013      0
udp4       0      0  *.*                    *.*                                196724   9216  38080      0

/ec2-user/logs/http.log

2016-12-23 02:53:21.505+0000 INFO  [REQUEST] [AsyncLog @ 2016-12-23 02:53:21.505+0000]  0:0:0:0:0:0:0:1 -  [Thu Dec 22 21:53:21 EST 2016] "/browser/views/frame-cypher.html?null" 200 22972 "http://localhost:7575/browser/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14" 2

query.log

2016-12-23 13:33:39.059+0000 INFO  175 ms: bolt-session bolt    neo4j   neo4j-javascript/[object Object]        client/0:0:0:0:0:0:0:1:64019    server/0:0:0:0:0:0:0:1:7688>    neo4j - MATCH (a)-[r]->(b) WHERE id(a) IN {node_ids}
AND id(b) IN {new_node_ids}
RETURN r; - {node_ids: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], new_node_ids: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]} - {}

Disclaimer: I believe a proper architecture involves a clustered set of server mode bolt enabled Neo4j servers using causal clustering under the raft protocol. This undocumented/internal API is used as a temporary workaround to get the neo4j browser up on an embedded Neo4j server in 3.1.0 as it used to work under the WrappingNeoServerBootstrapper in 2.3. Use of the embedded server for faster traversals was the use case - performance must be reevaluated to see if a bolt based architecture is more optimal for the traversal API. /michael

like image 42
Michael O'Brien Avatar answered Nov 03 '22 20:11

Michael O'Brien