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();