How to Set Up Multigraph Support in JanusGraph 1.1.0

July 11, 2025 (4d ago)

Configuring Multiple Graphs in JanusGraph (v1.1.0)

JanusGraph is a distributed, open source, massively scalable graph database. By default, Gremlin Server hosts a single graph. But to manage multiple graphs dynamically, JanusGraph provides the ConfiguredGraphFactory.

This guide walks you through the full setup for multigraph support using JanusGraph v1.1.0 — including configurations, Gremlin Console setup, and a Python client example.


Why Multigraph?

Sometimes your application needs different datasets or tenants logically isolated. With multigraph support, JanusGraph lets you spin up named graphs at runtime — each with its own schema and data.

However, in-memory storage won't work with ConfiguredGraphFactory, so we'll use Cassandra (via CQL) in this tutorial.


Step 1: Custom Gremlin Server Configuration

Create a new file: gremlin-server-configuration.yaml

host: 0.0.0.0
port: 8182
evaluationTimeout: 30000
channelizer: org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer
graphManager: org.janusgraph.graphdb.management.JanusGraphManager
graphs: {
  graph: conf/janusgraph-cql.properties,
  ConfigurationManagementGraph: conf/janusgraph-cql-configurationgraph.properties
}
scriptEngines: {
  gremlin-groovy: {
    plugins: { org.janusgraph.graphdb.tinkerpop.plugin.JanusGraphGremlinPlugin: {},
               org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
               org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {},
               org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]},
               org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample.groovy]}}}}
serializers:
  - { className: org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
processors:
  - { className: org.apache.tinkerpop.gremlin.server.op.session.SessionOpProcessor, config: { sessionTimeout: 28800000 }}
  - { className: org.apache.tinkerpop.gremlin.server.op.traversal.TraversalOpProcessor, config: { cacheExpirationTime: 600000, cacheMaxSize: 1000 }}
metrics: {
  consoleReporter: {enabled: true, interval: 180000},
  csvReporter: {enabled: true, interval: 180000, fileName: /tmp/gremlin-server-metrics.csv},
  jmxReporter: {enabled: true},
  slf4jReporter: {enabled: true, interval: 180000},
  graphiteReporter: {enabled: false, interval: 180000}}
maxInitialLineLength: 4096
maxHeaderSize: 8192
maxChunkSize: 8192
maxContentLength: 65536
maxAccumulationBufferComponents: 1024
resultIterationBatchSize: 64
writeBufferLowWaterMark: 32768
writeBufferHighWaterMark: 65536
 

Step 2: Graph Properties Files

janusgraph-cql-configurationgraph.properties

gremlin.graph=org.janusgraph.core.ConfiguredGraphFactory
storage.backend=cql
graph.graphname=ConfigurationManagementGraph
storage.hostname=127.0.0.1
storage.cql.local-datacenter=datacenter1
cache.db-cache=true
cache.db-cache-clean-wait=20
cache.db-cache-time=180000
cache.db-cache-size=0.5

janusgraph-cql.properties

gremlin.graph=org.janusgraph.core.JanusGraphFactory
storage.backend=cql
storage.hostname=127.0.0.1
storage.cql.keyspace=janusgraph
storage.cql.local-datacenter=datacenter1
cache.db-cache=true
cache.db-cache-clean-wait=20
cache.db-cache-time=180000
cache.db-cache-size=0.5

Step 3: Start the Server

sudo /opt/janusgraph/bin/janusgraph-server.sh start /opt/janusgraph/conf/gremlin-server-configuration.yaml

Step 4: Create a Template Configuration

In Gremlin Console (run from JanusGraph 1.0.0 for compatibility):

:remote connect tinkerpop.server conf/remote.yaml session
:remote console
map = new HashMap()
map.put("storage.backend", "cql")
map.put("storage.hostname", "127.0.0.1")
ConfiguredGraphFactory.createTemplateConfiguration(new MapConfiguration(map))

If all is well, each step should return null.

Note: There's a known bug in JanusGraph 1.1.0's Gremlin Console. Use the Gremlin Console from JanusGraph 1.0.0 as a temporary workaround.


Step 5: Accessing Graphs with Gremlin Python (Flask API Example)

import os
import time
import uuid
from flask import Flask, jsonify
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
from gremlin_python.process.anonymous_traversal import traversal
from gremlin_python.process.graph_traversal import __
from gremlin_python.process.traversal import T
from contextlib import closing
from gremlin_python.driver import serializer
 
GREMLIN_URL = os.getenv("GREMLIN_URL", "ws://localhost:8182/gremlin")
CGF_TIMEOUT = int(os.getenv("CGF_TIMEOUT", "20"))
message_serializer = serializer.GraphSONSerializersV3d0()
app = Flask(__name__)
 
def ensure_graph_bound(project_id):
    from gremlin_python.driver import client
    gremlin = client.Client(GREMLIN_URL, "g")
    script = f"""
    if (ConfiguredGraphFactory.getGraphNames().contains(\"{project_id}\")) {{
        g = ConfiguredGraphFactory.open(\"{project_id}\")
    }} else {{
        g = ConfiguredGraphFactory.create(\"{project_id}\")
    }}
    null
    """
    gremlin.submitAsync(script).result()
    gremlin.close()
    time.sleep(CGF_TIMEOUT)
 
def get_connection(project_id):
    return DriverRemoteConnection(GREMLIN_URL, f"{project_id}_traversal", message_serializer=message_serializer)
 
@app.route("/api/projects/<project_id>")
def project_route(project_id):
    ensure_graph_bound(project_id)
    with closing(get_connection(project_id)) as conn:
        g = traversal().withRemote(conn)
        return jsonify({
            "project": project_id,
            "vertex_count": g.V().count().next(),
            "edge_count": g.E().count().next()
        })
 
@app.route("/api/projects/<project_id>/dummy_insert", methods=["POST"])
def insert_dummy(project_id):
    ensure_graph_bound(project_id)
    with closing(get_connection(project_id)) as conn:
        g = traversal().withRemote(conn)
        uid1, uid2 = str(uuid.uuid4()), str(uuid.uuid4())
        v1 = g.addV("dummy").property("uid", uid1).project("id", "uid").by(T.id).by("uid").next()
        v2 = g.addV("dummy").property("uid", uid2).project("id", "uid").by(T.id).by("uid").next()
        edge = g.V(v1['id']).addE("connected_to").to(__.V(v2['id'])).property("since", time.time()).next()
        return jsonify({
            "project": project_id,
            "node1": v1,
            "node2": v2,
            "edge": {"id": edge.id, "label": "connected_to"}
        })
 
if __name__ == "__main__":
    app.run(debug=True, port=5000)

Wrapping Up

You now have a complete multigraph setup using JanusGraph 1.1.0. You can create and manage isolated graphs dynamically — each graph having its own traversal source and configuration.

This setup is especially powerful for multi-tenant SaaS, modular graph products, and experimentation across datasets.

Stay tuned for future releases, where the known Gremlin Console bug should be resolved. Until then, happy graphing! 🚀