Skip to content

This is the multi-page printable view of this section. .

Return to the regular view of this page.

HugeGraph Architecture Overview

Full-Stack Components

The HugeGraph ecosystem combines a graph database, graph computing engines, graph AI, and a toolchain with distinct responsibilities. HugeGraph Server provides OLTP graph database services; HugeGraph-Computer and Vermeer are independent OLAP engines; HugeGraph-AI provides graph AI capabilities. Toolchain clients, import tools, visualization, and operations tools provide entry points for applications and operators.

HugeGraph architecture: Toolchain, AI, Computer, and Vermeer connections to Server, PD, and Store

Figure: Server uses HStore as an example backend; see the Mermaid diagram below for independent backends such as RocksDB and HBase. Vermeer and HugeGraph-Computer are independent OLAP engines. Dashed arrows indicate optional integration.

Ecosystem Integration

flowchart TB
  Apps["Applications / Operations"] --> Toolchain["Toolchain"] --> Server["Server REST API"]
  AI["AI"] -. REST .-> Server
  Computer["Computer"] -. REST .-> Server
  Vermeer["Vermeer"] -. PD .-> PD["PD"]
  Vermeer -. Scan .-> Store["Store"]
  Vermeer -. Write .-> Server

Optional connections do not mean every AI, Computer, or Vermeer task must connect to Server, PD, or Store. Computer and AI read and write through the Server REST API. Vermeer’s HugeGraph input queries PD for partition metadata and scans HStore partitions directly through Store. With HugeGraph result output, it writes results back through the Server REST API.

Server Internals

flowchart TB
  API["REST API / Gremlin"] --> Core["Core"] --> Adapter["Backend adapter"]
  Adapter --> RocksDB["RocksDB"]
  Adapter --> HBase["HBase"]
  Adapter --> HStore["HStore"]
  HStore --> PD["PD"]
  HStore --> Store["Store"]

Server REST APIs and Gremlin send requests to Core, which reads and writes through a backend adapter. RocksDB and HBase are independent backends. The HStore adapter obtains cluster metadata and partition information from PD and sends graph operations to Store nodes. PD manages metadata and partitions; it does not carry graph data reads and writes.

Current Server implementations include RocksDB, HBase, HStore, and memory backends. Memory is for testing or temporary use, not a persistent production deployment. See the Server quick start for production deployment options.

Main Toolchain components:

  • Hubble — Web visualization and management workspace for schema, imports, and queries.
  • Loader — Multi-source batch data import utility for fast bulk loading.
  • Tools — CLI operations tool for cluster maintenance, backup, and restore.
  • Client — Official programmatic client SDKs available in Java, Python, and Go.

Independent Graph Computing Engines

  • Vermeer — High-performance Go in-memory graph computing engine with 15+ OLAP algorithms.
  • HugeGraph-Computer — Java distributed graph computing framework based on BSP/Pregel.

Historical Architecture

The following diagram is retained for historical reference. Its depiction of Server providing both OLTP and OLAP, and of various old backends, does not describe current master.

Expand the historical architecture diagram
Historical HugeGraph architecture, for reference only
  • HugeGraph Plugin

    Extension mechanism, SPI registration, and plugin lifecycle in HugeGraph.

  • Design Concepts (legacy)

    Historical background on the property graph model, graph partitioning, and early architecture tradeoffs.

1 - HugeGraph Plugin mechanism and plug-in extension process

For plugins targeting the planned 1.8.0 shared-foundation API, follow the Java/SPI migration guide and recompile against matching artifacts. The examples below describe the 1.7.0 API.

Background

  1. HugeGraph is not only open source and open, but also simple and easy to use. General users can easily add plug-in extension functions without changing the source code.
  2. HugeGraph supports a variety of built-in storage backends, and also allows users to extend custom backends without changing the existing source code.
  3. HugeGraph supports full-text search. The full-text search function involves word segmentation in various languages. Currently, there are 7 built-in word breakers (ansj, hanlp, smartcn, jieba, jcseg, mmseg4j, ikanalyzer), and it also allows users to expand custom word breakers without changing the existing source code.

Scalable dimension

Currently, the plug-in method provides extensions in the following dimensions:

  • backend storage
  • serializer
  • Custom configuration items
  • tokenizer

Plug-in implementation mechanism

  1. Provide SPI interface: HugeGraph provides the HugeGraphPlugin interface, supporting pluggability via Java SPI.
  2. Registration functions: HugeGraphPlugin exposes static registration methods: registerOptions(), registerBackend(), registerSerializer(), and registerAnalyzer().
  3. Implement components: The developer implements the corresponding OptionHolder, BackendStoreProvider, AbstractSerializer, or Analyzer interfaces.
  4. Register in plugin: The developer implements the register() method of HugeGraphPlugin to register implementation classes, declared in META-INF/services/.
  5. Deploy and activate: Place the packaged JAR in the HugeGraph Server plugins/ directory, configure the custom options, and restart the service.

Plug-in implementation process example

1 Create a new maven project

1.1 Name the project name: hugegraph-plugin-demo
1.2 Add hugegraph-core Jar package dependencies

The Maven pom.xml is shown below. Match hugegraph.version to the target Server version; this example uses the current master version 1.7.0:

 1<?xml version="1.0" encoding="UTF-8"?>
 2
 3<project xmlns="http://maven.apache.org/POM/4.0.0"
 4         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 5         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 6
 7    <modelVersion>4.0.0</modelVersion>
 8    <groupId>org.apache.hugegraph</groupId>
 9    <artifactId>hugegraph-plugin-demo</artifactId>
10    <version>1.0.0</version>
11    <packaging>jar</packaging>
12
13    <name>hugegraph-plugin-demo</name>
14
15    <properties>
16        <hugegraph.version>1.7.0</hugegraph.version>
17    </properties>
18
19    <dependencies>
20        <dependency>
21            <groupId>org.apache.hugegraph</groupId>
22            <artifactId>hugegraph-core</artifactId>
23            <version>${hugegraph.version}</version>
24        </dependency>
25    </dependencies>
26</project>

2 Realize extended functions

2.1 Extending a custom backend
2.1.1 Implement the interface BackendStoreProvider
  • Realizable interfaces: org.apache.hugegraph.backend.store.BackendStoreProvider
  • Or inherit an abstract class:org.apache.hugegraph.backend.store.AbstractBackendStoreProvider

Take the RocksDB backend RocksDBStoreProvider as an example:

 1public class RocksDBStoreProvider extends AbstractBackendStoreProvider {
 2
 3    protected String database() {
 4        return this.graph().toLowerCase();
 5    }
 6
 7    @Override
 8    protected BackendStore newSchemaStore(HugeConfig config, String store) {
 9        return new RocksDBStore.RocksDBSchemaStore(this, this.database(), store);
10    }
11
12    @Override
13    protected BackendStore newGraphStore(HugeConfig config, String store) {
14        return new RocksDBStore.RocksDBGraphStore(this, this.database(), store);
15    }
16
17    @Override
18    protected BackendStore newSystemStore(HugeConfig config, String store) {
19        return new RocksDBStore.RocksDBSystemStore(this, this.database(), store);
20    }
21
22    @Override
23    public String type() {
24        return "rocksdb";
25    }
26
27    @Override
28    public String driverVersion() {
29        return "1.11";
30    }
31}
2.1.2 Implement interface BackendStore

The main abstract methods of BackendStore are shown below; default helper methods are omitted:

 1public interface BackendStore {
 2    // Store name
 3    String store();
 4
 5    // Stored version
 6    String storedVersion();
 7
 8    // Database name
 9    String database();
10
11    // Get the parent provider
12    BackendStoreProvider provider();
13
14    // Get the system schema store
15    SystemSchemaStore systemSchemaStore();
16
17    // Whether it is the storage of schema
18    boolean isSchemaStore();
19
20    // Open/close database
21    void open(HugeConfig config);
22    void close();
23    boolean opened();
24
25    // Initialize/clear database
26    void init();
27    void clear(boolean clearSpace);
28    boolean initialized();
29
30    // Delete all data of database (keep table structure)
31    void truncate();
32
33    // Add/delete data
34    void mutate(BackendMutation mutation);
35
36    // Query data
37    Iterator<BackendEntry> query(Query query);
38    Iterator<Iterator<BackendEntry>> query(Iterator<Query> queries,
39                                          Function<Query, Query> queryWriter,
40                                          HugeGraph hugeGraph);
41    Number queryNumber(Query query);
42
43    // Transaction
44    void beginTx();
45    void commitTx();
46    void rollbackTx();
47
48    // Get metadata by key
49    <R> R metadata(HugeType type, String meta, Object[] args);
50
51    // Backend features
52    BackendFeatures features();
53
54    // Increase next id for specific type
55    void increaseCounter(HugeType type, long increment);
56
57    // Get current counter for a specific type
58    long getCounter(HugeType type);
59}
2.1.3 Extending custom serializers

The serializer must inherit the abstract class: org.apache.hugegraph.backend.serializer.AbstractSerializer ( implements GraphSerializer, SchemaSerializer) The main interface is defined as follows:

 1public interface GraphSerializer {
 2    BackendEntry writeVertex(HugeVertex vertex);
 3    BackendEntry writeOlapVertex(HugeVertex vertex);
 4    BackendEntry writeVertexProperty(HugeVertexProperty<?> prop);
 5    HugeVertex readVertex(HugeGraph graph, BackendEntry entry);
 6    BackendEntry writeEdge(HugeEdge edge);
 7    BackendEntry writeEdgeProperty(HugeEdgeProperty<?> prop);
 8    HugeEdge readEdge(HugeGraph graph, BackendEntry entry);
 9    CIter<Edge> readEdges(HugeGraph graph, BackendEntry bytesEntry);
10    BackendEntry writeIndex(HugeIndex index);
11    HugeIndex readIndex(HugeGraph graph, ConditionQuery query, BackendEntry entry);
12    BackendEntry writeId(HugeType type, Id id);
13    Query writeQuery(Query query);
14}
15
16public interface SchemaSerializer {
17    BackendEntry writeVertexLabel(VertexLabel vertexLabel);
18    VertexLabel readVertexLabel(HugeGraph graph, BackendEntry entry);
19    BackendEntry writeEdgeLabel(EdgeLabel edgeLabel);
20    EdgeLabel readEdgeLabel(HugeGraph graph, BackendEntry entry);
21    BackendEntry writePropertyKey(PropertyKey propertyKey);
22    PropertyKey readPropertyKey(HugeGraph graph, BackendEntry entry);
23    BackendEntry writeIndexLabel(IndexLabel indexLabel);
24    IndexLabel readIndexLabel(HugeGraph graph, BackendEntry entry);
25}
2.1.4 Extend custom configuration items

When adding a custom backend, it may be necessary to add new configuration items. The implementation process mainly includes:

  • Add a configuration item container class and implement the interface org.apache.hugegraph.config.OptionHolder
  • Provide a singleton method public static OptionHolder instance(), and call the method when the object is initialized OptionHolder.registerOptions()
  • Add configuration item declaration, single-value configuration item type is ConfigOption, multi-value configuration item type is ConfigListOption

Take the RocksDB configuration item definition as an example:

 1public class RocksDBOptions extends OptionHolder {
 2
 3    private RocksDBOptions() {
 4        super();
 5    }
 6
 7    private static volatile RocksDBOptions instance;
 8
 9    public static synchronized RocksDBOptions instance() {
10        if (instance == null) {
11            instance = new RocksDBOptions();
12            instance.registerOptions();
13        }
14        return instance;
15    }
16
17    public static final ConfigOption<String> DATA_PATH =
18            new ConfigOption<>(
19                    "rocksdb.data_path",
20                    "The path for storing data of RocksDB.",
21                    disallowEmpty(),
22                    "rocksdb-data/data"
23            );
24
25    public static final ConfigOption<String> WAL_PATH =
26            new ConfigOption<>(
27                    "rocksdb.wal_path",
28                    "The path for storing WAL of RocksDB.",
29                    disallowEmpty(),
30                    "rocksdb-data/wal"
31            );
32
33    public static final ConfigListOption<String> DATA_DISKS =
34            new ConfigListOption<>(
35                    "rocksdb.data_disks",
36                    false,
37                    "The optimized disks for storing data of RocksDB. " +
38                    "The format of each element: `STORE/TABLE: /path/disk`." +
39                    "Allowed keys are [g/vertex, g/edge_out, g/edge_in, " +
40                    "g/vertex_label_index, g/edge_label_index, " +
41                    "g/range_int_index, g/range_float_index, " +
42                    "g/range_long_index, g/range_double_index, " +
43                    "g/secondary_index, g/search_index, g/shard_index, " +
44                    "g/unique_index, g/olap]",
45                    null,
46                    String.class,
47                    ImmutableList.of()
48            );
49}
2.2 Extend custom tokenizer

The tokenizer needs to implement the interface org.apache.hugegraph.analyzer.Analyzer, take implementing a SpaceAnalyzer space tokenizer as an example.

 1package org.apache.hugegraph.plugin;
 2
 3import java.util.Arrays;
 4import java.util.HashSet;
 5import java.util.Set;
 6
 7import org.apache.hugegraph.analyzer.Analyzer;
 8
 9public class SpaceAnalyzer implements Analyzer {
10
11    @Override
12    public Set<String> segment(String text) {
13        return new HashSet<>(Arrays.asList(text.split(" ")));
14    }
15}

3. Implement the plug-in interface and register it

The plug-in registration entry is HugeGraphPlugin.register(), the custom plug-in must implement this interface method, and register the extension items defined above inside it. The interface org.apache.hugegraph.plugin.HugeGraphPlugin is defined as follows:

 1public interface HugeGraphPlugin {
 2
 3    String name();
 4
 5    void register();
 6
 7    String supportsMinVersion();
 8
 9    String supportsMaxVersion();
10}

And HugeGraphPlugin provides 4 static methods for registering extensions:

  • registerOptions(String name, String classPath): register configuration items
  • registerBackend(String name, String classPath): register backend (BackendStoreProvider)
  • registerSerializer(String name, String classPath): register serializer
  • registerAnalyzer(String name, String classPath): register tokenizer

The following is an example of registering the SpaceAnalyzer tokenizer:

 1package org.apache.hugegraph.plugin;
 2
 3public class DemoPlugin implements HugeGraphPlugin {
 4
 5    @Override
 6    public String name() {
 7        return "demo";
 8    }
 9
10    @Override
11    public void register() {
12        HugeGraphPlugin.registerAnalyzer("demo", SpaceAnalyzer.class.getName());
13    }
14
15    @Override
16    public String supportsMinVersion() {
17        return "1.7.0";
18    }
19
20    @Override
21    public String supportsMaxVersion() {
22        return "1.8.0";
23    }
24}

4. Configure SPI entry

  1. Make sure the services directory exists: hugegraph-plugin-demo/resources/META-INF/services
  2. Create a text file in the services directory: org.apache.hugegraph.plugin.HugeGraphPlugin
  3. The content of the file is as follows: org.apache.hugegraph.plugin.DemoPlugin

5. Make Jar package

Through maven packaging, execute the command in the project directory mvn package, and a Jar package file will be generated in the target directory. Copy the Jar package to the plugins directory when using it, and restart the service to take effect.

2 - HugeGraph Design Concepts

1. Property Graph

Version note: This page retains early design material. The property graph model and vertex ID strategies remain consistent with current master, but figures, partitioning conclusions, and transaction descriptions have the scope stated in each section. They are not current deployment or storage implementation guarantees.

There are two common graph data representation models, namely the RDF (Resource Description Framework) model and the Property Graph (Property Graph) model. Both RDF and Property Graph are the most basic and well-known graph representation modes, and both can represent entity-relationship modeling of various graphs. RDF is a W3C standard, while Property Graph is an industry standard and is widely supported by graph database vendors. HugeGraph currently uses Property Graph.

HugeGraph uses the property graph model. This person-and-book example shows labels and properties on vertices and directed edges. It is a logical model, not an illustration of the current internal data layout.

Property graph logical model: a Person vertex reads a Book vertex; vertices and the edge have separate labels and properties

Figure: Vertices and edges each carry their own labels and properties.

Inside HugeGraph, each vertex/edge is identified by a unique VertexId/EdgeId, and the attributes are stored inside the corresponding vertex/edge. The relationship/mapping between vertices is stored through edges.

When the vertex attribute value is stored by edge pointer, if you want to update a vertex-specific attribute value, you can directly write it by overwriting. The disadvantage is that the VertexId is redundantly stored; if you want to update the attribute of the relationship, you need to use the read-and-modify method , read all attributes first, modify some attributes, and then write to the storage system, the update efficiency is low. According to experience, there are more requirements for modifying vertex attributes, but less for edge attributes. For example, calculations such as PageRank and Graph Cluster require frequent modification of vertex attribute values.

2. Graph Partition Scheme

Version note: The Edge Cut and Vertex Cut comparison is historical design background. Current PD and Store code does not define “HugeGraph uses Edge Cut” as an implementation contract. Do not use that conclusion to describe current partitioning or replication; see the HStore quick start for current distributed deployments.

For distributed graph databases, there are two partition storage methods for graphs: Edge Cut and Vertex Cut, as shown in the following figure. When using the Edge Cut method to store graphs, any vertex will only appear on one machine, while edges may be distributed on different machines. This storage method may lead to multiple storage of edges. When using the Vertex Cut method to store graphs, any edge will only appear on one machine, and each same point may be distributed to different machines. This storage method may result in multiple storage of vertices.

image

The EdgeCut partition scheme can support high-performance insert and update operations, while the VertexCut partition scheme is more suitable for static graph query analysis, so EdgeCut is suitable for OLTP graph query, and VertexCut is more suitable for OLAP graph query. Early design material described HugeGraph as using Edge Cut. That historical conclusion is retained here and does not describe the current partitioning implementation.

3. VertexId Strategy

Vertex of HugeGraph supports four ID strategies. Different VertexLabels in the same graph database can use different Id strategies. Currently, the Id strategies supported by HugeGraph are:

  • Automatic generation (AUTOMATIC): Use the Snowflake algorithm to automatically generate a globally unique Id, Long type;
  • Primary Key (PRIMARY_KEY): Generate Id through VertexLabel+PrimaryKeyValues, String type;
  • Custom (CUSTOMIZE_STRING|CUSTOMIZE_NUMBER): User-defined Id, which is divided into two types: String and Long, and you need to ensure the uniqueness of the Id yourself;
  • Custom UUID (CUSTOMIZE_UUID): User-defined Id in UUID form, you need to ensure the uniqueness of the Id yourself;

The default Id policy is AUTOMATIC, if the user calls the primaryKeys() method and sets the correct PrimaryKeys, the PRIMARY_KEY policy is automatically enabled. After enabling the PRIMARY_KEY strategy, HugeGraph can implement data deduplication based on PrimaryKeys.

  1. AUTOMATIC ID Policy
1schema.vertexLabel("person")
2     .useAutomaticId()
3     .properties("name", "age", "city")
4     .create();
5graph.addVertex(T.label, "person","name", "marko", "age", 18, "city", "Beijing");
  1. PRIMARY_KEY ID policy
1schema.vertexLabel("person")
2     .usePrimaryKeyId()
3     .properties("name", "age", "city")
4     .primaryKeys("name", "age")
5     .create();
6graph.addVertex(T.label, "person","name", "marko", "age", 18, "city", "Beijing");
  1. CUSTOMIZE_STRING ID Policy
1schema.vertexLabel("person")
2     .useCustomizeStringId()
3     .properties("name", "age", "city")
4     .create();
5graph.addVertex(T.label, "person", T.id, "123456", "name", "marko","age", 18, "city", "Beijing");
  1. CUSTOMIZE_NUMBER ID Policy
1schema.vertexLabel("person")
2     .useCustomizeNumberId()
3     .properties("name", "age", "city")
4     .create();
5graph.addVertex(T.label, "person", T.id, 123456, "name", "marko","age", 18, "city", "Beijing");
  1. CUSTOMIZE_UUID ID Policy
1schema.vertexLabel("person")
2     .useCustomizeUuidId()
3     .properties("name", "age", "city")
4     .create();
5graph.addVertex(T.label, "person", T.id, UUID.randomUUID(), "name", "marko","age", 18, "city", "Beijing");

If users need Vertex deduplication, there are three options:

  1. Adopt PRIMARY_KEY strategy, automatic overwriting, suitable for batch insertion of large amount of data, users cannot know whether overwriting has occurred
  2. Adopt AUTOMATIC strategy, read-and-modify, suitable for small data insertion, users can clearly know whether overwriting occurs
  3. Using the CUSTOMIZE_STRING or CUSTOMIZE_NUMBER strategy, the user guarantees the uniqueness

4. EdgeId policy

Current implementation: The four-part composition below is a historical simplification. Current EdgeId also distinguishes edge direction and parent/child edge labels. See EdgeId in the Server source; use the source for your version when relying on ID formats or persistent keys.

The EdgeId of HugeGraph is composed of srcVertexId + edgeLabel + sortKey + tgtVertexId. Among them sortKey is an important concept of HugeGraph. There are two reasons for adding sortKey to Edge as the unique ID of Edge:

  1. If there are multiple edges of the same Label between two vertices, they can be distinguished by sortKey
  2. For SuperNode nodes, edges can be sorted and truncated by sortKey.

Since EdgeId is composed of srcVertexId + edgeLabel + sortKey + tgtVertexId, HugeGraph will automatically overwrite when the same Edge is inserted multiple times to achieve deduplication. It should be noted that the properties of Edge will also be overwritten in the batch insert mode.

In addition, because HugeGraph’s EdgeId adopts an automatic deduplication strategy, HugeGraph considers that there is only one edge in the case of self-loop (a vertex has an edge pointing to itself), while a graph database that uses the AUTOMATIC strategy (TitanDB for example) considers that the graph has two edges.

The edges of HugeGraph only support directed edges, and undirected edges can be realized by creating two edges, Out and In.

5. HugeGraph transaction overview

Version scope: The isolation-level, thread-binding, and backend atomicity descriptions below are historical implementation notes. Transaction isolation and rollback boundaries depend on the backend implementation; do not treat these historical descriptions as cross-backend guarantees.

TinkerPop transaction overview

A TinkerPop transaction refers to a unit of work that performs operations on the database. A set of operations within a transaction either succeeds or all fail. For a detailed introduction, please refer to the official documentation of TinkerPop: http://tinkerpop.apache.org/docs/current/reference/#transactions

TinkerPop transaction interfaces
  • open open transaction
  • commit commit transaction
  • rollback rollback transaction
  • close closes the transaction
TinkerPop transaction specification
  • The transaction must be explicitly committed before it can take effect (the modification operation can only be seen by the query in this transaction if it is not committed)
  • A transaction must be opened before it can be committed or rolled back
  • If the transaction setting is automatically turned on, there is no need to explicitly turn it on (the default method), if it is set to be turned on manually, it must be turned on explicitly
  • When the transaction is closed, you can set three modes: automatic commit, automatic rollback (default mode), manual (explicit shutdown is prohibited), etc.
  • The transaction must be closed after committing or rolling back
  • The transaction must be open after the query
  • Transactions (non-threaded tx) must be thread-isolated, and multi-threaded operations on the same transaction do not affect each other

For more transaction specification use cases, see: Transaction Test

HugeGraph transaction implementation
  • All operations in a transaction either succeed or fail
  • A transaction can only read what has been committed by another transaction (Read committed)
  • All uncommitted operations can be queried in this transaction, including:
    • Adding a vertex can query the vertex
    • Delete a vertex to filter out the vertex
    • Deleting a vertex can filter out the related edges of the vertex
    • Adding an edge can query the edge
    • Delete edge can filter out the edge
    • Adding/modifying (vertex, edge) attributes can take effect when querying
    • Delete (vertex, edge) attributes can take effect at query time
  • All uncommitted operations become invalid after the transaction is rolled back, including:
    • Adding and deleting vertices and edges
    • Addition/modification, deletion of attributes

Example: One transaction cannot read another transaction’s uncommitted content

 1    static void testUncommittedTx(final HugeGraph graph) throws InterruptedException {
 2
 3        final CountDownLatch latchUncommit = new CountDownLatch(1);
 4        final CountDownLatch latchRollback = new CountDownLatch(1);
 5
 6        Thread thread = new Thread(() -> {
 7            // this is a new transaction in the new thread
 8            graph.tx().open();
 9
10            System.out.println("current transaction operations");
11
12            Vertex james = graph.addVertex(T.label, "author",
13                                           "id", 1, "name", "James Gosling",
14                                           "age", 62, "lived", "Canadian");
15            Vertex java = graph.addVertex(T.label, "language", "name", "java",
16                                          "versions", Arrays.asList(6, 7, 8));
17            james.addEdge("created", java);
18
19            // we can query the uncommitted records in the current transaction
20            System.out.println("current transaction assert");
21            assert graph.vertices().hasNext() == true;
22            assert graph.edges().hasNext() == true;
23
24            latchUncommit.countDown();
25
26            try {
27                latchRollback.await();
28            } catch (InterruptedException e) {
29                throw new RuntimeException(e);
30            }
31
32            System.out.println("current transaction rollback");
33            graph.tx().rollback();
34        });
35
36        thread.start();
37
38        // query none result in other transaction when not commit()
39        latchUncommit.await();
40        System.out.println("other transaction assert for uncommitted");
41        assert !graph.vertices().hasNext();
42        assert !graph.edges().hasNext();
43
44        latchRollback.countDown();
45        thread.join();
46
47        // query none result in other transaction after rollback()
48        System.out.println("other transaction assert for rollback");
49        assert !graph.vertices().hasNext();
50        assert !graph.edges().hasNext();
51    }
Principle of transaction realization
  • The server internally realizes isolation by binding transactions to threads (ThreadLocal)
  • The uncommitted content of this transaction overwrites the old data in chronological order for this transaction to query the latest version of data
  • The bottom layer relies on the back-end database to ensure transaction atomicity (for example, the batch interface of RocksDB guarantees atomicity)
Notice

REST APIs do not expose a separate transaction lifecycle interface; write APIs invoke the relevant Server-side transaction commit logic.

TinkerPop API allows open transactions, which are automatically closed when the request is completed (Gremlin Server forces close)