diff --git a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphOperationCountingTest.java b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphOperationCountingTest.java index 8c5de41dfa..a96aa69db3 100644 --- a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphOperationCountingTest.java +++ b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphOperationCountingTest.java @@ -318,14 +318,14 @@ public void testKCVSAccess1() { v = getV(tx,v); assertCount(2, v.properties()); verifyStoreMetrics(EDGESTORE_NAME, ImmutableMap.of(M_GET_SLICE, 2L)); //1 verify vertex existence, 1 for query - verifyTypeCacheMetrics(3, 4); + verifyTypeCacheMetrics(3, 2); tx.commit(); tx = graph.buildTransaction().groupName(metricsPrefix).start(); v = getV(tx,v); assertCount(2, v.properties()); verifyStoreMetrics(EDGESTORE_NAME, ImmutableMap.of(M_GET_SLICE, 4L)); //1 verify vertex existence, 1 for query - verifyTypeCacheMetrics(3, 4); + verifyTypeCacheMetrics(3, 2); tx.commit(); //Check type index lookup caching @@ -343,7 +343,7 @@ public void testKCVSAccess1() { assertNotNull(v.value("name")); assertCount(1, v.query().direction(Direction.BOTH).edges()); verifyStoreMetrics(EDGESTORE_NAME, ImmutableMap.of(M_GET_SLICE, 11L)); //1 verify vertex existence, 3 for query - verifyTypeCacheMetrics(5, 10); + verifyTypeCacheMetrics(5, 9); tx.commit(); verifyLockingOverwrite(3); diff --git a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java index 19cd0c3e8e..d179bbfe0b 100644 --- a/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java +++ b/janusgraph-backend-testutils/src/main/java/org/janusgraph/graphdb/JanusGraphTest.java @@ -156,6 +156,7 @@ import org.janusgraph.testutil.TestGraphConfigs; import org.janusgraph.util.IDUtils; import org.janusgraph.util.stats.MetricManager; +import org.junit.Assert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -2644,7 +2645,8 @@ public void testPropertyIdAccessInDifferentTransaction() { // access property id in new transaction graph.tx().commit(); - assertEquals(expectedId, p.id()); + Exception exception = Assert.assertThrows(IllegalStateException.class, p::id); + assertEquals(exception.getMessage(), "Any lazy load operation is not supported when transaction is already closed."); } /** @@ -6286,10 +6288,15 @@ protected TraversalMetrics testLimitedBatch(Supplier> trave } private void assertEqualResultWithAndWithoutLimitBatchSize(Supplier> traversal) { + boolean isLazyLoad = ((StandardJanusGraphTx) tx).getConfiguration().isLazyLoadRelations(); clopen(option(USE_MULTIQUERY), true, option(LIMITED_BATCH), true); final List resultLimitedBatch = traversal.get().toList(); + if (isLazyLoad) resultLimitedBatch.forEach(Object::hashCode); + clopen(option(USE_MULTIQUERY), true, option(LIMITED_BATCH), false); final List resultUnimitedBatch = traversal.get().toList(); + if (isLazyLoad) resultUnimitedBatch.forEach(Object::hashCode); + clopen(option(USE_MULTIQUERY), false); final List resultNoMultiQuery = traversal.get().toList(); diff --git a/janusgraph-benchmark/src/main/java/org/janusgraph/LazyLoadBenchmark.java b/janusgraph-benchmark/src/main/java/org/janusgraph/LazyLoadBenchmark.java new file mode 100644 index 0000000000..4553f46c5b --- /dev/null +++ b/janusgraph-benchmark/src/main/java/org/janusgraph/LazyLoadBenchmark.java @@ -0,0 +1,137 @@ +// Copyright 2024 JanusGraph Authors +// +// Licensed 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.janusgraph; + +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.janusgraph.core.TransactionBuilder; +import org.janusgraph.core.schema.JanusGraphManagement; +import org.janusgraph.diskstorage.configuration.ModifiableConfiguration; +import org.janusgraph.diskstorage.configuration.WriteConfiguration; +import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration; +import org.janusgraph.core.Cardinality; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.JanusGraphFactory; +import org.janusgraph.core.JanusGraphTransaction; +import org.janusgraph.core.PropertyKey; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@BenchmarkMode(Mode.AverageTime) +@Fork(1) +@State(Scope.Benchmark) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class LazyLoadBenchmark { + + @Param({"5000"}) + int verticesAmount; + + @Param({"true", "false"}) + boolean isLazyLoad; + + JanusGraph graph; + + public WriteConfiguration getConfiguration() { + ModifiableConfiguration config = GraphDatabaseConfiguration.buildGraphConfiguration(); + config.set(GraphDatabaseConfiguration.STORAGE_BACKEND,"inmemory"); + return config.getConfiguration(); + } + + @Setup + public void setUp() throws Exception { + graph = JanusGraphFactory.open(getConfiguration()); + int itemsCount = 100; + + JanusGraphManagement mgmt = graph.openManagement(); + PropertyKey idProp = mgmt.makePropertyKey("id").dataType(Integer.class).cardinality(Cardinality.SINGLE).make(); + PropertyKey nameProp = mgmt.makePropertyKey("name").dataType(String.class).cardinality(Cardinality.SINGLE).make(); + PropertyKey detailsProp = mgmt.makePropertyKey("details").dataType(String.class).cardinality(Cardinality.SINGLE).make(); + PropertyKey itemsProp = mgmt.makePropertyKey("items").dataType(String.class).cardinality(Cardinality.LIST).make(); + + mgmt.buildIndex("nameIndex", Vertex.class).addKey(nameProp).buildCompositeIndex(); + + mgmt.commit(); + + for (int i = 0; i < verticesAmount; i++) { + Vertex vertex = graph.addVertex("id", i); + vertex.property("name", "name_test"); + vertex.property("details", "details_" + i); + + for (int j = 0; j < itemsCount; j++) { + vertex.property("items", "item_" + j); + } + } + + graph.tx().commit(); + } + + @TearDown + public void tearDown() { + graph.close(); + } + + @Benchmark + public List getProperties() { + + TransactionBuilder txBuilder = graph.buildTransaction(); + if (isLazyLoad) { + txBuilder = txBuilder.lazyLoadRelations(); + } else { + txBuilder = txBuilder.noLazyLoadRelations(); + } + + JanusGraphTransaction tx = txBuilder.start(); + List properties = tx.traversal() + .V() + .has("name", "name_test") + .properties() + .toList(); + + List itemIds = properties.stream() + .filter(p -> p.key().equals("id")) + .map(Property::value) + .map(id -> (Integer) id) + .collect(Collectors.toList()); + + tx.rollback(); + return itemIds; + } + + public static void main(String[] args) throws RunnerException { + Options options = new OptionsBuilder() + .include(LazyLoadBenchmark.class.getSimpleName()) + .warmupIterations(10) + .measurementIterations(10) + .build(); + new Runner(options).run(); + } + +} diff --git a/janusgraph-core/src/main/java/org/janusgraph/core/TransactionBuilder.java b/janusgraph-core/src/main/java/org/janusgraph/core/TransactionBuilder.java index 6fb041070f..99ec6fd140 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/core/TransactionBuilder.java +++ b/janusgraph-core/src/main/java/org/janusgraph/core/TransactionBuilder.java @@ -159,6 +159,15 @@ public interface TransactionBuilder { */ TransactionBuilder lazyLoadRelations(); + /** + * Do not set lazy-load for all properties and edges. + *

+ * When enabled, it can have a boost on large scale read operations, when only certain type of relations are being read. + * + * @return Object with lazy-load setting. + */ + TransactionBuilder noLazyLoadRelations(); + /** * Sets `has` step strategy mode. *

diff --git a/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/StandardTransactionBuilder.java b/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/StandardTransactionBuilder.java index 18285ee8d5..7e879a9135 100644 --- a/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/StandardTransactionBuilder.java +++ b/janusgraph-core/src/main/java/org/janusgraph/graphdb/transaction/StandardTransactionBuilder.java @@ -88,7 +88,7 @@ public class StandardTransactionBuilder implements TransactionConfiguration, Tra private boolean skipDBCacheRead; - private boolean isLazyLoadRelations; + private boolean isLazyLoadRelations = true; private MultiQueryHasStepStrategyMode hasStepStrategyMode; @@ -242,6 +242,12 @@ public TransactionBuilder lazyLoadRelations() { return this; } + @Override + public TransactionBuilder noLazyLoadRelations() { + this.isLazyLoadRelations = false; + return this; + } + @Override public TransactionBuilder setHasStepStrategyMode(MultiQueryHasStepStrategyMode hasStepStrategyMode) { this.hasStepStrategyMode = hasStepStrategyMode; diff --git a/janusgraph-inmemory/src/test/java/org/janusgraph/graphdb/inmemory/InMemoryLazyLoadGraphTest.java b/janusgraph-inmemory/src/test/java/org/janusgraph/graphdb/inmemory/InMemoryLazyLoadGraphTest.java index b91b2482ba..e31fbced57 100644 --- a/janusgraph-inmemory/src/test/java/org/janusgraph/graphdb/inmemory/InMemoryLazyLoadGraphTest.java +++ b/janusgraph-inmemory/src/test/java/org/janusgraph/graphdb/inmemory/InMemoryLazyLoadGraphTest.java @@ -14,15 +14,9 @@ package org.janusgraph.graphdb.inmemory; -import org.apache.tinkerpop.gremlin.structure.VertexProperty; -import org.janusgraph.core.JanusGraphVertex; import org.janusgraph.diskstorage.configuration.WriteConfiguration; import org.janusgraph.graphdb.configuration.builder.GraphDatabaseConfigurationBuilder; import org.janusgraph.graphdb.database.LazyLoadGraphTest; -import org.junit.jupiter.api.Test; - -import static org.junit.Assert.assertThrows; -import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author Matthias Broecheler (me@matthiasb.com) @@ -36,18 +30,4 @@ public void open(WriteConfiguration config) { tx = graph.buildTransaction().start(); mgmt = graph.openManagement(); } - - @Override @Test - public void testPropertyIdAccessInDifferentTransaction() { - JanusGraphVertex v1 = graph.addVertex(); - Object expectedId = v1.property("name", "foo").id(); - graph.tx().commit(); - - VertexProperty p = getOnlyElement(v1.properties("name")); - - // access property id in new transaction - graph.tx().commit(); - Exception exception = assertThrows(IllegalStateException.class, p::id); - assertEquals(exception.getMessage(), "Any lazy load operation is not supported when transaction is already closed."); - } }