This is an automated email from the ASF dual-hosted git repository.
liuxun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new d753e69316 [#7546] feat(cache): Add JMH E2E Performance Test (#7717)
d753e69316 is described below
commit d753e693162ce1453e34653face3b20438a68c25
Author: Lord of Abyss <[email protected]>
AuthorDate: Mon Jul 21 11:07:17 2025 +0800
[#7546] feat(cache): Add JMH E2E Performance Test (#7717)
### What changes were proposed in this pull request?
The following JMH benchmarks were executed under the environment below:
- CPU: Apple M2 Pro
- Memory: 16 GB
- OS: macOS Ventura 15.5
- JVM: openjdk version "17.0.15"
- JMH Version: 1.37
- Benchmark Config:
- `@BenchmarkMode`: Throughput & AverageTime
- `@State`: Scope.Thread
- `@Fork`: 1
- `@Warmup:` 5 iterations
- `@Measurement`: 10 iterations
- Backend: H2
JMH configuration as follows:
```gradle
jmh {
jmhVersion.set(libs.versions.jmh.asProvider())
warmupIterations = 5
iterations = 10
fork = 1
threads = 10
resultFormat = "csv"
resultsFile = file("$buildDir/reports/jmh/results.csv")
}
```
> !!!If the CPU number of the machine you tested is less than 10, the
results may not be very accurate.
Add JMH Tests for `EntityStorage`(**enable** cache system), the result
as follows:
```bash
Benchmark (totalCnt)
Mode Cnt Score Error Units
ContainsEntityStorageBenchmark.benchmarkContains 10
thrpt 10 8638224.459 ± 566802.726 ops/s
ContainsEntityStorageBenchmark.benchmarkContains 100
thrpt 9 8253686.221 ± 1024636.576 ops/s
ContainsEntityStorageBenchmark.benchmarkContains 1000
thrpt 9 7905133.978 ± 1657545.830 ops/s
GetEntityStorageBenchmark.benchmarkGet 10
thrpt 10 10514791.439 ± 1278830.339 ops/s
GetEntityStorageBenchmark.benchmarkGet 100
thrpt 9 10266771.834 ± 3241412.140 ops/s
GetEntityStorageBenchmark.benchmarkGet 1000
thrpt 9 9205199.021 ± 45147.883 ops/s
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 10
thrpt 10 9776657.093 ± 2340808.096 ops/s
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 100
thrpt 9 6256556.784 ± 1027312.353 ops/s
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 1000
thrpt 9 1056512.115 ± 33070.525 ops/s
ContainsEntityStorageBenchmark.benchmarkContains 10
avgt 10 ≈ 10⁻⁶ s/op
ContainsEntityStorageBenchmark.benchmarkContains 100
avgt 9 ≈ 10⁻⁶ s/op
ContainsEntityStorageBenchmark.benchmarkContains 1000
avgt 9 ≈ 10⁻⁶ s/op
GetEntityStorageBenchmark.benchmarkGet 10
avgt 10 ≈ 10⁻⁶ s/op
GetEntityStorageBenchmark.benchmarkGet 100
avgt 9 ≈ 10⁻⁶ s/op
GetEntityStorageBenchmark.benchmarkGet 1000
avgt 9 ≈ 10⁻⁶ s/op
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 10
avgt 10 ≈ 10⁻⁶ s/op
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 100
avgt 9 ≈ 10⁻⁶ s/op
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 1000
avgt 9 ≈ 10⁻⁵ s/op
```
Add JMH Tests for `EntityStorage`(**disable** cache system), the result
as follows:
```bash
Benchmark (totalCnt)
Mode Cnt Score Error Units
ContainsEntityStorageBenchmark.benchmarkContains 10
thrpt 10 89990.430 ± 6330.841 ops/s
ContainsEntityStorageBenchmark.benchmarkContains 100
thrpt 10 88707.494 ± 11850.585 ops/s
ContainsEntityStorageBenchmark.benchmarkContains 1000
thrpt 10 86073.612 ± 16018.779 ops/s
GetEntityStorageBenchmark.benchmarkGet 10
thrpt 10 91341.115 ± 4857.600 ops/s
GetEntityStorageBenchmark.benchmarkGet 100
thrpt 10 89763.528 ± 5004.285 ops/s
GetEntityStorageBenchmark.benchmarkGet 1000
thrpt 10 86559.595 ± 8223.997 ops/s
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 10
thrpt 10 44403.008 ± 2316.614 ops/s
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 100
thrpt 10 45409.150 ± 1077.432 ops/s
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 1000
thrpt 10 43654.250 ± 1195.958 ops/s
ContainsEntityStorageBenchmark.benchmarkContains 10
avgt 10 ≈ 10⁻⁴ s/op
ContainsEntityStorageBenchmark.benchmarkContains 100
avgt 10 ≈ 10⁻⁴ s/op
ContainsEntityStorageBenchmark.benchmarkContains 1000
avgt 10 ≈ 10⁻⁴ s/op
GetEntityStorageBenchmark.benchmarkGet 10
avgt 10 ≈ 10⁻⁴ s/op
GetEntityStorageBenchmark.benchmarkGet 100
avgt 10 ≈ 10⁻⁴ s/op
GetEntityStorageBenchmark.benchmarkGet 1000
avgt 10 ≈ 10⁻⁴ s/op
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 10
avgt 10 ≈ 10⁻⁴ s/op
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 100
avgt 10 ≈ 10⁻⁴ s/op
ListRelationEntityStorageBenchmark.benchmarkGetWithRelation 1000
avgt 10 ≈ 10⁻⁴ s/op
```
Also, I've added a mixed benchmark to simulate more realistic workloads,
consisting of:
- 60% get entity operations
- 30% listRelation operations
- 10% contains checks
Here are the benchmark results with caching **enabled**:
```bash
Benchmark (totalCnt) Mode
Cnt Score Error Units
MixEntityStorageBenchmark.ops 10 thrpt
10 16314701.666 ± 2321546.251 ops/s
MixEntityStorageBenchmark.ops:benchmarkContains 10 thrpt
10 11524726.896 ± 1375673.356 ops/s
MixEntityStorageBenchmark.ops:benchmarkGet 10 thrpt
10 4381654.718 ± 840550.376 ops/s
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 10 thrpt
10 408320.052 ± 111527.772 ops/s
MixEntityStorageBenchmark.ops 100 thrpt
10 15701411.738 ± 963344.095 ops/s
MixEntityStorageBenchmark.ops:benchmarkContains 100 thrpt
10 12149946.906 ± 716587.395 ops/s
MixEntityStorageBenchmark.ops:benchmarkGet 100 thrpt
10 3372738.319 ± 231818.443 ops/s
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 100 thrpt
10 178726.513 ± 22041.810 ops/s
MixEntityStorageBenchmark.ops 1000 thrpt
10 12649753.420 ± 455084.479 ops/s
MixEntityStorageBenchmark.ops:benchmarkContains 1000 thrpt
10 9676336.734 ± 355157.774 ops/s
MixEntityStorageBenchmark.ops:benchmarkGet 1000 thrpt
10 2916187.967 ± 103215.487 ops/s
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 1000 thrpt
10 57228.719 ± 6088.712 ops/s
MixEntityStorageBenchmark.ops 10 avgt
10 ≈ 10⁻⁶ s/op
MixEntityStorageBenchmark.ops:benchmarkContains 10 avgt
10 ≈ 10⁻⁷ s/op
MixEntityStorageBenchmark.ops:benchmarkGet 10 avgt
10 ≈ 10⁻⁶ s/op
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 10 avgt
10 ≈ 10⁻⁵ s/op
MixEntityStorageBenchmark.ops 100 avgt
10 ≈ 10⁻⁵ s/op
MixEntityStorageBenchmark.ops:benchmarkContains 100 avgt
10 ≈ 10⁻⁷ s/op
MixEntityStorageBenchmark.ops:benchmarkGet 100 avgt
10 ≈ 10⁻⁶ s/op
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 100 avgt
10 ≈ 10⁻⁵ s/op
MixEntityStorageBenchmark.ops 1000 avgt
10 ≈ 10⁻⁵ s/op
MixEntityStorageBenchmark.ops:benchmarkContains 1000 avgt
10 ≈ 10⁻⁷ s/op
MixEntityStorageBenchmark.ops:benchmarkGet 1000 avgt
10 ≈ 10⁻⁶ s/op
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 1000 avgt
10 ≈ 10⁻⁴ s/op
```
Here are the benchmark results with caching **disabled**:
```bash
Benchmark (totalCnt) Mode
Cnt Score Error Units
MixEntityStorageBenchmark.ops 10 thrpt
10 44856.068 ± 2272.854 ops/s
MixEntityStorageBenchmark.ops:benchmarkContains 10 thrpt
10 23405.339 ± 1167.919 ops/s
MixEntityStorageBenchmark.ops:benchmarkGet 10 thrpt
10 18215.276 ± 2410.224 ops/s
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 10 thrpt
10 3235.453 ± 1927.851 ops/s
MixEntityStorageBenchmark.ops 100 thrpt
10 34786.730 ± 14113.541 ops/s
MixEntityStorageBenchmark.ops:benchmarkContains 100 thrpt
10 17616.938 ± 6843.631 ops/s
MixEntityStorageBenchmark.ops:benchmarkGet 100 thrpt
10 16811.959 ± 7305.477 ops/s
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 100 thrpt
10 357.833 ± 268.326 ops/s
MixEntityStorageBenchmark.ops 1000 thrpt
10 40777.915 ± 5099.553 ops/s
MixEntityStorageBenchmark.ops:benchmarkContains 1000 thrpt
10 20994.004 ± 2557.324 ops/s
MixEntityStorageBenchmark.ops:benchmarkGet 1000 thrpt
10 19771.629 ± 2611.305 ops/s
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 1000 thrpt
10 12.282 ± 6.073 ops/s
MixEntityStorageBenchmark.ops 10 avgt
10 0.001 ± 0.001 s/op
MixEntityStorageBenchmark.ops:benchmarkContains 10 avgt
10 ≈ 10⁻⁴ s/op
MixEntityStorageBenchmark.ops:benchmarkGet 10 avgt
10 ≈ 10⁻³ s/op
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 10 avgt
10 0.001 ± 0.001 s/op
MixEntityStorageBenchmark.ops 100 avgt
10 0.114 ± 0.157 s/op
MixEntityStorageBenchmark.ops:benchmarkContains 100 avgt
10 ≈ 10⁻⁴ s/op
MixEntityStorageBenchmark.ops:benchmarkGet 100 avgt
10 ≈ 10⁻³ s/op
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 100 avgt
10 0.379 ± 0.524 s/op
MixEntityStorageBenchmark.ops 1000 avgt
10 0.230 ± 0.143 s/op
MixEntityStorageBenchmark.ops:benchmarkContains 1000 avgt
10 ≈ 10⁻⁴ s/op
MixEntityStorageBenchmark.ops:benchmarkGet 1000 avgt
10 ≈ 10⁻⁴ s/op
MixEntityStorageBenchmark.ops:benchmarkGetWithRelation 1000 avgt
10 0.765 ± 0.478 s/op
```
### Why are the changes needed?
Fix: #7546
### Does this PR introduce _any_ user-facing change?
no
### How was this patch tested?
local test.
---
core/build.gradle.kts | 2 +-
.../apache/gravitino/cache/BenchmarkHelper.java | 26 ++
.../cache/it/AbstractEntityStorageBenchmark.java | 392 +++++++++++++++++++++
.../cache/it/ContainsEntityStorageBenchmark.java | 69 ++++
.../cache/it/GetEntityStorageBenchmark.java | 79 +++++
.../it/ListRelationEntityStorageBenchmark.java | 88 +++++
.../cache/it/MixEntityStorageBenchmark.java | 155 ++++++++
7 files changed, 810 insertions(+), 1 deletion(-)
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
index 65129aab15..44b3319964 100644
--- a/core/build.gradle.kts
+++ b/core/build.gradle.kts
@@ -105,7 +105,7 @@ jmh {
warmupIterations = 5
iterations = 10
fork = 1
- threads = 4
+ threads = 10
resultFormat = "csv"
resultsFile = file("$buildDir/reports/jmh/results.csv")
}
diff --git a/core/src/jmh/java/org/apache/gravitino/cache/BenchmarkHelper.java
b/core/src/jmh/java/org/apache/gravitino/cache/BenchmarkHelper.java
index b8e3f1815c..7d80a36c47 100644
--- a/core/src/jmh/java/org/apache/gravitino/cache/BenchmarkHelper.java
+++ b/core/src/jmh/java/org/apache/gravitino/cache/BenchmarkHelper.java
@@ -19,6 +19,7 @@
package org.apache.gravitino.cache;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import java.util.ArrayList;
@@ -27,6 +28,7 @@ import java.util.Map;
import java.util.Random;
import org.apache.gravitino.Entity;
import org.apache.gravitino.HasIdentifier;
+import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.meta.ModelEntity;
import org.apache.gravitino.meta.RoleEntity;
import org.apache.gravitino.utils.TestUtil;
@@ -92,6 +94,30 @@ public class BenchmarkHelper {
return keys.get(random.nextInt(keys.size()));
}
+ /**
+ * Returns the {@link NameIdentifier} of the entity based on its type.
+ *
+ * @param entity The {@link Entity} instance.
+ * @return The {@link NameIdentifier} of the entity
+ */
+ public static NameIdentifier getIdentFromEntity(Entity entity) {
+ validateEntityHasIdentifier(entity);
+ HasIdentifier hasIdentifier = (HasIdentifier) entity;
+
+ return hasIdentifier.nameIdentifier();
+ }
+
+ /**
+ * Checks if the entity is of type {@link HasIdentifier}.
+ *
+ * @param entity The {@link Entity} instance to check.
+ */
+ public static void validateEntityHasIdentifier(Entity entity) {
+ Preconditions.checkArgument(entity != null, "Entity cannot be null");
+ Preconditions.checkArgument(
+ entity instanceof HasIdentifier, "Unsupported EntityType: " +
entity.type());
+ }
+
private static List<Entity> getUserList(RoleEntity roleEntity, int userCnt) {
List<Entity> userList = new ArrayList<>(userCnt);
List<Long> roleIds = ImmutableList.of(roleEntity.id());
diff --git
a/core/src/jmh/java/org/apache/gravitino/cache/it/AbstractEntityStorageBenchmark.java
b/core/src/jmh/java/org/apache/gravitino/cache/it/AbstractEntityStorageBenchmark.java
new file mode 100644
index 0000000000..a7f17709e7
--- /dev/null
+++
b/core/src/jmh/java/org/apache/gravitino/cache/it/AbstractEntityStorageBenchmark.java
@@ -0,0 +1,392 @@
+/*
+ * 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.gravitino.cache.it;
+
+import static org.apache.gravitino.Configs.DEFAULT_ENTITY_RELATIONAL_STORE;
+import static
org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_DRIVER;
+import static
org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_MAX_CONNECTIONS;
+import static
org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_PASSWORD;
+import static org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_PATH;
+import static org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_URL;
+import static org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_USER;
+import static
org.apache.gravitino.Configs.ENTITY_RELATIONAL_JDBC_BACKEND_WAIT_MILLISECONDS;
+import static org.apache.gravitino.Configs.ENTITY_RELATIONAL_STORE;
+import static org.apache.gravitino.Configs.ENTITY_STORE;
+import static org.apache.gravitino.Configs.RELATIONAL_ENTITY_STORE;
+import static org.apache.gravitino.Configs.STORE_DELETE_AFTER_TIME;
+import static org.apache.gravitino.Configs.VERSION_RETENTION_COUNT;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.Config;
+import org.apache.gravitino.Configs;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityStore;
+import org.apache.gravitino.EntityStoreFactory;
+import org.apache.gravitino.HasIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.SupportsRelationOperations;
+import org.apache.gravitino.meta.BaseMetalake;
+import org.apache.gravitino.meta.CatalogEntity;
+import org.apache.gravitino.meta.ModelEntity;
+import org.apache.gravitino.meta.RoleEntity;
+import org.apache.gravitino.meta.SchemaEntity;
+import org.apache.gravitino.meta.SchemaVersion;
+import org.apache.gravitino.meta.TopicEntity;
+import org.apache.gravitino.meta.UserEntity;
+import org.apache.gravitino.storage.RandomIdGenerator;
+import org.apache.gravitino.storage.relational.RelationalEntityStore;
+import org.apache.gravitino.storage.relational.converters.H2ExceptionConverter;
+import
org.apache.gravitino.storage.relational.converters.SQLExceptionConverterFactory;
+import org.apache.gravitino.utils.RandomNameUtils;
+import org.apache.gravitino.utils.TestUtil;
+import org.mockito.Mockito;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Level;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
+
+/**
+ * Benchmark base class for evaluating {@code EntityStorage} performance using
JMH.
+ *
+ * <p>This abstract class provides a reusable setup for benchmarking
relational entity store
+ * implementations. It uses an embedded H2 backend and generates randomized
metadata entities (e.g.,
+ * models, users, topics) to simulate realistic usage patterns. Subclasses
should implement specific
+ * {@code @Benchmark} methods to test operations such as {@code put}, {@code
get}, {@code contains},
+ * and {@code insertRelation}.
+ *
+ * <p>Benchmark operations include:
+ *
+ * <ul>
+ * <li>{@code benchmarkContains} — test for entity existence
+ * <li>{@code benchmarkGet} — test for entity retrieval
+ * <li>{@code benchmarkGetWithRelation} — test for listing entities with
relations
+ * </ul>
+ *
+ * <p>Environment:
+ *
+ * <ul>
+ * <li>CPU: Apple M2 Pro
+ * <li>Memory: 16 GB
+ * <li>OS: macOS Ventura 15.5
+ * <li>JVM: OpenJDK 17.0.15
+ * <li>JMH: 1.37
+ * <li>Backend: H2
+ * </ul>
+ *
+ * <p>Benchmark configuration:
+ *
+ * <ul>
+ * <li>{@code @BenchmarkMode}: Throughput and AverageTime
+ * <li>{@code @State}: Scope.Thread
+ * <li>{@code @Fork}: 1
+ * <li>{@code @Warmup}: 5 iterations
+ * <li>{@code @Measurement}: 10 iterations
+ * <li>Gradle config:
+ * <pre>{@code
+ * jmh {
+ * warmupIterations = 5
+ * iterations = 10
+ * fork = 1
+ * threads = 10
+ * resultFormat = "csv"
+ * resultsFile = file("$buildDir/reports/jmh/results.csv")
+ * }
+ *
+ * }</pre>
+ * </ul>
+ *
+ * <p>Two cache modes are benchmarked:
+ *
+ * <ol>
+ * <li><strong>With cache enabled</strong>: throughput up to ~10⁶ ops/sec
+ * <li><strong>With cache disabled</strong>: throughput around ~10⁴ ops/sec
+ * </ol>
+ *
+ * <p>A mixed benchmark is also included to simulate real workloads,
consisting of:
+ *
+ * <ul>
+ * <li>60% {@code get}
+ * <li>30% {@code listRelation}
+ * <li>10% {@code contains}
+ * </ul>
+ *
+ * <p><strong>Note:</strong> If your machine has fewer than 10 CPU cores,
benchmark results may
+ * vary.
+ *
+ * <p>Related PR: https://github.com/apache/gravitino/issues/7546
+ */
+@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
+@OutputTimeUnit(TimeUnit.SECONDS)
+@State(Scope.Thread)
+public class AbstractEntityStorageBenchmark<E extends Entity & HasIdentifier> {
+ protected static final Random random = ThreadLocalRandom.current();
+ private static final Logger LOG =
+ LoggerFactory.getLogger(AbstractEntityStorageBenchmark.class.getName());
+ private static final String JDBC_STORE_PATH =
+ "/tmp/gravitino_jdbc_entityStore_benchmark_" +
UUID.randomUUID().toString().replace("-", "");
+ private static final String DB_DIR = JDBC_STORE_PATH + "/testdb";
+ private static final String H2_FILE = DB_DIR + ".mv.db";
+ private static final String BENCHMARK_METALAKE_NAME = "benchmark_metalake";
+ private static final String BENCHMARK_CATALOG_NAME = "benchmark_catalog";
+ private static final String BENCHMARK_SCHEMA_NAME = "benchmark_schema";
+ private static final boolean CACHE_ENABLED = true;
+ private static RandomIdGenerator generator = new RandomIdGenerator();
+ protected List<ModelEntity> entities;
+ protected Map<UserEntity, Entity> entitiesWithRelations;
+ protected EntityStore store;
+ protected BaseMetalake metalake;
+ protected CatalogEntity catalog;
+ protected SchemaEntity schema;
+
+ @Param({"10", "100", "1000"})
+ public int totalCnt;
+
+ @Setup(Level.Trial)
+ public final void init() throws IOException {
+ initStore();
+ initParentEntities();
+ initBenchmarkEntities();
+ }
+
+ @TearDown(Level.Trial)
+ public void destroy() throws IOException {
+ try {
+ if (store != null) {
+ store.close();
+ }
+ } catch (Exception e) {
+ LOG.warn("WARNING: Failed to close store during benchmark teardown: " +
e.getMessage());
+ }
+ File dir = new File(DB_DIR);
+ if (dir.exists()) {
+ dir.delete();
+ }
+
+ FileUtils.deleteQuietly(new File(H2_FILE));
+ }
+
+ /**
+ * Get test entities.
+ *
+ * @param entityCnt The number of entity to create.
+ * @return The list of test entities.
+ */
+ protected List<ModelEntity> getEntities(int entityCnt) {
+ List<ModelEntity> entities = new ArrayList<>(entityCnt);
+ for (int i = 0; i < entityCnt; i++) {
+ entities.add(
+ TestUtil.getTestModelEntity(
+ generator.nextId(),
+ RandomNameUtils.genRandomName("model"),
+ Namespace.of(
+ BENCHMARK_METALAKE_NAME, BENCHMARK_CATALOG_NAME,
BENCHMARK_SCHEMA_NAME)));
+ }
+
+ return entities;
+ }
+
+ /**
+ * Get relation entities.
+ *
+ * @param entityCnt The number of entities to create.
+ * @return The map of relation entities.
+ */
+ protected Map<UserEntity, Entity> getRelationEntities(int entityCnt) {
+ Map<UserEntity, Entity> relationEntities = Maps.newHashMap();
+ for (int i = 0; i < entityCnt; i++) {
+ RoleEntity testRoleEntity =
+ TestUtil.getTestRoleEntity(
+ generator.nextId(), RandomNameUtils.genRandomName("role"),
BENCHMARK_METALAKE_NAME);
+ List<Long> roleIds = ImmutableList.of(testRoleEntity.id());
+ UserEntity testUserEntity =
+ TestUtil.getTestUserEntity(
+ generator.nextId(),
+ RandomNameUtils.genRandomName("user"),
+ BENCHMARK_METALAKE_NAME,
+ roleIds);
+ TopicEntity testTopicEntity =
+ TestUtil.getTestTopicEntity(
+ generator.nextId(),
+ RandomNameUtils.genRandomName("topic"),
+ Namespace.of(BENCHMARK_METALAKE_NAME, BENCHMARK_CATALOG_NAME,
BENCHMARK_SCHEMA_NAME),
+ "");
+
+ relationEntities.put(testUserEntity, testTopicEntity);
+ }
+
+ return relationEntities;
+ }
+
+ /**
+ * Validate entity from store.
+ *
+ * @param entity The target entity to validate.
+ * @param entityFromStore The entity from store.
+ * @return The validated entity. if the entity is not equal, it will throw
an exception.
+ */
+ protected Entity validateEntity(E entity, E entityFromStore) {
+ if (entity.equals(entityFromStore)) {
+ return entityFromStore;
+ }
+
+ throw new RuntimeException("Entity not equal");
+ }
+
+ /**
+ * Init entity store.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ protected void initStore() throws IOException {
+ File dir = new File(DB_DIR);
+ if (dir.exists()) {
+ FileUtils.deleteQuietly(dir);
+ }
+ dir.mkdirs();
+
+ Config config = mock(Config.class);
+ Mockito.when(config.get(ENTITY_STORE)).thenReturn(RELATIONAL_ENTITY_STORE);
+
Mockito.when(config.get(ENTITY_RELATIONAL_STORE)).thenReturn(DEFAULT_ENTITY_RELATIONAL_STORE);
+
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_PATH)).thenReturn(DB_DIR);
+
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_MAX_CONNECTIONS)).thenReturn(100);
+
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_WAIT_MILLISECONDS)).thenReturn(1000L);
+ Mockito.when(config.get(STORE_DELETE_AFTER_TIME)).thenReturn(20 * 60 *
1000L);
+ Mockito.when(config.get(VERSION_RETENTION_COUNT)).thenReturn(1L);
+ // Fix cache config for test
+ Mockito.when(config.get(Configs.CACHE_ENABLED)).thenReturn(CACHE_ENABLED);
+ Mockito.when(config.get(Configs.CACHE_MAX_ENTRIES)).thenReturn(10_000);
+
Mockito.when(config.get(Configs.CACHE_EXPIRATION_TIME)).thenReturn(3_600_000L);
+ Mockito.when(config.get(Configs.CACHE_WEIGHER_ENABLED)).thenReturn(true);
+ Mockito.when(config.get(Configs.CACHE_STATS_ENABLED)).thenReturn(false);
+
Mockito.when(config.get(Configs.CACHE_IMPLEMENTATION)).thenReturn("caffeine");
+
+ try {
+ Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_URL))
+ .thenReturn(
+ String.format(
+
"jdbc:h2:%s;DB_CLOSE_DELAY=-1;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE",
DB_DIR));
+
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_USER)).thenReturn("gravitino");
+
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_PASSWORD)).thenReturn("gravitino");
+
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_DRIVER)).thenReturn("org.h2.Driver");
+
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_MAX_CONNECTIONS)).thenReturn(100);
+
Mockito.when(config.get(ENTITY_RELATIONAL_JDBC_BACKEND_WAIT_MILLISECONDS)).thenReturn(1000L);
+
+ FieldUtils.writeStaticField(
+ SQLExceptionConverterFactory.class, "converter", new
H2ExceptionConverter(), true);
+
+ } catch (Exception e) {
+ LOG.error("Failed to init entity store", e);
+ throw new RuntimeException(e);
+ }
+
+ store = EntityStoreFactory.createEntityStore(config);
+ store.initialize(config);
+ }
+
+ /**
+ * Init benchmark entities with relations.
+ *
+ * @throws IOException if an I/O error occurs.
+ */
+ protected void initBenchmarkEntities() throws IOException {
+ this.entities = getEntities(totalCnt);
+ this.entitiesWithRelations = getRelationEntities(totalCnt);
+
+ for (ModelEntity entity : entities) {
+ store.put(entity);
+ }
+
+ for (Map.Entry<UserEntity, Entity> entry :
entitiesWithRelations.entrySet()) {
+ UserEntity user = entry.getKey();
+ TopicEntity topic = (TopicEntity) entry.getValue();
+
+ store.put(user, true);
+ store.put(topic, true);
+
+ ((RelationalEntityStore) store)
+ .insertRelation(
+ SupportsRelationOperations.Type.OWNER_REL,
+ topic.nameIdentifier(),
+ Entity.EntityType.TOPIC,
+ user.nameIdentifier(),
+ Entity.EntityType.USER,
+ true);
+ }
+ }
+
+ private void initParentEntities() throws IOException {
+ metalake =
+ BaseMetalake.builder()
+ .withId(generator.nextId())
+ .withName(BENCHMARK_METALAKE_NAME)
+ .withComment("benchmark metalake")
+ .withProperties(ImmutableMap.of())
+ .withVersion(SchemaVersion.V_0_1)
+ .withAuditInfo(TestUtil.getTestAuditInfo())
+ .build();
+ store.put(metalake, true);
+
+ catalog =
+ CatalogEntity.builder()
+ .withId(generator.nextId())
+ .withName(BENCHMARK_CATALOG_NAME)
+ .withComment("benchmark catalog")
+ .withProperties(ImmutableMap.of())
+ .withNamespace(Namespace.of(BENCHMARK_METALAKE_NAME))
+ .withAuditInfo(TestUtil.getTestAuditInfo())
+ .withType(Catalog.Type.RELATIONAL)
+ .withProvider("hive")
+ .build();
+ store.put(catalog, true);
+
+ schema =
+ SchemaEntity.builder()
+ .withId(generator.nextId())
+ .withName(BENCHMARK_SCHEMA_NAME)
+ .withComment("benchmark schema")
+ .withProperties(ImmutableMap.of())
+ .withNamespace(Namespace.of(BENCHMARK_METALAKE_NAME,
BENCHMARK_CATALOG_NAME))
+ .withAuditInfo(TestUtil.getTestAuditInfo())
+ .build();
+ store.put(schema, true);
+ }
+}
diff --git
a/core/src/jmh/java/org/apache/gravitino/cache/it/ContainsEntityStorageBenchmark.java
b/core/src/jmh/java/org/apache/gravitino/cache/it/ContainsEntityStorageBenchmark.java
new file mode 100644
index 0000000000..e32f096c38
--- /dev/null
+++
b/core/src/jmh/java/org/apache/gravitino/cache/it/ContainsEntityStorageBenchmark.java
@@ -0,0 +1,69 @@
+/*
+ * 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.gravitino.cache.it;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityStore;
+import org.apache.gravitino.cache.BenchmarkHelper;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+/**
+ * Benchmark for testing the performance of the {@code EntityStore.exists()}
method.
+ *
+ * <p>This benchmark randomly selects an entity from a preloaded list and
checks whether it exists
+ * in the {@link EntityStore}. It ensures correctness by throwing an exception
if the entity is not
+ * found.
+ *
+ * <p>Benchmark results will reflect the performance of metadata existence
checks under different
+ * entity counts, as controlled by the {@code @Param totalCnt} field from
{@link
+ * AbstractEntityStorageBenchmark}.
+ *
+ * <p>This class extends {@link AbstractEntityStorageBenchmark}, which handles
store setup,
+ * teardown, and test entity generation.
+ *
+ * @see
org.apache.gravitino.EntityStore#exists(org.apache.gravitino.NameIdentifier,
+ * Entity.EntityType)
+ */
+@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
+@OutputTimeUnit(TimeUnit.SECONDS)
+@State(Scope.Thread)
+public class ContainsEntityStorageBenchmark extends
AbstractEntityStorageBenchmark {
+
+ @Benchmark
+ public boolean benchmarkContains() throws IOException {
+ int idx = random.nextInt(entities.size());
+ Entity sampleEntity = (Entity) entities.get(idx);
+
+ boolean exists =
+ store.exists(BenchmarkHelper.getIdentFromEntity(sampleEntity),
sampleEntity.type());
+ if (!exists) {
+ throw new RuntimeException("Entity not found in store");
+ }
+
+ return exists;
+ }
+}
diff --git
a/core/src/jmh/java/org/apache/gravitino/cache/it/GetEntityStorageBenchmark.java
b/core/src/jmh/java/org/apache/gravitino/cache/it/GetEntityStorageBenchmark.java
new file mode 100644
index 0000000000..65d13dcb3c
--- /dev/null
+++
b/core/src/jmh/java/org/apache/gravitino/cache/it/GetEntityStorageBenchmark.java
@@ -0,0 +1,79 @@
+/*
+ * 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.gravitino.cache.it;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.EntityStore;
+import org.apache.gravitino.HasIdentifier;
+import org.apache.gravitino.cache.BenchmarkHelper;
+import org.apache.gravitino.meta.ModelEntity;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+/**
+ * Benchmark for testing the performance of the {@code EntityStore.get()}
method.
+ *
+ * <p>This benchmark randomly selects a preloaded entity and retrieves it from
the {@link
+ * EntityStore} using its name identifier and type. It then validates the
retrieved entity against
+ * the expected one.
+ *
+ * <p>The test dataset is initialized by {@link
AbstractEntityStorageBenchmark}, and the benchmark
+ * is designed to simulate typical read-access patterns in metadata-intensive
workloads.
+ *
+ * <p>Benchmark results reflect the efficiency and correctness of metadata
retrieval under varying
+ * entity counts.
+ *
+ * @see
org.apache.gravitino.EntityStore#get(org.apache.gravitino.NameIdentifier,
Entity.EntityType,
+ * Class)
+ */
+@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
+@OutputTimeUnit(TimeUnit.SECONDS)
+@State(Scope.Thread)
+public class GetEntityStorageBenchmark<E extends Entity & HasIdentifier>
+ extends AbstractEntityStorageBenchmark {
+
+ /**
+ * Benchmark for getting an entity from the store.
+ *
+ * @return the entity from store.
+ */
+ @Benchmark
+ public Entity benchmarkGet() {
+ int idx = random.nextInt(entities.size());
+ Entity sampleEntity = (Entity) entities.get(idx);
+
+ try {
+ ModelEntity entityFromStore =
+ store.get(
+ BenchmarkHelper.getIdentFromEntity(sampleEntity),
+ sampleEntity.type(),
+ ModelEntity.class);
+ return validateEntity((Entity) entities.get(idx), entityFromStore);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git
a/core/src/jmh/java/org/apache/gravitino/cache/it/ListRelationEntityStorageBenchmark.java
b/core/src/jmh/java/org/apache/gravitino/cache/it/ListRelationEntityStorageBenchmark.java
new file mode 100644
index 0000000000..6f9f9e73ec
--- /dev/null
+++
b/core/src/jmh/java/org/apache/gravitino/cache/it/ListRelationEntityStorageBenchmark.java
@@ -0,0 +1,88 @@
+/*
+ * 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.gravitino.cache.it;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.HasIdentifier;
+import org.apache.gravitino.SupportsRelationOperations;
+import org.apache.gravitino.cache.BenchmarkHelper;
+import org.apache.gravitino.meta.TopicEntity;
+import org.apache.gravitino.meta.UserEntity;
+import org.apache.gravitino.storage.relational.RelationalEntityStore;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+/**
+ * Benchmark for testing the performance of listing entities via relation
operations in a {@link
+ * RelationalEntityStore}.
+ *
+ * <p>This benchmark simulates a common metadata query pattern: retrieving all
related entities
+ * (e.g., users) associated with a given entity (e.g., topic) by a specific
relation type (such as
+ * OWNER_REL).
+ *
+ * <p>The test randomly selects a preloaded {@link UserEntity}–{@link
TopicEntity} pair and invokes
+ * {@code listEntitiesByRelation} to fetch related entities from the store. It
validates that the
+ * result is non-empty to ensure correctness.
+ *
+ * <p>This benchmark reflects the cost and scalability of relation-based
metadata queries under
+ * load.
+ *
+ * @see
RelationalEntityStore#listEntitiesByRelation(SupportsRelationOperations.Type,
+ * org.apache.gravitino.NameIdentifier, Entity.EntityType, boolean)
+ */
+@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
+@OutputTimeUnit(TimeUnit.SECONDS)
+@State(Scope.Thread)
+public class ListRelationEntityStorageBenchmark<E extends Entity &
HasIdentifier>
+ extends AbstractEntityStorageBenchmark {
+
+ /**
+ * Benchmark for list entities by relation operation.
+ *
+ * @return the entity with relation from store.
+ * @throws IOException if there is an error getting the entity from the
store.
+ */
+ @Benchmark
+ public List<E> benchmarkGetWithRelation() throws IOException {
+ UserEntity user = (UserEntity)
BenchmarkHelper.getRandomKey(entitiesWithRelations);
+ TopicEntity topic = (TopicEntity) entitiesWithRelations.get(user);
+
+ List<E> entitiesFromStore =
+ ((RelationalEntityStore) store)
+ .listEntitiesByRelation(
+ SupportsRelationOperations.Type.OWNER_REL,
+ topic.nameIdentifier(),
+ Entity.EntityType.TOPIC,
+ true);
+
+ if (entitiesFromStore.isEmpty()) {
+ throw new RuntimeException("User list size does not match");
+ }
+
+ return entitiesFromStore;
+ }
+}
diff --git
a/core/src/jmh/java/org/apache/gravitino/cache/it/MixEntityStorageBenchmark.java
b/core/src/jmh/java/org/apache/gravitino/cache/it/MixEntityStorageBenchmark.java
new file mode 100644
index 0000000000..1c2e07498b
--- /dev/null
+++
b/core/src/jmh/java/org/apache/gravitino/cache/it/MixEntityStorageBenchmark.java
@@ -0,0 +1,155 @@
+/*
+ * 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.gravitino.cache.it;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.HasIdentifier;
+import org.apache.gravitino.SupportsRelationOperations;
+import org.apache.gravitino.cache.BenchmarkHelper;
+import org.apache.gravitino.meta.ModelEntity;
+import org.apache.gravitino.meta.TopicEntity;
+import org.apache.gravitino.meta.UserEntity;
+import org.apache.gravitino.storage.relational.RelationalEntityStore;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Group;
+import org.openjdk.jmh.annotations.GroupThreads;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+/**
+ * A JMH benchmark class that simulates a mixed workload over the EntityStore
system, combining
+ * multiple core operations in a realistic access ratio.
+ *
+ * <p>This benchmark is designed to reflect a read-heavy, concurrent usage
pattern, which helps
+ * evaluate the performance impact of typical usage scenarios, including:
+ *
+ * <ul>
+ * <li>60% {@code get} operations to retrieve entities by identifier
+ * <li>30% {@code listRelation} operations to query relation-based entities
+ * <li>10% {@code contains} operations to check entity existence
+ * </ul>
+ *
+ * <p>Each benchmark method is annotated with {@code @Group} and {@code
@GroupThreads} to simulate
+ * mixed operation concurrency under shared state.
+ *
+ * <p>The benchmark runs under both {@code Throughput} and {@code AverageTime}
modes, providing a
+ * comprehensive view of system performance in terms of latency and throughput.
+ *
+ * <p>This benchmark can be used to identify performance bottlenecks, cache
efficiency, and
+ * concurrency behavior of the EntityStore implementation, especially when
caching is enabled or
+ * disabled.
+ *
+ * @param <E> the type of entity under test, which must implement both {@link
Entity} and {@link
+ * HasIdentifier}
+ */
+@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
+@OutputTimeUnit(TimeUnit.SECONDS)
+@State(Scope.Group)
+public class MixEntityStorageBenchmark<E extends Entity & HasIdentifier>
+ extends AbstractEntityStorageBenchmark {
+ public static final int GET_ENTITY_RATIO = 6;
+ public static final int CONTAINS_RATIO = 1;
+ public static final int LIST_RELATION_RATIO = 3;
+
+ private final Random random = ThreadLocalRandom.current();
+
+ /**
+ * Test get from store.
+ *
+ * @return The entity.
+ * @throws IOException if an I/O error occurs.
+ */
+ @Benchmark
+ @Group("ops")
+ @GroupThreads(GET_ENTITY_RATIO)
+ public Entity benchmarkGet() {
+ int idx = random.nextInt(entities.size());
+ Entity sampleEntity = (Entity) entities.get(idx);
+
+ try {
+ ModelEntity entityFromStore =
+ store.get(
+ BenchmarkHelper.getIdentFromEntity(sampleEntity),
+ sampleEntity.type(),
+ ModelEntity.class);
+ return validateEntity((Entity) entities.get(idx), entityFromStore);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Test list entities with relation.
+ *
+ * @return The list of entities.
+ * @throws IOException if an I/O error occurs.
+ */
+ @Benchmark
+ @Group("ops")
+ @GroupThreads(LIST_RELATION_RATIO)
+ public List<E> benchmarkGetWithRelation() throws IOException {
+ UserEntity user = (UserEntity)
BenchmarkHelper.getRandomKey(entitiesWithRelations);
+ TopicEntity topic = (TopicEntity) entitiesWithRelations.get(user);
+
+ List<E> entitiesFromStore =
+ ((RelationalEntityStore) store)
+ .listEntitiesByRelation(
+ SupportsRelationOperations.Type.OWNER_REL,
+ topic.nameIdentifier(),
+ Entity.EntityType.TOPIC,
+ true);
+
+ if (entitiesFromStore.isEmpty()) {
+ throw new RuntimeException("User list size does not match");
+ }
+
+ return entitiesFromStore;
+ }
+
+ /**
+ * Test exists in store.
+ *
+ * @return True if the entity exists in the store.
+ * @throws IOException if an I/O error occurs.
+ */
+ @Benchmark
+ @Group("ops")
+ @GroupThreads(CONTAINS_RATIO)
+ public boolean benchmarkContains() throws IOException {
+ int idx = random.nextInt(entities.size());
+ Entity sampleEntity = (Entity) entities.get(idx);
+
+ boolean exists =
+ store.exists(BenchmarkHelper.getIdentFromEntity(sampleEntity),
sampleEntity.type());
+ if (!exists) {
+ throw new RuntimeException("Entity not found in store");
+ }
+
+ return exists;
+ }
+}