Skip to content

Add KVIN ingestion benchmarks for CSV and JSON processing#52

Open
wmehling wants to merge 1 commit into
mainfrom
51-better-benchmarking-with-jmh-for-time-series-data-ingest
Open

Add KVIN ingestion benchmarks for CSV and JSON processing#52
wmehling wants to merge 1 commit into
mainfrom
51-better-benchmarking-with-jmh-for-time-series-data-ingest

Conversation

@wmehling

Copy link
Copy Markdown
Contributor

Changes

  • Implement KvinIngestionCsvDiagnosticBenchmark for performance testing of CSV ingestion.
  • Create KvinIngestionJsonDiagnosticBenchmark for benchmarking JSON ingestion.
  • Introduce KvinIngestionWorkload to generate test data for benchmarks.
  • Add unit tests for KvinIngestionCsvDiagnosticBenchmark and KvinIngestionWorkload.
  • Remove obsolete KvinServiceBenchmark Scala file.
  • Document benchmark setup and execution in markdown format

How to review

Look at doc kvin-ingestion.md and run benchmarks and see results.

Results from my tests (max values)

Overall

  • putBatch: ~925 k tuples/s
  • putCsvDirect: ~480 k tuples/s
  • postCsv: ~415 k tuples/s
  • postCsvSequentialFiles: ~371 k tuples/s
  • postJson: ~127 k tuples/s

CSV (as known) significantly faster with some headroom to faster parse so we can achieve closer to raw speed of DB around 900 k tuples/s

CSV diagnostics

Steps building on each other with more and more of the actual postCSV pipeline:

  • consumePrebuilt: ~69689 / ~59060 k tuples/s (consumes already-built tuples through a Blackhole → Iteration fast, no bottleneck)
  • decodeCsvAndConsumeFields: ~1404 / ~1725 k tuples/s (adds raw CSV decoding with OpenCSV reader, tokenization, field string creation. Still very fast)
  • parseCsvAndConsumeTuples: ~675 / ~621 k tuples/s (adds header mapping, trimming, type interpretation, and actual KvinTuple construction (still no LevelDB) -> Here quite some performance lost
  • postCsvParseOnly: ~512 / ~602 k tuples/s (overhead for request / routing: adds real in-process Lift route and routing on top, with a sink that discards tuples (no persistence)

So would try to make the parsing and consuming faster in a seperate issue building on that branch.

- Implement KvinIngestionCsvDiagnosticBenchmark for performance testing of CSV ingestion.
- Create KvinIngestionJsonDiagnosticBenchmark for benchmarking JSON ingestion.
- Introduce KvinIngestionWorkload to generate test data for benchmarks.
- Add unit tests for KvinIngestionCsvDiagnosticBenchmark and KvinIngestionWorkload.
- Remove obsolete KvinServiceBenchmark Scala file.
- Document benchmark setup and execution in markdown format.
@wmehling wmehling linked an issue Jul 17, 2026 that may be closed by this pull request
@wmehling
wmehling requested a review from kenwenzel July 17, 2026 19:26

@kenwenzel kenwenzel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to remove the use of reflection and to think about reworking the generation of tuples with more diversity in item, property and time.

jsonPayload = workload.jsonPayload();
csvPayload = workload.csvPayload();
csvPayloads = workload.csvPayloads(SEQUENTIAL_CSV_FILE_COUNT);
try {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI often uses reflection to load classes from other modules - this is usually wrong.

Use something like

 // create configuration and a model set factory
    val module: KommaModule = ModelPlugin.createModelSetModule(classOf[ModelPlugin].getClassLoader)
    val factory: IModelSetFactory = Guice.createInjector(new ModelSetModule(module)).getInstance(classOf[IModelSetFactory])

    // create a model set with an in-memory repository
    modelSet = factory.createModelSet(MODELS.NAMESPACE_URI.appendFragment("MemoryModelSet"))
    Globals.contextModelSet.default.set(Full(modelSet))
``
as it was also contained in the deleted KvinServiceBenchmark.

workload = new KvinIngestionWorkload();
csvPayload = workload.csvPayload();
try {
KommaModule module = ModelPlugin.createModelSetModule(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace by version without reflection.

public static final int SEQUENCES_PER_TIMESTAMP = 5;
public static final long START_TIME = 1_710_000_000_000L;

public static final URI PROPERTY = URIs.createURI("http://iwu.lf.de/ecc4p/values");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be more realistic to slightly randomize this. Something like choosing 6 channels out of 100 randomly and one property out of 10.

private final byte[] csvPayload;

public KvinIngestionWorkload() {
this.tuples = createTuples();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully understand the logic but it seems that only a fixed set of tuples with static timestamps is generated once and then inserted on each iteration?!

}

private static JValue parseJson(String json) throws Exception {
Class<?> parserClass = Class.forName("org.json4s.native.JsonParser$");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use of reflection instead of direct class names


@SuppressWarnings("unchecked")
private static Box<List<KvinTuple>> parseProductionJson(JValue json) throws Exception {
Class<?> parserClass = Class.forName("io.github.linkedfactory.service.util.JsonFormatParser$");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use of reflection instead of direct class names

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Better benchmarking with JMH for time series data ingest

2 participants