neo4j-contrib / neovis.js

Neo4j + vis.js = neovis.js. Graph visualizations in the browser with data from Neo4j.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

rerendering results in websocket consumption

wvullhorst opened this issue · comments

I started a small project with neovis.js today. Everything works fine at first look but now I observe a problem:
Rendering my graph the first and second time works fine. After that every new rendering doubles the websocket connections in "pending" state. When the number reaches 32, exceptions are printed to the console.
Depending on my strategy to update the graph (when selecting a node) the websocket consumption differs.

I use the movie database example for my tests. The render method looks like this:

function render(config) {
    viz = new NeoVis.default(config);
    viz.renderWithCypher(cypherQuery());
    viz.registerOnEvent("completed", (e) => {
        viz.network.on("click", (e) => {
            if(e.nodes[0]) {
                currentId = e.nodes[0];
                viz.reload();
            }
        });
    });
}

Screenshots of my tests:

Initial state after page load

Initial state after page load

Node selected and refresh done
Node selected and refresh done 1

Same node selected again and refresh done
Same node selected again and refresh done

Here comes the full source code:

<!doctype html>
<html>
<head>
    <title>Neo4J Graph Explorer</title>
    <style type="text/css">
        html, body {
            font: 16pt arial;
        }

        #viz {
            width: 900px;
            height: 700px;
            border: 1px solid lightgray;
            font: 10pt arial;
        }
    </style>
    <script src="https://rawgit.com/neo4j-contrib/neovis.js/master/dist/neovis.js"></script>
    <script type="text/javascript">
        const relations = "<-[r:ACTED_IN*1..3]->";
        const CFG = {
            server: {
                url: "bolt://syn1:7687",
                user: "neo4j",
                password: "************",
            },
            query: {
                initial: "MATCH (m:Movie { title: \"The Matrix Reloaded\" })" + relations + "(a) RETURN *",
                byId: (id) => "MATCH (a) " + relations + "(b) WHERE ID(a)=" + id + " RETURN *"
            },
            render: {
                node: {
                    active: { color: '#d00', size: 15 },
                    inactive: { color: '#55a', size: 10 },
                    fontSize: 14
                },
                link: {
                    color: '#888'
                }
            }
        }
        let viz;
        let currentId;
        function draw() {
            const config = {
                containerId: "viz",
                neo4j: {
                    serverUrl: CFG.server.url,
                    serverUser: CFG.server.user,
                    serverPassword: CFG.server.password,
                },
                visConfig: {
                    nodes: {
                        shape: 'dot',
                        size: CFG.render.node.inactive.size,
                        font: { size: CFG.render.node.fontSize },
                        color: CFG.render.node.inactive.color
                    },
                    edges: {
                        arrows: {
                            to: {enabled: true},
                        },
                        color: CFG.render.link.color,
                    },
                    physics: {
                        solver: 'forceAtlas2Based'
                    }
                },
                labels: {
                    Movie: {
                        label: "title",
                        [NeoVis.NEOVIS_ADVANCED_CONFIG]: {
                            function: { color: nodeColor, size: nodeSize, physics: nodePhysics }
                        },
                    },
                    Person: {
                        label: "name",
                        [NeoVis.NEOVIS_ADVANCED_CONFIG]: {
                            function: { color: nodeColor, size: nodeSize, physics: nodePhysics }
                        },
                    }
                },
            };
            render(config);
        }
        function nodeColor(node) {
            if(node.identity === currentId) return CFG.render.node.active.color;
            return CFG.render.node.inactive.color;
        }
        function nodeSize(node) {
            if(node.identity === currentId) return CFG.render.node.active.size;
            return CFG.render.node.inactive.size;
        }
        function nodePhysics(node) {
            if(node.identity === currentId) return false;
            return true;
        }
        function cypherQuery() {
            if(currentId) return CFG.query.byId(currentId);
            return CFG.query.initial;
        }
        function render(config) {
            viz = new NeoVis.default(config);
            viz.renderWithCypher(cypherQuery());
            viz.registerOnEvent("completed", (e) => {
                viz.network.on("click", (e) => {
                    if(e.nodes[0]) {
                        currentId = e.nodes[0];
                        viz.reload();
                    }
                });
            });
        }
    </script>
</head>
<body onload="draw()">
    <div id="viz"></div>
</body>
</html>

additional info (latest results)

Changing the event handler (see below) improves the behavior. Before, the number of websocket connection doubles at each node click, after the number only increases by 1 at each node click...

        function render(config) {
            viz = new NeoVis.default(config);
            viz.renderWithCypher(cypherQuery());
            viz.registerOnEvent("completed", (e) => {
                viz.network.on("click", (e) => {
                    if(e.nodes[0]) {
                        currentId = e.nodes[0];
                        render(config);
                    }
                });
            });
        }

That's wierd... There shouldn't be a new neo4j session, Im pretty sure chrome network panel has which line of code generated the websocket, I checked the code and I did close the session on render complete, which makes it really weird as you didn't use any cypher config...

Also checked I did close session when used, that's why the websocket still open?

First connection:

  | n | @ | browser-channel.js:45
-- | -- | -- | --
  | (anonymous) | @ | browser-channel.js:215
  | t | @ | browser-channel.js:56
  | o | @ | connection-channel.js:93
  | e.createChannelConnection | @ | connection-channel.js:95
  | C._createChannelConnection | @ | connection-provider-pooled.js:109
  | e._createConnection | @ | connection-provider-pooled.js:133
  | (anonymous) | @ | pool.js:261
  | (anonymous) | @ | pool.js:51
  | (anonymous) | @ | pool.js:32
  | (anonymous) | @ | pool.js:26
  | r | @ | pool.js:22
  | t._acquire | @ | pool.js:208
  | t.acquire | @ | pool.js:106
  | e.acquireConnection | @ | connection-provider-direct.js:102
  | t.initializeConnection | @ | connection-holder.js:87
  | t._run | @ | session.js:161
  | t.run | @ | session.js:139
  | render | @ | neovis.ts:371
  | renderWithCypher | @ | neovis.ts:528
  | render | @ | vis.html:103
  | draw | @ | vis.html:83
  | onload | @ | vis.html:115

Subsequent connections using viz.reload():

  | n | @ | browser-channel.js:45
-- | -- | -- | --
  | (anonymous) | @ | browser-channel.js:215
  | t | @ | browser-channel.js:56
  | o | @ | connection-channel.js:93
  | e.createChannelConnection | @ | connection-channel.js:95
  | C._createChannelConnection | @ | connection-provider-pooled.js:109
  | e._createConnection | @ | connection-provider-pooled.js:133
  | (anonymous) | @ | pool.js:261
  | (anonymous) | @ | pool.js:51
  | (anonymous) | @ | pool.js:32
  | (anonymous) | @ | pool.js:26
  | r | @ | pool.js:22
  | t._acquire | @ | pool.js:208
  | t.acquire | @ | pool.js:106
  | e.acquireConnection | @ | connection-provider-direct.js:102
  | t.initializeConnection | @ | connection-holder.js:87
  | t._run | @ | session.js:161
  | t.run | @ | session.js:139
  | render | @ | neovis.ts:371
  | reload | @ | neovis.ts:509
  | (anonymous) | @ | vis.html:112
  | e.emit | @ | vis-network.js:1502
  | value | @ | vis-network.js:36352
  | value | @ | vis-network.js:35061
  | (anonymous) | @ | vis-network.js:33439
  | e.emit | @ | vis-network.js:7332
  | n.emit | @ | vis-network.js:6480
  | e.tryEmit | @ | vis-network.js:6266
  | e.recognize | @ | vis-network.js:6319
  | e.recognize | @ | vis-network.js:7169
  | fd | @ | vis-network.js:5362
  | e.handler | @ | vis-network.js:5585
  | domHandler | @ | vis-network.js:5437

Subsequent connections reinitializing viz (see additional info in original post)

  | n | @ | browser-channel.js:45
-- | -- | -- | --
  | (anonymous) | @ | browser-channel.js:215
  | t | @ | browser-channel.js:56
  | o | @ | connection-channel.js:93
  | e.createChannelConnection | @ | connection-channel.js:95
  | C._createChannelConnection | @ | connection-provider-pooled.js:109
  | e._createConnection | @ | connection-provider-pooled.js:133
  | (anonymous) | @ | pool.js:261
  | (anonymous) | @ | pool.js:51
  | (anonymous) | @ | pool.js:32
  | (anonymous) | @ | pool.js:26
  | r | @ | pool.js:22
  | t._acquire | @ | pool.js:208
  | t.acquire | @ | pool.js:106
  | e.acquireConnection | @ | connection-provider-direct.js:102
  | t.initializeConnection | @ | connection-holder.js:87
  | t._run | @ | session.js:161
  | t.run | @ | session.js:139
  | render | @ | neovis.ts:371
  | renderWithCypher | @ | neovis.ts:528
  | render | @ | vis.html:103
  | (anonymous) | @ | vis.html:108
  | e.emit | @ | vis-network.js:1502
  | value | @ | vis-network.js:36352
  | value | @ | vis-network.js:35061
  | (anonymous) | @ | vis-network.js:33439
  | e.emit | @ | vis-network.js:7332
  | n.emit | @ | vis-network.js:6480
  | e.tryEmit | @ | vis-network.js:6266
  | e.recognize | @ | vis-network.js:6319
  | e.recognize | @ | vis-network.js:7169
  | fd | @ | vis-network.js:5362
  | e.handler | @ | vis-network.js:5585
  | domHandler | @ | vis-network.js:5437

This is really wierd, it looks like driver bug as I do close this session onComplete, Ill test that out later this week

Also for just changing color I would not suggest reloading the entire network, you can get the id from viz.nodes.get and change the data there

Also for just changing color I would not suggest reloading the entire network, you can get the id from viz.nodes.get and change the data there

When you select a node, a new cypher query is executed with the selected node being the "the root of the query" (see viz.renderWithCypher(cypherQuery()))

In the second one, yes, in the original you only called reload 😅 anyway Ill test why the session doesn't close even though I call close probably tomorrow

this one is almost a year old...but maybe this helps, as it seams to fit into the same area (possible the same root cause)

With every reload() an extra "clickNode" event is created. What I mean: First time you create viz, everything is fine. I registerOnEvent for "clickNode", user clicks, and the function is called.
If you now do a reload, the clickNode is fired twice. Another reload: three times clickNode. (just print it to the console and have a look).

That said: Something like this could cause internnaly to create multiple requests to the server, hence the websocket is spammed.

Just as an idea.

@mdopp that's a bug 😅 but it shouldn't affect the websocket as the event doesn't add any session to the neo4j