Skip to content

Commit 9391873

Browse files
authored
[feat] Add commits creator simulation (#24)
This new simulation adds a new workload that continuously updates the properties of every table and every view. There is no bound to the maximum number of properties that can be created this way. Each new property results in a new snapshot to be created for that particular entity. This can quickly amount to hundreds of commits entity. This simulation can serve as the basis for table metadata management workloads.
1 parent 8f45b51 commit 9391873

File tree

6 files changed

+201
-1
lines changed

6 files changed

+201
-1
lines changed

benchmarks/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Benchmarks for the Polaris service using Gatling.
2626
- `org.apache.polaris.benchmarks.simulations.CreateTreeDataset`: Creates a test dataset with a specific structure. It is a write-only workload designed to populate the system for subsequent benchmarks.
2727
- `org.apache.polaris.benchmarks.simulations.ReadTreeDataset`: Performs read-only operations to fetch namespaces, tables, and views. Some attributes of the objects are also fetched. This benchmark is intended to be used against a Polaris instance with a pre-existing tree dataset. It has no side effects on the dataset and can be executed multiple times without any issues.
2828
- `org.apache.polaris.benchmarks.simulations.ReadUpdateTreeDataset`: Performs read and update operations against a Polaris instance populated with a test dataset. It is a read/write workload that can be used to test the system's ability to handle concurrent read and update operations. It is not destructive and does not prevent subsequent executions of `ReadTreeDataset` or `ReadUpdateTreeDataset`.
29+
- `org.apache.polaris.benchmarks.simulations.CreateCommits`: Creates table and view commits at configurable rates. This benchmark is useful for testing the system's ability to handle table and view commits and can be used to generate a history of thousands of commits for both tables and views.
2930

3031
## Parameters
3132

@@ -119,7 +120,9 @@ Run benchmarks with your configuration:
119120
./gradlew gatlingRun --simulation org.apache.polaris.benchmarks.simulations.ReadTreeDataset \
120121
-Dconfig.file=./application.conf
121122

122-
123+
# Commits creation
124+
./gradlew gatlingRun --simulation org.apache.polaris.benchmarks.simulations.CreateCommits \
125+
-Dconfig.file=./application.conf
123126
```
124127

125128
A message will show the location of the Gatling report:

benchmarks/src/gatling/resources/benchmark-defaults.conf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,21 @@ dataset.tree {
121121

122122
# Workload configuration
123123
workload {
124+
# Configuration for the CreateCommits simulation
125+
create-commits {
126+
# Number of table commits to create per second
127+
# Default: 10
128+
table-commits-throughput = 10
129+
130+
# Number of view commits to create per second
131+
# Default: 5
132+
view-commits-throughput = 5
133+
134+
# Duration of the simulation in minutes
135+
# Default: 1
136+
duration-in-minutes = 1
137+
}
138+
124139
# Configuration for the ReadTreeDataset simulation
125140
read-tree-dataset {
126141
# Number of table operations to perform simultaneously

benchmarks/src/gatling/scala/org/apache/polaris/benchmarks/parameters/BenchmarkConfig.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,17 @@ object BenchmarkConfig {
3939
)
4040

4141
val workloadParams = {
42+
val ccConfig = workload.getConfig("create-commits")
4243
val rtdConfig = workload.getConfig("read-tree-dataset")
4344
val ctdConfig = workload.getConfig("create-tree-dataset")
4445
val rutdConfig = workload.getConfig("read-update-tree-dataset")
4546

4647
WorkloadParameters(
48+
CreateCommitsParameters(
49+
ccConfig.getInt("table-commits-throughput"),
50+
ccConfig.getInt("view-commits-throughput"),
51+
ccConfig.getInt("duration-in-minutes")
52+
),
4753
ReadTreeDatasetParameters(
4854
rtdConfig.getInt("table-concurrency"),
4955
rtdConfig.getInt("view-concurrency")
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.polaris.benchmarks.parameters
21+
22+
/**
23+
* Case class to hold the parameters for the CreateCommits simulation.
24+
*
25+
* @param tableCommitsThroughput The number of table commits to create per second.
26+
* @param viewCommitsThroughput The number of view commits to create per second.
27+
* @param durationInMinutes The duration of the simulation in minutes.
28+
*/
29+
case class CreateCommitsParameters(
30+
tableCommitsThroughput: Int,
31+
viewCommitsThroughput: Int,
32+
durationInMinutes: Int
33+
) {
34+
require(tableCommitsThroughput >= 0, "Table commits throughput cannot be negative")
35+
require(viewCommitsThroughput >= 0, "View commits throughput cannot be negative")
36+
require(durationInMinutes > 0, "Duration in minutes must be positive")
37+
}

benchmarks/src/gatling/scala/org/apache/polaris/benchmarks/parameters/WorkloadParameters.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.apache.polaris.benchmarks.parameters
2121

2222
case class WorkloadParameters(
23+
createCommits: CreateCommitsParameters,
2324
readTreeDataset: ReadTreeDatasetParameters,
2425
createTreeDataset: CreateTreeDatasetParameters,
2526
readUpdateTreeDataset: ReadUpdateTreeDatasetParameters
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.polaris.benchmarks.simulations
21+
22+
import io.gatling.core.Predef._
23+
import io.gatling.core.structure.ScenarioBuilder
24+
import io.gatling.http.Predef.http
25+
import org.apache.polaris.benchmarks.actions.{
26+
AuthenticationActions,
27+
NamespaceActions,
28+
TableActions,
29+
ViewActions
30+
}
31+
import org.apache.polaris.benchmarks.parameters.BenchmarkConfig.config
32+
import org.apache.polaris.benchmarks.parameters.{
33+
ConnectionParameters,
34+
DatasetParameters,
35+
WorkloadParameters
36+
}
37+
import org.apache.polaris.benchmarks.util.CircularIterator
38+
import org.slf4j.LoggerFactory
39+
40+
import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference}
41+
import scala.concurrent.duration._
42+
43+
class CreateCommits extends Simulation {
44+
private val logger = LoggerFactory.getLogger(getClass)
45+
46+
// --------------------------------------------------------------------------------
47+
// Load parameters
48+
// --------------------------------------------------------------------------------
49+
val cp: ConnectionParameters = config.connectionParameters
50+
val dp: DatasetParameters = config.datasetParameters
51+
val wp: WorkloadParameters = config.workloadParameters
52+
53+
// --------------------------------------------------------------------------------
54+
// Helper values
55+
// --------------------------------------------------------------------------------
56+
private val accessToken: AtomicReference[String] = new AtomicReference()
57+
private val shouldRefreshToken: AtomicBoolean = new AtomicBoolean(true)
58+
59+
private val authActions = AuthenticationActions(cp, accessToken)
60+
private val tableActions = TableActions(dp, wp, accessToken)
61+
private val viewActions = ViewActions(dp, wp, accessToken)
62+
63+
// --------------------------------------------------------------------------------
64+
// Authentication related workloads:
65+
// * Authenticate and store the access token for later use every minute
66+
// * Wait for an OAuth token to be available
67+
// * Stop the token refresh loop
68+
// --------------------------------------------------------------------------------
69+
val continuouslyRefreshOauthToken: ScenarioBuilder =
70+
scenario("Authenticate every minute using the Iceberg REST API")
71+
.asLongAs(_ => shouldRefreshToken.get())(
72+
feed(authActions.feeder())
73+
.exec(authActions.authenticateAndSaveAccessToken)
74+
.pause(1.minute)
75+
)
76+
77+
val waitForAuthentication: ScenarioBuilder =
78+
scenario("Wait for the authentication token to be available")
79+
.asLongAs(_ => accessToken.get() == null)(
80+
pause(1.second)
81+
)
82+
83+
val stopRefreshingToken: ScenarioBuilder =
84+
scenario("Stop refreshing the authentication token")
85+
.exec { session =>
86+
shouldRefreshToken.set(false)
87+
session
88+
}
89+
90+
// --------------------------------------------------------------------------------
91+
// Read and write workloads:
92+
// * Create table commits by updating table properties
93+
// * Read namespaces, tables and views
94+
// --------------------------------------------------------------------------------
95+
val tableUpdateScenario: ScenarioBuilder =
96+
scenario("Create table commits by updating properties")
97+
.exec(authActions.restoreAccessTokenInSession)
98+
.feed(tableActions.propertyUpdateFeeder())
99+
.exec(tableActions.updateTable)
100+
101+
// --------------------------------------------------------------------------------
102+
// Read and write workloads:
103+
// * Create table commits by updating table properties
104+
// * Read namespaces, tables and views
105+
// --------------------------------------------------------------------------------
106+
val viewUpdateScenario: ScenarioBuilder =
107+
scenario("Create view commits by updating properties")
108+
.exec(authActions.restoreAccessTokenInSession)
109+
.feed(viewActions.propertyUpdateFeeder())
110+
.exec(viewActions.updateView)
111+
112+
private val httpProtocol = http
113+
.baseUrl(cp.baseUrl)
114+
.acceptHeader("application/json")
115+
.contentTypeHeader("application/json")
116+
117+
private val tableCommitsThroughput = wp.createCommits.tableCommitsThroughput
118+
private val viewCommitsThroughput = wp.createCommits.viewCommitsThroughput
119+
private val durationInMinutes = wp.createCommits.durationInMinutes
120+
setUp(
121+
continuouslyRefreshOauthToken.inject(atOnceUsers(1)).protocols(httpProtocol),
122+
waitForAuthentication
123+
.inject(atOnceUsers(1))
124+
.andThen(
125+
tableUpdateScenario
126+
.inject(
127+
constantUsersPerSec(tableCommitsThroughput).during(durationInMinutes.minutes)
128+
)
129+
.protocols(httpProtocol),
130+
viewUpdateScenario
131+
.inject(
132+
constantUsersPerSec(viewCommitsThroughput).during(durationInMinutes.minutes)
133+
)
134+
.protocols(httpProtocol)
135+
)
136+
.andThen(stopRefreshingToken.inject(atOnceUsers(1)).protocols(httpProtocol))
137+
)
138+
}

0 commit comments

Comments
 (0)