Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ dependency-reduced-pom.xml
### Mac OS ###
.DS_Store

### microbench output ###
.microbench/

# website & docs
website/blog
website/node_modules
Expand Down
64 changes: 64 additions & 0 deletions fluss-microbench/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Fluss Microbench

This module runs repeatable Fluss workloads from YAML scenarios against an independently started cluster. It executes phases in order and writes a JSON result with throughput and latency for each phase.

## Run

```bash
./fluss-microbench/fluss-microbench.sh list
./fluss-microbench/fluss-microbench.sh validate --scenario-file kv-upsert-get
./fluss-dist/target/fluss-*-bin/fluss-*/bin/local-cluster.sh start
./fluss-microbench/fluss-microbench.sh run --scenario-file kv-upsert-get --bootstrap-servers localhost:9123
./fluss-microbench/fluss-microbench.sh run --scenario-file my-scenario.yaml --bootstrap-servers localhost:9123
./fluss-dist/target/fluss-*-bin/fluss-*/bin/local-cluster.sh stop
```

Build the distribution with `./mvnw -pl fluss-dist -am package -DskipTests` before starting the local cluster. The launcher builds the benchmark module on first use. Results are written to `.microbench/runs/<scenario>/<timestamp>/summary.json`; `config-snapshot.yaml` records the input. Set `MICROBENCH_ROOT` to change the output root. Workload parameters are read from YAML.

The client writer buffer defaults to 512 MB and must exceed 256 MB when set in YAML. It uses JVM heap. The launcher defaults to a 2 GB heap and a 1 GB direct-memory limit; set `MICROBENCH_HEAP_SIZE` and `MICROBENCH_DIRECT_MEMORY_SIZE` to size them for the workload and host. Allow additional process memory for JVM metadata, threads, and other native allocations.

The bundled KV scenarios use 128 buckets and at least 32 threads in every phase that writes. They set the Fluss client limits `client.writer.max-inflight-requests-per-bucket: 5` and `client.lookup.max-inflight-requests: 128`. The writer limit applies per bucket and requires idempotent writes, which are enabled by default. The lookup limit applies to unacknowledged lookup requests in the shared client; lookup operations may be batched into requests.

Bundled scenarios: `kv-upsert-get`, `kv-agg-mixed`, and `kv-agg-rbm32`.

## Scenario

```yaml
meta:
name: kv-example
client:
config:
client.writer.buffer.memory-size: "512mb"
client.writer.batch-size: "2mb"
client.writer.max-inflight-requests-per-bucket: "5"
client.lookup.max-inflight-requests: "128"
table:
name: bench_table
columns:
- name: id
type: BIGINT
- name: value
type: STRING
primary-key: [id]
buckets: 128
data:
seed: 42
generators:
id: {type: sequential, start: 0, end: 1000000}
value: {type: random-string, length: 64}
workload:
- phase: write
warmup: 10000
records: 500000
threads: 32
- phase: lookup
duration: 1m
threads: 4
key-range: [0, 1000000]
- phase: mixed
duration: 1m
threads: 32
mix: {write: 50, lookup: 50}
```

Phases support `write`, `lookup`, `scan`, and `mixed`. A phase name may add a suffix, such as `write-bulk`. The `scan` phase accepts `from-offset` and a `filter` map. Aggregation columns use `agg` and `merge-engine: AGGREGATION`; see the bundled aggregation scenarios. Data generators include sequential values, random primitive values, strings, bytes, enums, timestamps, and RoaringBitmap values.
51 changes: 51 additions & 0 deletions fluss-microbench/fluss-microbench.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
#
# 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.
#

# Fluss-Microbench launcher script.
# Usage: ./fluss-microbench.sh run --scenario-file kv-upsert-get --bootstrap-servers localhost:9123

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
FLUSS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
PERF_MODULE="$SCRIPT_DIR"

# Default microbench root to .microbench under the project root
MICROBENCH_ROOT="${MICROBENCH_ROOT:-$FLUSS_ROOT/.microbench}"

CP_CACHE="$PERF_MODULE/target/.classpath-cache"

# Build if needed
if [ ! -d "$PERF_MODULE/target/classes" ]; then
echo "Building fluss-microbench..."
mvn -f "$FLUSS_ROOT/pom.xml" install -pl fluss-microbench -am -DskipTests -Drat.skip=true -q
fi

# Generate classpath cache if missing or stale
if [ ! -f "$CP_CACHE" ] || [ "$PERF_MODULE/pom.xml" -nt "$CP_CACHE" ]; then
mvn -f "$FLUSS_ROOT/pom.xml" dependency:build-classpath \
-pl fluss-microbench -Drat.skip=true -q \
-Dmdep.outputFile="$CP_CACHE"
fi

CP="$PERF_MODULE/target/classes:$(cat "$CP_CACHE")"

exec java -Xmx"${MICROBENCH_HEAP_SIZE:-2g}" \
-XX:MaxDirectMemorySize="${MICROBENCH_DIRECT_MEMORY_SIZE:-1g}" \
--add-opens=java.base/java.nio=ALL-UNNAMED -Dmicrobench.root="$MICROBENCH_ROOT" \
-cp "$CP" org.apache.fluss.microbench.PerfRunner "$@"
99 changes: 99 additions & 0 deletions fluss-microbench/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.fluss</groupId>
<artifactId>fluss</artifactId>
<version>1.1-SNAPSHOT</version>
</parent>

<artifactId>fluss-microbench</artifactId>
<name>Apache Fluss : Microbench</name>
<packaging>jar</packaging>

<dependencies>
<!-- Fluss modules -->
<dependency>
<groupId>org.apache.fluss</groupId>
<artifactId>fluss-client</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Distribution algorithms -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>

<!-- RoaringBitmap -->
<dependency>
<groupId>org.roaringbitmap</groupId>
<artifactId>RoaringBitmap</artifactId>
<version>${roaringbitmap.version}</version>
</dependency>

<!-- YAML parsing -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.0</version>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.apache.fluss.microbench.PerfRunner</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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.fluss.microbench;

import java.nio.file.Path;
import java.nio.file.Paths;

/** Output paths for benchmark runs. */
public final class MicrobenchPaths {

private final Path root;

public MicrobenchPaths(Path root) {
this.root = root;
}

public static MicrobenchPaths fromSystemProperty() {
String configured = System.getProperty("microbench.root");
Path root =
configured == null || configured.isEmpty()
? Paths.get(System.getProperty("user.dir"), ".microbench")
: Paths.get(configured);
return new MicrobenchPaths(root);
}

public Path runDir(String scenario, String timestamp) {
return root.resolve("runs").resolve(scenario).resolve(timestamp);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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.fluss.microbench;

import org.apache.fluss.microbench.config.ScenarioConfig;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/** Loads YAML scenarios from a file or a bundled preset. */
public final class PerfConfig {

private PerfConfig() {}

public static ScenarioConfig load(String presetOrFile) throws IOException {
return ScenarioConfig.parse(loadYaml(presetOrFile));
}

public static String loadYaml(String presetOrFile) throws IOException {
Path file = Paths.get(presetOrFile);
if (Files.isRegularFile(file)) {
return new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
}
String resource = "presets/" + presetOrFile + ".yaml";
try (InputStream in = PerfConfig.class.getClassLoader().getResourceAsStream(resource)) {
if (in != null) {
java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
byte[] buffer = new byte[8192];
int n;
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
return new String(out.toByteArray(), StandardCharsets.UTF_8);
}
}
throw new IOException("Scenario not found: " + presetOrFile);
}
}
Loading
Loading