Discount for my course: High Performance Coding with .NET Core and C#

Gergely Kalapos


Changing the JavaScript engine of MongoDB

Posted on July 13, 2014



This semester I was a part of a small project with Oracle labs. The goal was to see if the JavaScript engine of MongoDB could be replaced by a JVM based JavaScript engine and if so then building a prototype to show how this can be done.

This post is a documentation/wrap up for that project.

Initial State

MongoDB is an open source C++ project hosted on GitHub and it currently uses Google’s V8 JavaScript engine to run the stored procedures. (In MongoDB everything is stored as a JSON object and stored procedures are written in JavaScript)

The original source code can be found here: 

https://github.com/mongodb/mongo/tree/master

We wanted to cut off V8 and start a JVM with the Nashorn JavaScript engine and every time when a stored procedure is used instead of a V8 call we wanted to push the JavaScript code to Nashorn evaluate it there and push the result back to Mongo.

We checked out the tag 2.5.5 and we built all our modifications for this tag. 

The things we did

The result of the examination of the source code:

The interesting part regarding the JavaScript evaluation can be found in the src/mongo/scripting/engine_v8.h (and in the engine_v8.cpp) files. 

The two classes which are responsible for the handling of all the JavaScript code are the ScriptEngine and the Scope classes. 

This two classes are subclassed and the ScriptEngine is initialised with the actual Scope instance which is used by the JavaScript engine. The two classes which we completely modified are the V8Scope and the V8ScriptEingine and we replaced them with our FakeScope and FakeScriptEngine (which is pretty bad naming.. it should be something like JVMScriptEngine and JVMScope).

The most important things happen in the invoke function of the FakeScope class. Here we get the JavaScript code as a char* from Mongo, we do some stuff with it and then it is pushed to the JVM. Inside the JVM we start Nashorn which evaluates the JavaScript and pushes back the result to the C++ layer. One problem was that we were not able to query the database from Java via JNI, so we decided to use a Java driver to mongo. (This is the first point which we should improve). With Java8/Nashorn we can back up the JS objects by a Java class. The logic to access the database is in the MongoManager.java file and this class is available as the variable ‘mongo' from JS.

Compilation of our modified version

First you have to install the JDK 8 (or newer) and all the stuff which is required to build mongo (Scons/python etc. see: http://www.mongodb.org/about/contributors/tutorial/build-mongodb-from-source/ )

The source code can be found here: https://github.com/gergo89/mongo .

Important: you have to switch to the branch “mongo_r255_jvm”. 

As the read me file describes you have to adopt the dir/SConstruct file so the c++ compiler can reference the JNI headers from your machine. 

The configuration for the JVM begins at line 480 and ends at line 845. Currently it is configured for 64bit Windows. Modify these 4 lines to your environment:

env.Append(CPPPATH = ['C:/Program Files/Java/jdk1.8.0/include'])

env.Append(CPPPATH = ['C:/Program Files/Java/jdk1.8.0/include/win32'])

env.Append(LIBPATH = ['C:/Program Files/Java/jdk1.8.0/lib/'])

env.Append(LIBS = ['C:/Program Files/Java/jdk1.8.0/lib/jvm.lib'])

After these steps you can compile the code with scons. 

The next step is to compile the Java code. The source can be found unser src/mongo/java . Compile these files with a java 8 javac. 

The compiled java classes must remain in this folder because when we start the JVM the C++ code looks into this folder for the Java classes. (this can be seen in the src/mongo/scripting/engine_v8.h (line 667)).

After compiling the java code you can start Mongo and every stored procedure will be executed in the JVM with Nashorn. 

List of modified files

  • SConstruct:

    jvm and jni related files are included

    

  • mongo\db\dbeval.cpp:

    calls our invoke instead of the original

  

  • mongo\scripting\engine.cpp:

    invoke function modification

  • mongo\scripting\engine.h:

    invoke function modification

  

  • mongo\scripting\engine_v8.cpp:

    creates the fake scriptengine as global scriptengine

  • mongo\scripting\engine_v8.h:

    replaced with fake scope and scriptengine

    java invocation' thread is here

  

  • mongo\scripting\v8_db.cpp:

    removed

  

  • mongo\scripting\v8_db.h:

    removed

  

  • mongo\scripting\v8_utils.cpp:

   removed


;