Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
119f582
Add ES94 TSDB doc values format with pipeline-based numeric encoding
salvatore-campagna Jan 27, 2026
ced58db
fix(tsdb): pad zstd blocks
salvatore-campagna Jan 27, 2026
6408782
fix(tsdb): skip patched-pfor on signed values
salvatore-campagna Jan 27, 2026
208c55d
fix(tsdb): reuse numeric decoder in ES94 producer
salvatore-campagna Jan 27, 2026
fcd5670
test(tsdb): add skip assertions for delta/offset/gcd
salvatore-campagna Jan 27, 2026
9bdb8ec
[CI] Auto commit changes from spotless
Jan 27, 2026
ae6f1dd
chore(tsdb): normalize pipeline javadoc
salvatore-campagna Jan 27, 2026
15ac4b8
Merge branch 'feature/tsdb-pipeline-poc' of github.com:salvatore-camp…
salvatore-campagna Jan 27, 2026
86748f2
chore(tsdb): normalize benchmark javadoc
salvatore-campagna Jan 27, 2026
5429f68
Merge branch 'main' into feature/tsdb-pipeline-poc
salvatore-campagna Feb 5, 2026
5416d16
feat(tsdb): add float/date stages, fused quantization, and pipeline i…
salvatore-campagna Feb 11, 2026
a7296a0
Merge branch 'main' into feature/tsdb-pipeline-poc
salvatore-campagna Feb 11, 2026
6346d4e
fix(tsdb): replace forbidden Math.abs(int) with direct comparison in …
salvatore-campagna Feb 11, 2026
fc7d650
fix(tsdb): fix checkstyle violations and NumberFieldType constructor …
salvatore-campagna Feb 11, 2026
4c7cccc
refactor(tsdb): use thread-local buffers for Zstd stages and remove r…
salvatore-campagna Feb 11, 2026
31f66ce
feat(tsdb): add LZ4 payload stage with fast and high compression modes
salvatore-campagna Feb 11, 2026
8239fc2
fix(tsdb): use lazy-sized thread-local buffers for Zstd and LZ4 stages
salvatore-campagna Feb 11, 2026
233b294
refactor(tsdb): remove Closeable from pipeline codec types
salvatore-campagna Feb 11, 2026
82453fe
fix(tsdb): store FPC selector bits instead of full predictions in met…
salvatore-campagna Feb 12, 2026
3fe6bd1
feat(tsdb): add GCD to timestamp pipeline and scale PatchedPFor excep…
salvatore-campagna Feb 12, 2026
f586666
chore(tsdb): remove bench test utilities from tracked files
salvatore-campagna Feb 12, 2026
8fdb709
[CI] Auto commit changes from spotless
Feb 12, 2026
c59a062
fix(tsdb): restore NumericDataGenerators and init logging for benchmarks
salvatore-campagna Feb 12, 2026
5811547
fix: use default pipeline for timestamp field
salvatore-campagna Feb 12, 2026
346dd5c
Merge branch 'main' into feature/tsdb-pipeline-poc
salvatore-campagna Feb 12, 2026
20fde7d
Merge branch 'main' into feature/tsdb-pipeline-poc
salvatore-campagna Feb 12, 2026
2e901ce
optimize for balanced
salvatore-campagna Feb 13, 2026
aeb8035
feat(tsdb): use FPC for double gauges, float gauges, and double counters
salvatore-campagna Feb 13, 2026
d6f1e5f
feat(tsdb): use ALP for gauges and FPC for counters
salvatore-campagna Feb 14, 2026
f3ba092
feat(tsdb): refactor pipeline resolver and set otel template to storage
salvatore-campagna Feb 14, 2026
c71b061
chore: remove command.sh from benchmarks
salvatore-campagna Feb 15, 2026
d97c19f
feat(tsdb): add chimp stages, gorilla-float, and date-aware pipelines
salvatore-campagna Feb 16, 2026
2ec2e5f
fix(tsdb): search full ALP (e,f) space for lossless mode
salvatore-campagna Feb 16, 2026
d8ef902
feat(tsdb): set otel template to balanced for chimp+fpc experiment
salvatore-campagna Feb 16, 2026
494920b
Merge branch 'main' into feature/tsdb-pipeline-poc
salvatore-campagna Feb 16, 2026
ca9be59
[CI] Auto commit changes from spotless
Feb 16, 2026
6856237
refactor(tsdb): extract shared QuantizeUtils from ALP quantize methods
salvatore-campagna Feb 18, 2026
01e013c
feat(tsdb): add quantization support to FPC and wire chimp/fpc maxError
salvatore-campagna Feb 18, 2026
4d21de8
feat(tsdb): enable chimp quantization in balanced mode and simplify F…
salvatore-campagna Feb 18, 2026
5423bc9
feat(tsdb): revise balanced pipeline to lossless ALP gauges and delta…
salvatore-campagna Feb 18, 2026
c54db49
feat(tsdb): deferred pipeline resolution with field context and block…
salvatore-campagna Feb 20, 2026
19ed942
feat(tsdb): adaptive pipeline selection with block profiling
salvatore-campagna Feb 20, 2026
be05dc9
feat(tsdb): add pipeline selection logging and stage display names
salvatore-campagna Feb 20, 2026
267d976
chore(otel): use storage optimize_for hint for otel double metrics
salvatore-campagna Feb 20, 2026
373d02d
fix(tsdb): skip XOR and FPC in noisy float path when they provide no …
salvatore-campagna Feb 20, 2026
15f1701
Merge branch 'main' into feature/tsdb-pipeline-poc
salvatore-campagna Feb 20, 2026
739e627
fix(tsdb): propagate correct DataType in pipeline selector for all paths
salvatore-campagna Feb 20, 2026
6d8cc08
feat(tsdb): use Gorilla for monotonic double counters and ALP with 6-…
salvatore-campagna Feb 21, 2026
6e2da48
feat(tsdb): route _seq_no through adaptive pipeline resolver
salvatore-campagna Feb 21, 2026
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
15 changes: 14 additions & 1 deletion benchmarks/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,24 @@ tasks.named('test').configure {
}
}

// NOTE: Copy NumericDataGenerators from server test sources so benchmarks can reuse
// the same data patterns without adding a testFixtures dependency.
def numericDataGenSource = "${project(':server').projectDir}/src/test/java/org/elasticsearch/index/codec/tsdb/pipeline/NumericDataGenerators.java"
def numericDataGenDir = layout.buildDirectory.dir("generated/sources/numericDataGenerators")
def copyNumericDataGenerators = tasks.register('copyNumericDataGenerators', Copy) {
inputs.file(numericDataGenSource)
from numericDataGenSource
into numericDataGenDir.map { it.dir("org/elasticsearch/index/codec/tsdb/pipeline") }
outputs.dir(numericDataGenDir)
}
sourceSets.main.java.srcDir numericDataGenDir
tasks.named('compileJava').configure { dependsOn(copyNumericDataGenerators) }

spotless {
java {
// IDEs can sometimes run annotation processors that leave files in
// here, causing Spotless to complain. Even though this path ought not
// to exist, exclude it anyway in order to avoid spurious failures.
targetExclude 'src/main/generated/**/*.java'
targetExclude 'src/main/generated/**/*.java', 'build/generated/**/*.java'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,8 @@ private static BlockLoader numericBlockLoader(WhereAndBaseName w, NumberFieldMap
false,
null,
null,
false
false,
null
).blockLoader(blContext());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.benchmark.index.codec.tsdb;

import org.elasticsearch.benchmark.index.codec.tsdb.internal.AbstractTSDBCodecBenchmark;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.AllStagesSupplier;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.DecodeBenchmark;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.ThroughputMetrics;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
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.Warmup;
import org.openjdk.jmh.infra.Blackhole;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
* Benchmark for decoding data that triggers all encoding stages (Delta -> Offset -> GCD -> FOR).
*/
@Fork(value = 1)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class DecodeAllStagesBenchmark {
private static final int SEED = 17;

@Param({ "10" })
private int blocksPerInvocation;

private final AbstractTSDBCodecBenchmark decode;

public DecodeAllStagesBenchmark() {
this.decode = new DecodeBenchmark();
}

@Setup(Level.Trial)
public void setupTrial() throws IOException {
AllStagesSupplier supplier = AllStagesSupplier.builder(SEED, decode.getBlockSize()).build();
decode.setupTrial(supplier);
decode.setBlocksPerInvocation(blocksPerInvocation);
decode.run();
}

@Benchmark
public void throughput(Blackhole bh, ThroughputMetrics metrics) throws IOException {
decode.benchmark(bh);
metrics.recordOperation(decode.getBlockSize() * blocksPerInvocation, decode.getEncodedSize() * blocksPerInvocation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.benchmark.index.codec.tsdb;

import org.elasticsearch.benchmark.index.codec.tsdb.internal.DecodeBenchmark;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.NumericDataSupplierRegistry;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.ThroughputMetrics;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
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.Warmup;
import org.openjdk.jmh.infra.Blackhole;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

// NOTE: Decoding benchmark using NumericDataGenerators double data sources.
@Fork(value = 1)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class DecodeDoubleGeneratorsBenchmark {

@Param(
{
"constant-double",
"percentage-double",
"monotonic-double",
"gauge-double",
"realistic-gauge-double",
"sparse-gauge-double",
"random-double",
"stable-sensor-double",
"tiny-increment-double",
"steady-counter-double",
"burst-spike-double",
"zero-crossing-oscillation-double",
"step-with-spikes-double",
"counter-with-resets-double",
"quantized-double",
"sensor-2dp-double",
"temperature-1dp-double",
"financial-2dp-double",
"percentage-rounded-1dp-double",
"mixed-sign-double",
"step-hold-double",
"timestamp-as-double",
"counter-as-double",
"gauge-as-double",
"gcd-as-double",
"constant-as-double",
"random-as-double",
"decreasing-timestamp-as-double",
"small-as-double",
"timestamp-with-jitter-as-double" }
)
private String datasetName;

@Param({ "42" })
private String seed;

@Param({ "10" })
private int blocksPerInvocation;

private final DecodeBenchmark decode = new DecodeBenchmark();

@Setup(Level.Trial)
public void setupTrial() throws IOException {
final Map<String, Supplier<long[]>> registry = NumericDataSupplierRegistry.toMap(
NumericDataSupplierRegistry.doubleSuppliers(decode.getBlockSize(), Long.parseLong(seed))
);
if (registry.size() != 30) {
throw new IllegalStateException(
"Expected 30 double datasets but found " + registry.size() + ". Update @Param list. Available: " + registry.keySet()
);
}
final Supplier<long[]> supplier = registry.get(datasetName);
if (supplier == null) {
throw new IllegalArgumentException("Unknown double dataset: [" + datasetName + "]. Available: " + registry.keySet());
}
decode.setupTrial(supplier);
decode.setBlocksPerInvocation(blocksPerInvocation);
}

@Benchmark
public void throughput(Blackhole bh, ThroughputMetrics metrics) throws IOException {
decode.benchmark(bh);
metrics.recordOperation(decode.getBlockSize() * blocksPerInvocation, decode.getEncodedSize() * blocksPerInvocation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.benchmark.index.codec.tsdb;

import org.elasticsearch.benchmark.index.codec.tsdb.internal.DecodeBenchmark;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.NumericDataSupplierRegistry;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.ThroughputMetrics;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
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.Warmup;
import org.openjdk.jmh.infra.Blackhole;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

// NOTE: Decoding benchmark using NumericDataGenerators long data sources.
@Fork(value = 1)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class DecodeLongGeneratorsBenchmark {

@Param(
{
"timestamp",
"counter",
"gauge",
"gcd",
"constant",
"random",
"decreasing-timestamp",
"boundary",
"small",
"timestamp-with-jitter" }
)
private String datasetName;

@Param({ "42" })
private String seed;

@Param({ "10" })
private int blocksPerInvocation;

private final DecodeBenchmark decode = new DecodeBenchmark();

@Setup(Level.Trial)
public void setupTrial() throws IOException {
final Map<String, Supplier<long[]>> registry = NumericDataSupplierRegistry.toMap(
NumericDataSupplierRegistry.longSuppliers(decode.getBlockSize(), Long.parseLong(seed))
);
final Supplier<long[]> supplier = registry.get(datasetName);
if (supplier == null) {
throw new IllegalArgumentException("Unknown long dataset: [" + datasetName + "]. Available: " + registry.keySet());
}
decode.setupTrial(supplier);
decode.setBlocksPerInvocation(blocksPerInvocation);
}

@Benchmark
public void throughput(Blackhole bh, ThroughputMetrics metrics) throws IOException {
decode.benchmark(bh);
metrics.recordOperation(decode.getBlockSize() * blocksPerInvocation, decode.getEncodedSize() * blocksPerInvocation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.benchmark.index.codec.tsdb;

import org.elasticsearch.benchmark.index.codec.tsdb.internal.AbstractTSDBCodecBenchmark;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.AllStagesSupplier;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.CompressionMetrics;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.EncodeBenchmark;
import org.elasticsearch.benchmark.index.codec.tsdb.internal.ThroughputMetrics;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
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.Warmup;
import org.openjdk.jmh.infra.Blackhole;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
* Benchmark for encoding data that triggers all encoding stages (Delta -> Offset -> GCD -> FOR).
*/
@Fork(value = 1)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class EncodeAllStagesBenchmark {
private static final int SEED = 17;

@Param({ "10" })
private int blocksPerInvocation;

private final AbstractTSDBCodecBenchmark encode;

public EncodeAllStagesBenchmark() {
this.encode = new EncodeBenchmark();
}

@Setup(Level.Trial)
public void setupTrial() throws IOException {
AllStagesSupplier supplier = AllStagesSupplier.builder(SEED, encode.getBlockSize()).build();
encode.setupTrial(supplier);
encode.setBlocksPerInvocation(blocksPerInvocation);
encode.run();
}

@Benchmark
public void throughput(Blackhole bh, ThroughputMetrics metrics) throws IOException {
encode.benchmark(bh);
metrics.recordOperation(encode.getBlockSize() * blocksPerInvocation, encode.getEncodedSize() * blocksPerInvocation);
}

@Benchmark
@Warmup(iterations = 0)
@Measurement(iterations = 1)
@BenchmarkMode(Mode.SingleShotTime)
public void compression(Blackhole bh, CompressionMetrics metrics) throws IOException {
encode.benchmark(bh);
metrics.recordOperation(encode.getBlockSize(), encode.getEncodedSize(), 0);
}
}
Loading