spring-projects / spring-projects/spring-data-commons

Avoid Kotlin reflection in KotlinReflectionUtils.isSuspend for methods without a Continuation parameter

Open Beginner friendly
#3,544 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
838
Forks
730
PR merge metrics
No merged PRs in 30d

Description

KotlinReflectionUtils.isSuspend(Method) resolves the KFunction for every Kotlin method it is asked about via ReflectJvmMapping.getKotlinFunction(method). That lookup materializes all members of the declaring KClass through Kotlin reflection (metadata deserialization plus a linear scan comparing javaMethod for each member), only to read KFunction.isSuspend().

AbstractRepositoryMetadata.getReturnType(Method) calls isSuspend for every query method during repository initialization, and RepositoryMethodInvoker calls it again when the invoker is created. For Kotlin repositories that do not use coroutines at all, this is pure overhead: a suspend function always compiles to a JVM method whose last parameter is kotlin.coroutines.Continuation, so a method without such a parameter can never be suspending. Spring Framework's KotlinDetector.isSuspendingFunction(Method) performs exactly that check without loading Kotlin reflection, and QueryExecutionResultHandler in this module already relies on it.

In a Spring Boot 4.2.0-SNAPSHOT / Spring Data 2026.1.0-SNAPSHOT Kotlin application with 243 JPA repositories and 1,418 non-synthetic repository interface methods, a wall-clock startup profile (async-profiler, 2 ms sampling, extracted Boot layout) attributed 241 of 8,694 main-thread samples (2.8 % of startup, about 0.5 s) to KotlinReflectionUtils.isSuspend, all of it below ReflectJvmMapping.getKotlinFunction. A cold-JVM micro-benchmark that calls isSuspend for those 1,418 methods takes 1,146–1,341 ms on current main and 3–4 ms when the Continuation parameter check runs first (5 runs each, JDK 27, Apple M5 Max).

Proposal: short-circuit isSuspend with KotlinDetector.isSuspendingFunction(method) before touching Kotlin reflection, keeping the KFunction.isSuspend() verification for methods that do declare a trailing Continuation parameter so the result stays identical for non-suspending functions that take a Continuation argument explicitly.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at KotlinReflectionUtils.isSuspend(Method), then inspect its callers AbstractRepositoryMetadata.getReturnType(Method) and RepositoryMethodInvoker, along with KotlinDetector.isSuspendingFunction(Method) and QueryExecutionResultHandler. Done means methods without a trailing Continuation avoid Kotlin reflection while methods with one retain the KFunction.isSuspend() verification; run the relevant existing tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, kotlin, spring
Domain
backend, performance
Issue type
Refactor
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.