You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hugegraph.apache.org by ji...@apache.org on 2026/09/16 15:58:51 UTC

(hugegraph) branch master updated: fix(server): handle page correctly when limit(500 * N) (#3207)

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

imbajin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hugegraph.git


The following commit(s) were added to refs/heads/master by this push:
     new f0056b1d0 fix(server): handle page correctly when limit(500 * N) (#3207)
f0056b1d0 is described below

commit f0056b1d0e93e81cb204344697578d8540956984
Author: Sebastian Gruza <se...@gruzalab.pl>
AuthorDate: Wed Sep 16 17:58:46 2026 +0200

    fix(server): handle page correctly when limit(500 * N) (#3207)
    
    BinaryEntryIterator appends a record to the current entry, then breaks
    when that entry has reached INLINE_BATCH_SIZE (500). The next page restarts
    inclusively from results.position(), which already points at that record,
    so a page limit ending exactly at a batch boundary (500, 1000, ...) returned
    the last element of page k again as the first element of page k+1. Start a
    new entry with the record instead of appending it once the current entry
    holds a full batch.
    
    Affects every query whose result is one backend entry with more than 500
    columns: the edges of one owner vertex and label-index vertex queries, on
    RocksDB and HStore alike (server code).
    
    Co-authored-by: Claude Fable 5.1 <no...@anthropic.com>
---
 .../backend/serializer/BinaryEntryIterator.java    | 24 ++++++++++----
 .../org/apache/hugegraph/core/EdgeCoreTest.java    | 37 ++++++++++++++++++++++
 2 files changed, 54 insertions(+), 7 deletions(-)

diff --git a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/serializer/BinaryEntryIterator.java b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/serializer/BinaryEntryIterator.java
index 0eb22f6d5..109574268 100644
--- a/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/serializer/BinaryEntryIterator.java
+++ b/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/serializer/BinaryEntryIterator.java
@@ -68,23 +68,33 @@ public class BinaryEntryIterator<Elem> extends BackendEntryIterator {
 
         while (this.results.hasNext()) {
             Elem elem = this.results.next();
+            if (this.current != null &&
+                this.sizeOf(this.current) >= INLINE_BATCH_SIZE) {
+                /*
+                 * The current entry already holds a full batch: start the
+                 * next entry with this record instead of appending it.
+                 * results.position() has already advanced to this record and
+                 * a page restarts inclusively from position(), so it must
+                 * never point at a record that has been emitted (a page
+                 * limit ending exactly at a batch boundary would otherwise
+                 * re-emit it as the first record of the next page).
+                 */
+                assert this.next == null;
+                this.next = this.merger.apply(null, elem);
+                break;
+            }
             BackendEntry merged = this.merger.apply(this.current, elem);
             E.checkState(merged != null, "Error when merging entry");
             if (this.current == null) {
                 // The first time to read
                 this.current = merged;
-            } else if (merged == this.current) {
-                // The next entry belongs to the current entry
-                assert this.current != null;
-                if (this.sizeOf(this.current) >= INLINE_BATCH_SIZE) {
-                    break;
-                }
-            } else {
+            } else if (merged != this.current) {
                 // New entry
                 assert this.next == null;
                 this.next = merged;
                 break;
             }
+            // Else the record belongs to the current entry
 
             // When limit exceed, stop fetching
             if (this.reachLimit(this.fetched() - 1)) {
diff --git a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java
index fccb49513..9ea6b0e91 100644
--- a/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java
+++ b/hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/core/EdgeCoreTest.java
@@ -5380,6 +5380,43 @@ public class EdgeCoreTest extends BaseCoreTest {
         Assert.assertEquals(2, count);
     }
 
+    @Test
+    public void testQueryOutEdgesOfVertexInPagingAtBatchBoundary() {
+        HugeGraph graph = graph();
+        Assume.assumeTrue("Not support paging",
+                          storeFeatures().supportsQueryByPage());
+        // More edges than BackendEntryIterator.INLINE_BATCH_SIZE (500)
+        int total = 1200;
+        Vertex louise = graph.addVertex(T.label, "person", "name", "Louise",
+                                        "city", "Beijing", "age", 21);
+        Vertex java1 = graph.addVertex(T.label, "book", "name", "java-1");
+        for (int i = 0; i < total; i++) {
+            louise.addEdge("look", java1, "time", String.format("2017-%04d", i));
+        }
+        graph.tx().commit();
+
+        // A page limit ending exactly at a batch boundary (500, 1000) used to
+        // re-emit the last edge of a page as the first edge of the next page
+        for (int limit : new int[]{400, 500, 600, 1000}) {
+            Set<Object> ids = new HashSet<>();
+            int count = 0;
+            String page = PageInfo.PAGE_NONE;
+            while (page != null) {
+                GraphTraversal<Vertex, Edge> iterator = graph.traversal()
+                                                             .V(louise).outE("look")
+                                                             .has("~page", page)
+                                                             .limit(limit);
+                while (iterator.hasNext()) {
+                    ids.add(iterator.next().id());
+                    count++;
+                }
+                page = TraversalUtil.page(iterator);
+            }
+            Assert.assertEquals("limit " + limit, total, count);
+            Assert.assertEquals("limit " + limit, total, ids.size());
+        }
+    }
+
     @Test
     public void testQueryCount() {
         HugeGraph graph = graph();