From efda2d5c832dba62db1edff37cc8b0cec170a9f3 Mon Sep 17 00:00:00 2001 From: ulofiai Date: Fri, 7 Aug 2026 09:35:56 +0800 Subject: [PATCH 1/2] fix(hstore): invalidate graph registration after clear Signed-off-by: ulofiai --- .../store/hstore/HstoreSessionsImpl.java | 6 ++++ .../store/hstore/HstoreSessionsImplTest.java | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/hugegraph-server/hugegraph-hstore/src/main/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImpl.java b/hugegraph-server/hugegraph-hstore/src/main/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImpl.java index e619aff4fb..625dd100ff 100755 --- a/hugegraph-server/hugegraph-hstore/src/main/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImpl.java +++ b/hugegraph-server/hugegraph-hstore/src/main/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImpl.java @@ -184,9 +184,15 @@ public void clear() { hgStoreClient.getPdClient().delGraph(this.graphName); } catch (PDException ignored) { + } finally { + clearInitializedGraph(this.graphName); } } + private static void clearInitializedGraph(String graphName) { + infoInitializedGraph.remove(graphName); + } + @Override public final Session session() { return (Session) super.getOrNewSession(); diff --git a/hugegraph-server/hugegraph-hstore/src/test/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImplTest.java b/hugegraph-server/hugegraph-hstore/src/test/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImplTest.java index 69f2591e25..5cadbda6e2 100644 --- a/hugegraph-server/hugegraph-hstore/src/test/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImplTest.java +++ b/hugegraph-server/hugegraph-hstore/src/test/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImplTest.java @@ -19,7 +19,10 @@ import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; +import java.util.Set; import org.junit.Assert; import org.junit.Test; @@ -36,4 +39,30 @@ public void testProductionClassDoesNotReferenceTestAssert() throws IOException { "org/apache/hugegraph/testutil/Assert")); } } + + @Test + public void testClearInitializedGraphStateRepeatedly() throws Exception { + Field initializedGraphField = HstoreSessionsImpl.class.getDeclaredField( + "infoInitializedGraph"); + initializedGraphField.setAccessible(true); + @SuppressWarnings("unchecked") + Set initializedGraphs = + (Set) initializedGraphField.get(null); + + Method clearInitializedGraph = + HstoreSessionsImpl.class.getDeclaredMethod( + "clearInitializedGraph", String.class); + clearInitializedGraph.setAccessible(true); + + String graphName = "hugegraph/hstore-clear-test"; + try { + for (int i = 0; i < 50; i++) { + Assert.assertTrue(initializedGraphs.add(graphName)); + clearInitializedGraph.invoke(null, graphName); + Assert.assertFalse(initializedGraphs.contains(graphName)); + } + } finally { + initializedGraphs.remove(graphName); + } + } } From e053609857d1981b471f3ebdb8092852293ff6cd Mon Sep 17 00:00:00 2001 From: ulofiai Date: Fri, 7 Aug 2026 20:39:21 +0800 Subject: [PATCH 2/2] test(hstore): cover public clear and recreate lifecycle Signed-off-by: ulofiai --- .../store/hstore/HstoreSessionsImpl.java | 51 ++++++++++--- .../store/hstore/HstoreSessionsImplTest.java | 73 ++++++++++++++----- 2 files changed, 95 insertions(+), 29 deletions(-) diff --git a/hugegraph-server/hugegraph-hstore/src/main/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImpl.java b/hugegraph-server/hugegraph-hstore/src/main/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImpl.java index 625dd100ff..7a61dd502a 100755 --- a/hugegraph-server/hugegraph-hstore/src/main/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImpl.java +++ b/hugegraph-server/hugegraph-hstore/src/main/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImpl.java @@ -74,6 +74,7 @@ public class HstoreSessionsImpl extends HstoreSessions { private static volatile Boolean initializedNode = Boolean.FALSE; private static volatile PDClient defaultPdClient; private static volatile HgStoreClient hgStoreClient; + private final GraphStoreClient graphStoreClient; private final HugeConfig config; private final HstoreSession session; private final Map tables; @@ -81,11 +82,23 @@ public class HstoreSessionsImpl extends HstoreSessions { private final String graphName; public HstoreSessionsImpl(HugeConfig config, String database, String store) { + this(config, database, store, null); + } + + HstoreSessionsImpl(HugeConfig config, String database, String store, + GraphStoreClient graphStoreClient) { super(config, database, store); this.config = config; this.graphName = database + "/" + store; - this.initStoreNode(config); - this.session = new HstoreSession(this.config, graphName); + if (graphStoreClient == null) { + this.initStoreNode(config); + this.graphStoreClient = new GraphStoreClient(); + } else { + this.graphStoreClient = graphStoreClient; + } + this.session = new HstoreSession( + this.config, this.graphName, + this.graphStoreClient.openSession(this.graphName)); this.tables = new ConcurrentHashMap<>(); this.refCount = new AtomicInteger(1); } @@ -130,10 +143,11 @@ public void open() throws Exception { E.checkArgument(partitionCount > -1, "The value of hstore.partition_count " + "cannot be less than 0."); - defaultPdClient.setGraph(Metapb.Graph.newBuilder() - .setGraphName(this.graphName) - .setPartitionCount(partitionCount) - .build()); + this.graphStoreClient.setGraph( + Metapb.Graph.newBuilder() + .setGraphName(this.graphName) + .setPartitionCount(partitionCount) + .build()); infoInitializedGraph.add(this.graphName); } } @@ -181,7 +195,7 @@ public void truncateTable(String table) { public void clear() { this.session.deleteGraph(); try { - hgStoreClient.getPdClient().delGraph(this.graphName); + this.graphStoreClient.delGraph(this.graphName); } catch (PDException ignored) { } finally { @@ -200,7 +214,8 @@ public final Session session() { @Override protected final Session newSession() { - return new HstoreSession(this.config(), this.graphName); + return new HstoreSession(this.config(), this.graphName, + this.graphStoreClient.openSession(this.graphName)); } @Override @@ -222,6 +237,21 @@ protected synchronized void doClose() { private void checkValid() { } + static class GraphStoreClient { + + HgStoreSession openSession(String graphName) { + return hgStoreClient.openSession(graphName); + } + + void setGraph(Metapb.Graph graph) throws PDException { + defaultPdClient.setGraph(graph); + } + + void delGraph(String graphName) throws PDException { + hgStoreClient.getPdClient().delGraph(graphName); + } + } + private static class ColumnIterator implements BackendColumnIterator, Countable { @@ -424,10 +454,11 @@ private final class HstoreSession extends Session { private final HgStoreSession graph; int changedSize = 0; - public HstoreSession(HugeConfig conf, String graphName) { + public HstoreSession(HugeConfig conf, String graphName, + HgStoreSession graph) { setGraphName(graphName); setConf(conf); - this.graph = hgStoreClient.openSession(graphName); + this.graph = graph; } @Override diff --git a/hugegraph-server/hugegraph-hstore/src/test/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImplTest.java b/hugegraph-server/hugegraph-hstore/src/test/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImplTest.java index 5cadbda6e2..6476e37cc5 100644 --- a/hugegraph-server/hugegraph-hstore/src/test/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImplTest.java +++ b/hugegraph-server/hugegraph-hstore/src/test/java/org/apache/hugegraph/backend/store/hstore/HstoreSessionsImplTest.java @@ -19,11 +19,15 @@ import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.nio.charset.StandardCharsets; -import java.util.Set; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.hugegraph.config.HugeConfig; +import org.apache.hugegraph.pd.common.PDException; +import org.apache.hugegraph.pd.grpc.Metapb; +import org.apache.hugegraph.store.HgStoreSession; import org.junit.Assert; import org.junit.Test; @@ -41,28 +45,59 @@ public void testProductionClassDoesNotReferenceTestAssert() throws IOException { } @Test - public void testClearInitializedGraphStateRepeatedly() throws Exception { - Field initializedGraphField = HstoreSessionsImpl.class.getDeclaredField( - "infoInitializedGraph"); - initializedGraphField.setAccessible(true); - @SuppressWarnings("unchecked") - Set initializedGraphs = - (Set) initializedGraphField.get(null); + public void testClearAndRecreateGraphRepeatedly() throws Exception { + String graphName = "hugegraph/hstore-clear-test"; + AtomicInteger graphRegistrations = new AtomicInteger(); + AtomicInteger storeGraphDeletes = new AtomicInteger(); + AtomicInteger pdGraphDeletes = new AtomicInteger(); + HgStoreSession storeSession = (HgStoreSession) Proxy.newProxyInstance( + HgStoreSession.class.getClassLoader(), + new Class[]{HgStoreSession.class}, + (proxy, method, args) -> { + Assert.assertEquals("deleteGraph", method.getName()); + Assert.assertArrayEquals(new Object[]{graphName}, args); + storeGraphDeletes.incrementAndGet(); + return true; + }); + HstoreSessionsImpl.GraphStoreClient client = + new HstoreSessionsImpl.GraphStoreClient() { + @Override + HgStoreSession openSession(String name) { + Assert.assertEquals(graphName, name); + return storeSession; + } - Method clearInitializedGraph = - HstoreSessionsImpl.class.getDeclaredMethod( - "clearInitializedGraph", String.class); - clearInitializedGraph.setAccessible(true); + @Override + void setGraph(Metapb.Graph graph) { + Assert.assertEquals(graphName, graph.getGraphName()); + graphRegistrations.incrementAndGet(); + } + + @Override + void delGraph(String name) throws PDException { + Assert.assertEquals(graphName, name); + pdGraphDeletes.incrementAndGet(); + throw new PDException(0, "exercise clear() finally block"); + } + }; + HugeConfig config = new HugeConfig(Collections.emptyMap()); + HstoreSessionsImpl sessions = new HstoreSessionsImpl( + config, "hugegraph", "hstore-clear-test", client); - String graphName = "hugegraph/hstore-clear-test"; try { + sessions.open(); + Assert.assertEquals(1, graphRegistrations.get()); + for (int i = 0; i < 50; i++) { - Assert.assertTrue(initializedGraphs.add(graphName)); - clearInitializedGraph.invoke(null, graphName); - Assert.assertFalse(initializedGraphs.contains(graphName)); + sessions.clear(); + sessions.open(); + Assert.assertEquals(i + 2, graphRegistrations.get()); } + + Assert.assertEquals(50, storeGraphDeletes.get()); + Assert.assertEquals(50, pdGraphDeletes.get()); } finally { - initializedGraphs.remove(graphName); + sessions.clear(); } } }