imbajin commented on code in PR #3167:
URL: https://github.com/apache/hugegraph/pull/3167#discussion_r3840564888


##########
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/algorithm/ShortestPathTraverser.java:
##########
@@ -184,33 +185,36 @@ public PathSet forward(boolean all) {
             while (this.pathResults.hasNextKey()) {
                 Id source = this.pathResults.nextKey();
 
-                Iterator<Edge> edges = edgesOfVertex(source, this.direction,
-                                                     this.labels, degree);
-                edges = skipSuperNodeIfNeeded(edges, this.degree,
-                                              this.skipDegree);
+                Iterator<Edge> sourceEdges = edgesOfVertex(

Review Comment:
   ⚠️ Important
   
   Blocking: yes. `edgesOfVertex(...)` is called before the `try`. For multiple 
labels, `HugeTraverser` builds an `ExtendableIterator` and opens one backend 
iterator per label; if a later label query throws, earlier closeable HStore 
iterators are already owned by that local aggregator but are lost before this 
`finally` is installed. The same acquisition pattern is used by `backward()`. 
Please make multi-label iterator acquisition close all previously opened 
children on failure (or provide an exception-safe acquisition API) before 
relying on this cleanup block.



##########
hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/traversal/ShortestPathTraverserTest.java:
##########
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.unit.traversal;
+
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.hugegraph.HugeGraph;
+import org.apache.hugegraph.backend.id.EdgeId;
+import org.apache.hugegraph.backend.id.Id;
+import org.apache.hugegraph.backend.id.IdGenerator;
+import org.apache.hugegraph.config.CoreOptions;
+import org.apache.hugegraph.structure.HugeEdge;
+import org.apache.hugegraph.testutil.Assert;
+import org.apache.hugegraph.traversal.algorithm.HugeTraverser.Path;
+import org.apache.hugegraph.traversal.algorithm.ShortestPathTraverser;
+import org.apache.hugegraph.type.define.CollectionType;
+import org.apache.hugegraph.type.define.Directions;
+import org.apache.hugegraph.unit.BaseUnitTest;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class ShortestPathTraverserTest extends BaseUnitTest {
+
+    @Test
+    public void testCloseEdgesWhenPathFoundForward() {
+        Id source = IdGenerator.of(1L);
+        Id target = IdGenerator.of(2L);
+        TrackingIterator edges = edges(edgeTo(target));
+        TestTraverser traverser = new TestTraverser(edges);
+
+        Path path = shortestPath(traverser, source, target, 1, 0L);
+
+        Assert.assertEquals(Arrays.asList(source, target), path.vertices());
+        Assert.assertTrue(edges.closed());
+    }
+
+    @Test
+    public void testCloseEdgesWhenPathFoundBackward() {
+        Id source = IdGenerator.of(1L);
+        Id middle = IdGenerator.of(2L);
+        Id target = IdGenerator.of(3L);
+        TrackingIterator forwardEdges = edges(edgeTo(middle));
+        TrackingIterator backwardEdges = edges(edgeTo(middle));
+        TestTraverser traverser = new TestTraverser(forwardEdges,
+                                                    backwardEdges);
+
+        Path path = shortestPath(traverser, source, target, 2, 0L);
+
+        Assert.assertEquals(Arrays.asList(source, middle, target),
+                            path.vertices());
+        Assert.assertTrue(forwardEdges.closed());
+        Assert.assertTrue(backwardEdges.closed());
+    }
+
+    @Test
+    public void testCloseEdgesWhenCheckingSuperNode() {
+        Id source = IdGenerator.of(1L);
+        Id target = IdGenerator.of(2L);
+        TrackingIterator sourceEdges = edges(edgeTo(target));

Review Comment:
   ⚠️ Important
   
   Blocking: no. This test supplies one source edge while `skipDegree=2`, so 
`skipSuperNodeIfNeeded()` never reaches its threshold branch that returns the 
empty iterator; it also uses a raw `TrackingIterator`, empty labels, and only 
`shortestPath(..., all=false)`. The added tests therefore do not prove cleanup 
for the actual super-node skip, wrapper chain, `allShortestPaths()`, or 
iterator exception paths. Please add threshold, wrapped-backend, 
full-traversal, and throwing-iterator cases with close assertions.



##########
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/algorithm/ShortestPathTraverser.java:
##########
@@ -184,33 +185,36 @@ public PathSet forward(boolean all) {
             while (this.pathResults.hasNextKey()) {
                 Id source = this.pathResults.nextKey();
 
-                Iterator<Edge> edges = edgesOfVertex(source, this.direction,
-                                                     this.labels, degree);
-                edges = skipSuperNodeIfNeeded(edges, this.degree,
-                                              this.skipDegree);
+                Iterator<Edge> sourceEdges = edgesOfVertex(
+                        source, this.direction, this.labels, degree);
+                try {
+                    Iterator<Edge> edges = skipSuperNodeIfNeeded(
+                            sourceEdges, this.degree, this.skipDegree);
 
-                this.vertexCount += 1L;
+                    this.vertexCount += 1L;
 
-                while (edges.hasNext()) {
-                    HugeEdge edge = (HugeEdge) edges.next();
-                    Id target = edge.id().otherVertexId();
+                    while (edges.hasNext()) {
+                        HugeEdge edge = (HugeEdge) edges.next();
+                        Id target = edge.id().otherVertexId();
 
-                    this.edgeResults.addEdge(source, target, edge);
+                        this.edgeResults.addEdge(source, target, edge);
 
-                    PathSet paths = this.pathResults.findPath(target,
-                                                              t -> 
!this.superNode(t,
-                                                                               
    this.direction),
-                                                              all, false);
+                        PathSet paths = this.pathResults.findPath(
+                                target,
+                                t -> !this.superNode(t, this.direction),
+                                all, false);
 
-                    if (paths.isEmpty()) {
-                        continue;
-                    }
-                    results.addAll(paths);
-                    if (!all) {
-                        return paths;
+                        if (paths.isEmpty()) {
+                            continue;
+                        }
+                        results.addAll(paths);
+                        if (!all) {
+                            return paths;
+                        }
                     }
+                } finally {

Review Comment:
   ⚠️ Important
   
   Blocking: no. `CloseableIterator.closeIterator()` wraps an 
`AutoCloseable.close()` failure in a `RuntimeException`. In these `finally` 
blocks, that cleanup exception replaces an exception already thrown by 
`hasNext()`, `next()`, or path matching, so a backend query failure can be 
reported as a close failure; the same applies to the backward and super-node 
cleanup blocks. Please preserve the primary exception and attach a close 
failure as suppressed, propagating it directly only when no primary exception 
exists.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to