Skip to content

Add JMH benchmark for auto-mapping code#140

Merged
driessamyn merged 3 commits into
mainfrom
refactor-kotlin-reflection
Jun 21, 2025
Merged

Add JMH benchmark for auto-mapping code#140
driessamyn merged 3 commits into
mainfrom
refactor-kotlin-reflection

Conversation

@driessamyn
Copy link
Copy Markdown
Owner

This will facilate optimisation efforts.

Copilot AI review requested due to automatic review settings June 20, 2025 16:28
@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Jun 20, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Make null logic deterministic

The null determination logic uses modulo operation that could produce
inconsistent results across benchmark runs due to the random seed. Consider
using a deterministic approach based only on row and column indices for
reproducible benchmarks.

benchmark/src/main/kotlin/net/samyn/kapper/benchmark/mapper/ResultSetStub.kt [48]

-private fun isNull(columnIndex: Int): Boolean = columnIndex in nullableColumns && (currentRow + columnIndex + random) % 2 != 0
+private fun isNull(columnIndex: Int): Boolean = columnIndex in nullableColumns && (currentRow + columnIndex) % 3 == 0
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that using a random value in the isNull check makes the benchmark's data generation non-deterministic. For reliable and reproducible benchmarks, the input data must be consistent across runs. The proposed change to a deterministic logic is a significant improvement.

Medium
Replace assert with require

Replace assert with require for better error handling in production benchmarks.
The assert statement can be disabled with JVM flags, making the validation
ineffective during actual benchmark runs.

benchmark/src/jmh/kotlin/net/samyn/kapper/benchmark/AutoMapperBenchmark.kt [53-55]

-assert(allResults.size == state.benchmarkStrategy.numberOfResults) {
+require(allResults.size == state.benchmarkStrategy.numberOfResults) {
     "Expected ${state.benchmarkStrategy.numberOfResults} results, but got ${allResults.size}"
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion to replace assert with require is valid. assert statements can be disabled at runtime using JVM flags, which would cause this important validation to be skipped. Using require ensures the check is always performed, making the benchmark more robust.

Low
Enhance error message with JDBC type

Include the JDBC type information in the error message to provide more context
for debugging. This helps developers understand both the database type name and
the corresponding JDBC type when conversion fails.

core/src/main/kotlin/net/samyn/kapper/internal/automapper/SQLTypesConverter.kt [79-81]

 throw KapperUnsupportedOperationException(
-    "Conversion of field[${field.columnIndex}] from type ${field.typeName} is not supported",
+    "Conversion of field[${field.columnIndex}] from type ${field.typeName} (JDBC: ${field.jdbcType}) is not supported",
 )
  • Apply / Chat
Suggestion importance[1-10]: 4

__

Why: This is a good suggestion for improving developer experience. Including the jdbcType in the exception message provides more specific information, which can aid in debugging unsupported type conversion issues.

Low
Organization
best practice
Simplify interface implementation complexity

This class implements the entire ResultSet interface with mostly
NotImplementedError methods, making it overly complex. Consider creating a
minimal interface or abstract class that only defines the methods actually
needed for benchmarking.

benchmark/src/main/kotlin/net/samyn/kapper/benchmark/mapper/ResultSetStub.kt [24-56]

+interface BenchmarkResultSet {
+    fun next(): Boolean
+    fun getString(columnIndex: Int): String?
+    fun getInt(columnIndex: Int): Int
+    fun getBoolean(columnIndex: Int): Boolean
+    fun getDouble(columnIndex: Int): Double
+    fun close()
+}
+
 class ResultSetStub(
     private val rowCount: Int,
     private val nullableColumns: Set<Int> = emptySet(),
     private val uuidColumns: Set<Int> = emptySet(),
-) : ResultSet {
-    private var currentRow = 0
-    private val random = (System.currentTimeMillis() % 1234).toInt()
-    private val name = "Name-$random"
+) : BenchmarkResultSet {
+    ...
+}
 
-    override fun next(): Boolean {
-        return ++currentRow <= rowCount
-    }
-    ...
-    override fun <T : Any?> unwrap(iface: Class<T>?): T {
-        throw NotImplementedError()
-    }
-    ...
-

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Keep It Simple, Stupid - systems work best if they are kept simple rather than made complex

Low
  • Update

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a JMH benchmarking suite for the auto-mapping layer to help profile and optimize performance. Key changes:

  • Promote DbFlavour to a public API and update imports across tests and main code.
  • Add new benchmark classes and a script (run-mapper.sh) to run mapper performance tests via JMH.
  • Adjust JMH plugin configuration and refine exception messaging in SQLTypesConverter.

Reviewed Changes

Copilot reviewed 34 out of 36 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
coroutines/src/…/FlowQueryTest.kt Switched import to public DbFlavour
coroutines/src/…/KapperKotlinFlowQueryFun.kt Switched import to public DbFlavour
core/src/main/kotlin/.../SQLTypesConverter.kt Refined unsupported-conversion exception message; imported public DbFlavour.
core/src/main/kotlin/.../DbFlavourFunc.kt Removed internal DbFlavour enum and added import of public version
core/src/main/kotlin/net/samyn/kapper/DbFlavour.kt Added public DbFlavour enum
core/src/test/kotlin/... Updated dozens of tests to import public DbFlavour
benchmark/src/main/kotlin/... Added MapperBenchmark interface and multiple mapper benchmarks
benchmark/src/jmh/.../AutoMapperBenchmark.kt Added JMH benchmark harness for mapping scenarios
benchmark/run-mapper.sh New script to build and execute the JMH benchmarks
benchmark/build.gradle.kts Updated JMH plugin settings and added custom JMH tasks

Comment thread benchmark/run-mapper.sh Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 20, 2025

Unit Tests

 42 files  +1   42 suites  +1   20s ⏱️ +3s
437 tests +4  437 ✅ +4  0 💤 ±0  0 ❌ ±0 
452 runs  +4  452 ✅ +4  0 💤 ±0  0 ❌ ±0 

Results for commit b19566d. ± Comparison against base commit 965f4e2.

This pull request removes 42 and adds 45 tests. Note that renamed tests count towards both.

net.samyn.kapper.internal.QueryParserTest ‑ [5] 
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [10] LONGNVARCHAR, LONGNVARCHAR, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$1021/0x00007fa2e84faba0@43755c41
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [11] INSTANT, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$913/0x00007fa2e84bb710@44e165e6
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [11] LONGVARCHAR, LONGVARCHAR, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$1022/0x00007fa2e84fadc0@791cb215
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [12] DATE, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$914/0x00007fa2e84bb930@3ce3cd43
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [12] NCHAR, NCHAR, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$1023/0x00007fa2e84fafe0@4dc2a4d1
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [13] LOCALDATE, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$915/0x00007fa2e84bbb50@57c64178
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [13] NCLOB, NCLOB, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$1024/0x00007fa2e84fb200@39bfa161
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [14] LOCALDATETIME, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$916/0x00007fa2e84bbd70@5b484791
…
net.samyn.kapper.benchmark.mapper.MapperBenchmarkTest ‑ complex data class mapper should return mapped results()
net.samyn.kapper.benchmark.mapper.MapperBenchmarkTest ‑ complex record mapper should return mapped results()
net.samyn.kapper.benchmark.mapper.MapperBenchmarkTest ‑ simple data class mapper should return mapped results()
net.samyn.kapper.benchmark.mapper.MapperBenchmarkTest ‑ simple record mapper should return mapped results()
net.samyn.kapper.internal.QueryParserTest ‑ [5] 

net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [10] LONGNVARCHAR, LONGNVARCHAR, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$1010/0x00007f3b4c4fe220@31949439
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [11] INSTANT, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$920/0x00007f3b4c4cfba0@65e296bf
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [11] LONGVARCHAR, LONGVARCHAR, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$1011/0x00007f3b4c4fe440@164b7a56
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [12] DATE, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$921/0x00007f3b4c4cfdc0@81bc9c2
net.samyn.kapper.internal.automapper.SQLTypesConverterTest ‑ [12] NCHAR, NCHAR, net.samyn.kapper.internal.automapper.SQLTypesConverterTest$Companion$$Lambda$1012/0x00007f3b4c4fe660@56213c4b
…

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 20, 2025

SQLITE Integration Tests

 9 files  ±0   9 suites  ±0   23s ⏱️ ±0s
39 tests ±0  39 ✅ ±0  0 💤 ±0  0 ❌ ±0 
63 runs  ±0  63 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit b19566d. ± Comparison against base commit 965f4e2.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 20, 2025

POSTGRESQL Integration Tests

 9 files  ±0   9 suites  ±0   15s ⏱️ ±0s
39 tests ±0  39 ✅ ±0  0 💤 ±0  0 ❌ ±0 
63 runs  ±0  63 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit b19566d. ± Comparison against base commit 965f4e2.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 20, 2025

MYSQL Integration Tests

 9 files  ±0   9 suites  ±0   21s ⏱️ ±0s
39 tests ±0  39 ✅ ±0  0 💤 ±0  0 ❌ ±0 
63 runs  ±0  63 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit b19566d. ± Comparison against base commit 965f4e2.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 20, 2025

MSSQLSERVER Integration Tests

 9 files  ±0   9 suites  ±0   21s ⏱️ -3s
39 tests ±0  39 ✅ ±0  0 💤 ±0  0 ❌ ±0 
63 runs  ±0  63 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit b19566d. ± Comparison against base commit 965f4e2.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 20, 2025

ORACLE Integration Tests

 9 files  ±0   9 suites  ±0   24s ⏱️ -1s
39 tests ±0  39 ✅ ±0  0 💤 ±0  0 ❌ ±0 
63 runs  ±0  63 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit b19566d. ± Comparison against base commit 965f4e2.

♻️ This comment has been updated with latest results.

@driessamyn driessamyn force-pushed the refactor-kotlin-reflection branch from 40f7641 to b19566d Compare June 21, 2025 10:59
@sonarqubecloud
Copy link
Copy Markdown

@driessamyn driessamyn merged commit 3ef5df9 into main Jun 21, 2025
16 checks passed
@driessamyn driessamyn deleted the refactor-kotlin-reflection branch June 21, 2025 11:55
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.

2 participants