Janusgraph not using index in production

Viewed 207

Problem

When performing queries in my production environment, the index is not being used and a full scan is performed, but my development environment works fine and uses the index.

After looking deeper at the problem in production, it also seems that the index information is being saved to the storage backend, but the data is not, and is being stored locally. I have no idea why this is...

I will explain the architecture now:

Environments

The following describe my two environments. Important to note, the index in question is a composite index, as such uses the storage backend, but I still included the index-backend in the architecture environment (aka Elasticsearch).

Both local and production environment versions are the same, i.e:

  • Janusgraph: 0.5.2
  • ScyllaDB: 0.5.2
  • Elasticsearch: 7.13.1

Local Environment

Services are running in docker-compose, consisting of a single Janusgraph instance, a single ScyllaDB instance, and a single Elasticsearch Instance.

Production Environment

Running on AWS, kubernetes cluster managed with EKS, I have multiple janusgraph deployments, which connect to a ScyllaDB cluster (in the same k8s cluster), which is done via Scylla For Kubernetes (https://operator.docs.scylladb.com/stable/), and an Elasticsearch cluster.

Setup

The following will give the simplest example I can that contains the problems I describe.

  1. I pre-create the index's with the Janusgraph management system, such as:

    # management.groovy
    import org.janusgraph.graphdb.database.management.ManagementSystem
    cluster = Cluster.open("/opt/janusgraph/my_scripts/gremlin.yaml")
    client = cluster.connect()
    graph = JanusGraphFactory.open("/opt/janusgraph/my_scripts/env.properties")
    g = graph.traversal().withRemote(DriverRemoteConnection.using(client, "g"))
    m = graph.openManagement()
    uid_property = m.makePropertyKey("uid").dataType(String).make()
    user_label = m.makeVertexLabel("User").make()
    m.buildIndex("index::User::uid", Vertex.class).addKey(uid_property).indexOnly(user_label).buildCompositeIndex()
    m.commit()
    
  2. Upon inspection with m.printSchema() I can see that the index's are ENABLED, in both my local environment and production environment.

  3. I proceed to import all the data that needs to exist on the graph, both local env and production env are OK.

Performing Queries

The following outline what happens when I run a query

Local Environment

What we see here is a simple lookup just to check that the query is using the index:

gremlin> g.V().has("User", "uid", "00003b90-dcc2-494d-a179-ac9009029501").profile()
==>Traversal Metrics
Step                                                               Count  Traversers       Time (ms)    % Dur
=============================================================================================================
JanusGraphStep([],[~label.eq(User), uid.eq(...                     1           1           1.837   100.00
    \_condition=(~label = User AND uid = 00003b90-dcc2-494d-a179-ac9009029501)
    \_isFitted=true
    \_query=multiKSQ[1]@4000
    \_index=index::User::uid
    \_orders=[]
    \_isOrdered=true
  optimization                                                                                 0.038
  optimization                                                                                 0.497
  backend-query                                                        1                       0.901
    \_query=index::User::uid:multiKSQ[1]@4000
    \_limit=4000
                                            >TOTAL                     -           -           1.837        -

Production Environment

Again, we run the query to see if it using the index (which it is not)

g.V().has("User", "uid", "00003b90-dcc2-494d-a179-ac9009029501").profile()
==>Traversal Metrics
Step                                                               Count  Traversers       Time (ms)    % Dur
=============================================================================================================
JanusGraphStep([],[~label.eq(User), uid.eq(...                     1           1       11296.568   100.00
    \_condition=(~label = User AND uid = 00003b90-dcc2-494d-a179-ac9009029501)
    \_isFitted=false
    \_query=[]
    \_orders=[]
    \_isOrdered=true
  optimization                                                                                 0.025
  optimization                                                                                 0.102
  scan                                                                                         0.000
    \_condition=VERTEX
    \_query=[]
    \_fullscan=true
                                            >TOTAL                     -           -       11296.568        -

What Happened? So far my best guess:

The storage backend is NOT being used for storing data, but is being used for storing information about the indexes

Update: Aug 16 2021, after digging around some more I found out something interesting

It is now clear that the data is actually not being saved to the storage backend at all.

In my local environment I set the storage.directory environment variable to /var/lib/janusgraph/data, which mounts onto an empty directory, this directory remains empty. Any vertex/edge updates get's saved to the scyllaDB storage backend, and the data persists between janusgraph instance restarts.

In my production environment, this directory (/var/lib/janusgraph/data) is populated with files:

-rw-r--r-- 1 janusgraph janusgraph     0 Aug 16 05:46 je.lck
-rw-r--r-- 1 janusgraph janusgraph  9650 Aug 16 05:46 je.config.csv
-rw-r--r-- 1 janusgraph janusgraph   450 Aug 16 05:46 je.info.0
-rw-r--r-- 1 janusgraph janusgraph     0 Aug 16 05:46 je.info.0.lck
drwxr-xr-x 2 janusgraph janusgraph   118 Aug 16 05:46 .
-rw-r--r-- 1 janusgraph janusgraph  7533 Aug 16 05:46 00000000.jdb
drwx------ 1 janusgraph janusgraph    75 Aug 16 05:53 ..
-rw-r--r-- 1 janusgraph janusgraph 19951 Aug 16 06:09 je.stat.csv

and any subsequent updates on the graph seem to be reflected here, the update do not get put onto the storage backend, and other janusgraph instances on kubernetes cannot see any changes other instances make, leading me to come to the conclusion, the storage backend is not being used for storing data

The domain name used for the storage.hostname and index.hostname both resolve to IP address's, confirmed with using nslookup.

The endpoints must also work, as the keyspace janusgraph is created, and also has a different replication factor that I defined, and also retains the index information regardless of restarting the janusgraph instances.

Idea 1 (Index is not enabled)

This was disproved via running m.printSchema() showing that all the index's were ENABLED

Idea 2 (Storage backends have different data)

I looked at the data stored in scylladb, and got a summary with nodetool cfstats, this does show something different:

# Local
Keyspace : janusgraph
    Read Count: 1688328
    Read Latency: 2.5682805710738673E-5 ms
    Write Count: 1055210
    Write Latency: 1.702409946835227E-5 ms
    ...
    Memtable cell count: 126411
    Memtable data size: 345700491
    Memtable off heap memory used: 480247808

# Production
Keyspace : janusgraph
    Read Count: 6367
    Read Latency: 2.1203078372860058E-5 ms
    Write Count: 21
    Write Latency: 0.0 ms
    ...
    Memtable cell count: 4
    Memtable data size: 10092
    Memtable off heap memory used: 131072

Although I don't know how to explain the difference, it is clear that both backends contain all the data, verified with various count() queries over labels, such as g.V().hasLabel("User").count(), which both environments report the same result

Idea 3 (Elasticsearch Warnings)

When launching a gremlin console session, there is a difference in that the production environment shows:

07:27:09 WARN  org.elasticsearch.client.RestClient  - request [PUT http://*******<i_removed_the_domain>******:9200/_cluster/settings] returned 3 warnings: [299 Elasticsearch-7.13.4-c5f60e894ca0c61cdbae4f5a686d9f08bcefc942 "[node.data] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version."],[299 Elasticsearch-7.13.4-c5f60e894ca0c61cdbae4f5a686d9f08bcefc942 "[node.master] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version."],[299 Elasticsearch-7.13.4-c5f60e894ca0c61cdbae4f5a686d9f08bcefc942 "[node.ml] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation for the next major version."]

but as my problem is using composite index's, I believe we can disregard elasticsearch warnings.

Idea 4 (ScyllaDB cluster node resources)

Another idea I had was increasing the node resources, even with 7gb RAM, the problem still persists.

Finally...

I don't know what to try next in order to solve this problem, this is my first time pushing Janusgraph into production and perhaps I have missed something important. I have been stuck on this problem for quite a while, hence now asking the community here for help.

Thank you very much for reading this for, and hopefully helping me to solve this problem

1 Answers

I solved the problem myself, I realised that my K8s Deployment .yaml file I use for deploying needed all environment variables to have the prefix janusgraph., as such the janusgraph server was starting with all default variables rather than my selected ones.

Every-time I was creating a gremlin shell session (which connected to it's localhost server), although I was specifying all the correct endpoints and configuration, it was still saving the data according to default janusgraph variables. Although, even in this case, I don't know why the index's were successfully created on my specified backend.

But none the less, the solution was to make sure environment variables have the prefix janusgraph.

Related