You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hugegraph.apache.org by je...@apache.org on 2025/08/30 14:39:46 UTC

(incubator-hugegraph) branch only-one-id-query-optimize created (now a929396e2)

This is an automated email from the ASF dual-hosted git repository.

jermy pushed a change to branch only-one-id-query-optimize
in repository https://gitbox.apache.org/repos/asf/incubator-hugegraph.git


      at a929396e2 perf(core): optimize queryVerticesByIds for only-one-id query

This branch includes the following new commits:

     new a929396e2 perf(core): optimize queryVerticesByIds for only-one-id query

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(incubator-hugegraph) 01/01: perf(core): optimize queryVerticesByIds for only-one-id query

Posted by je...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jermy pushed a commit to branch only-one-id-query-optimize
in repository https://gitbox.apache.org/repos/asf/incubator-hugegraph.git

commit a929396e2de355689823a3ca6f985d877a2f65ca
Author: Jermy Li <je...@apache.org>
AuthorDate: Sat Aug 30 22:35:18 2025 +0800

    perf(core): optimize queryVerticesByIds for only-one-id query
    
    Change-Id: Ic6e01eb8e2b4c97c5263cfa0f78c58b6033435da
---
 .../hugegraph/backend/tx/GraphTransaction.java     | 272 ++++++++++++++++-----
 1 file changed, 208 insertions(+), 64 deletions(-)

diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java
index e50fa5c6f..515043097 100644
--- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java
+++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java
@@ -102,6 +102,7 @@ import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterators;
 
 import jakarta.ws.rs.ForbiddenException;
@@ -307,8 +308,8 @@ public class GraphTransaction extends IndexableTransaction {
         return false;
     }
 
-    @Watched(prefix = "tx")
     @Override
+    @Watched(prefix = "graph")
     protected BackendMutation prepareCommit() {
         // Serialize and add updates into super.deletions
         if (!this.removedVertices.isEmpty() || !this.removedEdges.isEmpty()) {
@@ -514,6 +515,7 @@ public class GraphTransaction extends IndexableTransaction {
     }
 
     @Override
+    @Watched(prefix = "graph")
     public void commit() throws BackendException {
         try {
             super.commit();
@@ -523,6 +525,7 @@ public class GraphTransaction extends IndexableTransaction {
     }
 
     @Override
+    @Watched(prefix = "graph")
     public void rollback() throws BackendException {
         // Rollback properties changes
         for (HugeProperty<?> prop : this.updatedOldestProps) {
@@ -536,6 +539,7 @@ public class GraphTransaction extends IndexableTransaction {
     }
 
     @Override
+    @Watched(prefix = "graph")
     public QueryResults<BackendEntry> query(Query query) {
         if (!(query instanceof ConditionQuery)) {
             // It's a sysprop-query, don't need to optimize
@@ -550,6 +554,7 @@ public class GraphTransaction extends IndexableTransaction {
     }
 
     @Override
+    @Watched(prefix = "graph")
     public Number queryNumber(Query query) {
         boolean isConditionQuery = query instanceof ConditionQuery;
         boolean hasUpdate = this.hasUpdate();
@@ -709,6 +714,7 @@ public class GraphTransaction extends IndexableTransaction {
         this.afterWrite();
     }
 
+    @Watched(prefix = "graph")
     public Iterator<Vertex> queryAdjacentVertices(Iterator<Edge> edges) {
         if (this.lazyLoadAdjacentVertex) {
             return new MapperIterator<>(edges, edge -> {
@@ -726,15 +732,18 @@ public class GraphTransaction extends IndexableTransaction {
         });
     }
 
+    @Watched(prefix = "graph")
     public Iterator<Vertex> queryAdjacentVertices(Object... vertexIds) {
         return this.queryVerticesByIds(vertexIds, true,
                                        this.checkAdjacentVertexExist);
     }
 
+    @Watched(prefix = "graph")
     public Iterator<Vertex> queryVertices(Object... vertexIds) {
         return this.queryVerticesByIds(vertexIds, false, false);
     }
 
+    @Watched(prefix = "graph")
     public Vertex queryVertex(Object vertexId) {
         Iterator<Vertex> iter = this.queryVerticesByIds(new Object[]{vertexId},
                                                         false, true);
@@ -775,40 +784,103 @@ public class GraphTransaction extends IndexableTransaction {
         return this.queryVerticesByIds(vertexIds, adjacentVertex, checkMustExist, HugeType.VERTEX);
     }
 
+    @Watched(prefix = "graph")
     protected Iterator<Vertex> queryVerticesByIds(Object[] vertexIds, boolean adjacentVertex,
                                                   boolean checkMustExist, HugeType type) {
         Query.checkForceCapacity(vertexIds.length);
 
-        // NOTE: allowed duplicated vertices if query by duplicated ids
-        List<Id> ids = InsertionOrderUtil.newList();
-        Map<Id, HugeVertex> vertices = new HashMap<>(vertexIds.length);
+        List<Id> ids;
+        Map<Id, HugeVertex> vertices;
+        boolean verticesUpdated = this.verticesInTxSize() > 0;
 
-        IdQuery query = new IdQuery(type);
-        for (Object vertexId : vertexIds) {
-            HugeVertex vertex;
-            Id id = HugeVertex.getIdValue(vertexId);
-            if (id == null || this.removedVertices.containsKey(id)) {
-                // The record has been deleted
-                continue;
-            } else if ((vertex = this.addedVertices.get(id)) != null ||
-                       (vertex = this.updatedVertices.get(id)) != null) {
-                if (vertex.expired()) {
-                    continue;
+        if (vertexIds.length == 1) {
+            Id id = HugeVertex.getIdValue(vertexIds[0]);
+
+            boolean tryQueryBackend = true;
+            if (id == null) {
+                tryQueryBackend = false;
+                ids = ImmutableList.of();
+            } else {
+                ids = ImmutableList.of(id);
+            }
+
+            HugeVertex vertex = null;
+            if (id != null && verticesUpdated) {
+                if (this.removedVertices.containsKey(id)) {
+                    // The record has been deleted
+                    tryQueryBackend = false;
+                } else if ((vertex = this.addedVertices.get(id)) != null ||
+                           (vertex = this.updatedVertices.get(id)) != null) {
+                    // Found from local tx
+                    tryQueryBackend = false;
+                    if (vertex.expired()) {
+                        vertex = null;
+                    } else {
+                        assert vertex != null;
+                    }
                 }
-                // Found from local tx
-                vertices.put(vertex.id(), vertex);
+            }
+
+            if (vertex != null) {
+                assert !tryQueryBackend;
+                vertices = ImmutableMap.of(vertex.id(), vertex);
+            } else if (!tryQueryBackend) {
+                assert vertex == null;
+                vertices = ImmutableMap.of();
             } else {
-                // Prepare to query from backend store
-                query.query(id);
+                // Query from backend store
+                IdQuery query = new IdQuery.OneIdQuery(type, id);
+                Iterator<HugeVertex> it = this.queryVerticesFromBackend(query);
+                vertex = QueryResults.one(it);
+                if (vertex == null) {
+                    vertices = ImmutableMap.of();
+                } else {
+                    vertices = ImmutableMap.of(vertex.id(), vertex);
+                }
+            }
+        } else {
+            // NOTE: allowed duplicated vertices if query by duplicated ids
+            ids = InsertionOrderUtil.newList();
+            vertices = new HashMap<>(vertexIds.length);
+
+            IdQuery query = new IdQuery(type);
+            for (Object vertexId : vertexIds) {
+                Id id = HugeVertex.getIdValue(vertexId);
+                if (id == null) {
+                    continue;
+                }
+                boolean foundLocal = false;
+                if (verticesUpdated) {
+                    HugeVertex vertex;
+                    if (this.removedVertices.containsKey(id)) {
+                        // The record has been deleted
+                        continue;
+                    }
+                    if ((vertex = this.addedVertices.get(id)) != null ||
+                        (vertex = this.updatedVertices.get(id)) != null) {
+                        if (vertex.expired()) {
+                            continue;
+                        }
+                        // Found from local tx
+                        foundLocal = true;
+                        vertices.put(vertex.id(), vertex);
+                    } else {
+                        assert !foundLocal;
+                    }
+                }
+                if (!foundLocal) {
+                    // Prepare to query from backend store
+                    query.query(id);
+                }
+                ids.add(id);
             }
-            ids.add(id);
-        }
 
-        if (!query.empty()) {
-            // Query from backend store
-            query.mustSortByInput(false);
-            Iterator<HugeVertex> it = this.queryVerticesFromBackend(query);
-            QueryResults.fillMap(it, vertices);
+            if (!query.empty()) {
+                // Query from backend store
+                query.mustSortByInput(false);
+                Iterator<HugeVertex> it = this.queryVerticesFromBackend(query);
+                QueryResults.fillMap(it, vertices);
+            }
         }
 
         return new MapperIterator<>(ids.iterator(), id -> {
@@ -830,11 +902,13 @@ public class GraphTransaction extends IndexableTransaction {
         });
     }
 
+    @Watched(prefix = "graph")
     public Iterator<Vertex> queryVertices() {
         Query q = new Query(HugeType.VERTEX);
         return this.queryVertices(q);
     }
 
+    @Watched(prefix = "graph")
     public Iterator<Vertex> queryVertices(Query query) {
         if (this.hasUpdate()) {
             E.checkArgument(query.noLimitAndOffset(),
@@ -856,6 +930,7 @@ public class GraphTransaction extends IndexableTransaction {
         return this.skipOffsetOrStopLimit(r, query);
     }
 
+    @Watched(prefix = "graph")
     protected Iterator<HugeVertex> queryVerticesFromBackend(Query query) {
         assert query.resultType().isVertex();
 
@@ -917,14 +992,17 @@ public class GraphTransaction extends IndexableTransaction {
         this.afterWrite();
     }
 
+    @Watched(prefix = "graph")
     public Iterator<Edge> queryEdgesByVertex(Id id) {
         return this.queryEdges(constructEdgesQuery(id, Directions.BOTH, new Id[0]));
     }
 
+    @Watched(prefix = "graph")
     public Iterator<Edge> queryEdges(Object... edgeIds) {
         return this.queryEdgesByIds(edgeIds, false);
     }
 
+    @Watched(prefix = "graph")
     public Edge queryEdge(Object edgeId) {
         Iterator<Edge> iter = this.queryEdgesByIds(new Object[]{edgeId}, true);
         Edge edge = QueryResults.one(iter);
@@ -934,57 +1012,121 @@ public class GraphTransaction extends IndexableTransaction {
         return edge;
     }
 
+    @Watched(prefix = "graph")
     protected Iterator<Edge> queryEdgesByIds(Object[] edgeIds,
                                              boolean verifyId) {
         Query.checkForceCapacity(edgeIds.length);
 
-        // NOTE: allowed duplicated edges if query by duplicated ids
-        List<Id> ids = InsertionOrderUtil.newList();
-        Map<Id, HugeEdge> edges = new HashMap<>(edgeIds.length);
+        List<Id> ids;
+        Map<Id, HugeEdge> edges;
+        boolean edgesUpdated = this.edgesInTxSize() > 0;
 
-        IdQuery query = new IdQuery(HugeType.EDGE);
-        for (Object edgeId : edgeIds) {
-            HugeEdge edge;
-            EdgeId id = HugeEdge.getIdValue(edgeId, !verifyId);
+        if (edgeIds.length == 1) {
+            EdgeId id = HugeEdge.getIdValue(edgeIds[0], !verifyId);
+
+            boolean tryQueryBackend = true;
             if (id == null) {
-                continue;
+                tryQueryBackend = false;
+                ids = ImmutableList.of();
+            } else {
+                if (id.direction() == Directions.IN) {
+                    id = id.switchDirection();
+                }
+                ids = ImmutableList.of(id);
+            }
+
+            HugeEdge edge = null;
+            if (id != null && edgesUpdated) {
+                if (this.removedEdges.containsKey(id)) {
+                    // The record has been deleted
+                    tryQueryBackend = false;
+                } else if ((edge = this.addedEdges.get(id)) != null ||
+                           (edge = this.updatedEdges.get(id)) != null) {
+                    // Found from local tx
+                    tryQueryBackend = false;
+                    if (edge.expired()) {
+                        edge = null;
+                    } else {
+                        assert edge != null;
+                    }
+                }
             }
-            if (id.direction() == Directions.IN) {
-                id = id.switchDirection();
+
+            if (edge != null) {
+                assert !tryQueryBackend;
+                edges = ImmutableMap.of(edge.id(), edge);
+            } else if (!tryQueryBackend) {
+                assert edge == null;
+                edges = ImmutableMap.of();
+            } else {
+                // Query from backend store
+                IdQuery query = new IdQuery.OneIdQuery(HugeType.EDGE, id);
+                Iterator<HugeEdge> it = this.queryEdgesFromBackend(query);
+                edge = QueryResults.one(it);
+                if (edge == null) {
+                    edges = ImmutableMap.of();
+                } else {
+                    edges = ImmutableMap.of(edge.id(), edge);
+                }
             }
-            if (this.removedEdges.containsKey(id)) {
-                // The record has been deleted
-                continue;
-            } else if ((edge = this.addedEdges.get(id)) != null ||
-                       (edge = this.updatedEdges.get(id)) != null) {
-                if (edge.expired()) {
+        } else {
+            // NOTE: allowed duplicated edges if query by duplicated ids
+            ids = InsertionOrderUtil.newList();
+            edges = new HashMap<>(edgeIds.length);
+
+            IdQuery query = new IdQuery(HugeType.EDGE);
+            for (Object edgeId : edgeIds) {
+                HugeEdge edge;
+                EdgeId id = HugeEdge.getIdValue(edgeId, !verifyId);
+                if (id == null) {
                     continue;
                 }
-                // Found from local tx
-                edges.put(edge.id(), edge);
-            } else {
-                // Prepare to query from backend store
-                query.query(id);
+                if (id.direction() == Directions.IN) {
+                    id = id.switchDirection();
+                }
+
+                boolean foundLocal = false;
+                if (edgesUpdated) {
+                    if (this.removedEdges.containsKey(id)) {
+                        // The record has been deleted
+                        continue;
+                    }
+                    if ((edge = this.addedEdges.get(id)) != null ||
+                        (edge = this.updatedEdges.get(id)) != null) {
+                        if (edge.expired()) {
+                            continue;
+                        }
+                        // Found from local tx
+                        foundLocal = true;
+                        edges.put(edge.id(), edge);
+                    } else {
+                        assert !foundLocal;
+                    }
+                }
+                if (!foundLocal) {
+                    // Prepare to query from backend store
+                    query.query(id);
+                }
+                ids.add(id);
             }
-            ids.add(id);
-        }
 
-        if (!query.empty()) {
-            // Query from backend store
-            if (edges.isEmpty() && query.idsSize() == ids.size()) {
-                /*
-                 * Sort at the lower layer and return directly if there is no
-                 * local vertex and duplicated id.
-                 */
+            if (!query.empty()) {
+                // Query from backend store
+                if (edges.isEmpty() && query.idsSize() == ids.size()) {
+                    /*
+                     * Sort at the lower layer and return directly if there is
+                     * no local vertex and duplicated id.
+                     */
+                    Iterator<HugeEdge> it = this.queryEdgesFromBackend(query);
+                    @SuppressWarnings({ "unchecked", "rawtypes" })
+                    Iterator<Edge> r = (Iterator) it;
+                    return r;
+                }
+
+                query.mustSortByInput(false);
                 Iterator<HugeEdge> it = this.queryEdgesFromBackend(query);
-                @SuppressWarnings({"unchecked", "rawtypes"})
-                Iterator<Edge> r = (Iterator) it;
-                return r;
+                QueryResults.fillMap(it, edges);
             }
-
-            query.mustSortByInput(false);
-            Iterator<HugeEdge> it = this.queryEdgesFromBackend(query);
-            QueryResults.fillMap(it, edges);
         }
 
         return new MapperIterator<>(ids.iterator(), id -> {
@@ -993,6 +1135,7 @@ public class GraphTransaction extends IndexableTransaction {
         });
     }
 
+    @Watched(prefix = "graph")
     public Iterator<Edge> queryEdges() {
         Query q = new Query(HugeType.EDGE);
         return this.queryEdges(q);
@@ -1046,6 +1189,7 @@ public class GraphTransaction extends IndexableTransaction {
         return this.skipOffsetOrStopLimit(r, query);
     }
 
+    @Watched(prefix = "graph")
     protected Iterator<HugeEdge> queryEdgesFromBackend(Query query) {
         assert query.resultType().isEdge();
 
@@ -1543,7 +1687,7 @@ public class GraphTransaction extends IndexableTransaction {
                      * Just query by primary-key(id), ignore other user-props(if exists)
                      * that it will be filtered by queryVertices(Query)
                      */
-                    return new IdQuery(query, id);
+                    return new IdQuery.OneIdQuery(query, id);
                 }
             }
         }