8000 [PERF] Jackson reflection-free serialization/deserialization by mabartos · Pull Request #42946 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Merged
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: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,11 @@ jobs:
uses: ./.github/actions/integration-test-setup

- name: Run base tests
# enable the http-optimized-serializers feature for the old testsuite to verify it works as expected
run: |
TESTS=`testsuite/integration-arquillian/tests/base/testsuites/base-suite.sh ${{ matrix.group }}`
echo "Tests: $TESTS"
./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base 2>&1 | misc/log/trimmer.sh
./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus -Dauth.server.feature=http-optimized-serializers -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base 2>&1 | misc/log/trimmer.sh

- uses: ./.github/actions/upload-flaky-tests
name: Upload flaky tests
Expand Down
2 changes: 2 additions & 0 deletions common/src/main/java/org/keycloak/common/Profile.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ public enum Feature {

DB_TIDB("TiDB database type", Type.EXPERIMENTAL),

HTTP_OPTIMIZED_SERIALIZERS("Optimized JSON serializers for better performance of the HTTP layer", Type.PREVIEW),

/**
* @see <a href="https://github.com/keycloak/keycloak/issues/37967">Deprecate for removal the Instagram social broker</a>.
*/
Expand Down
3 changes: 3 additions & 0 deletions docs/documentation/release_notes/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ include::topics/templates/document-attributes.adoc[]
:release_header_latest_link: {releasenotes_link_latest}
include::topics/templates/release-header.adoc[]

== {project_name_full} 26.5.0
include::topics/26_5_0.adoc[leveloffset=2]

== {project_name_full} 26.4.0
include::topics/26_4_0.adoc[leveloffset=2]

Expand Down
19 changes: 16 additions & 3 deletions docs/documentation/release_notes/topics/26_5_0.adoc
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
// Release notes should contain only headline-worthy new features,
// assuming that people who migrate will read the upgrading guide anyway.
//
== Breaking Fix for Windows in Loopback Hostname Verification

= Preview of enhanced HTTP performance

You can now enable a more efficient way to handle JSON data in the HTTP layer.
This change increases throughput by ~5%, stabilizes response times, and reduces system resource usage.

In order to apply it, you need to explicitly enable the feature `http-optimized-serializers`.

NOTE: This feature is *preview*.
ifeval::[{project_community}==true]
We gather more feedback about potential issues in https://github.com/keycloak/keycloak/discussions/43484[this discussion]. We appreciate any feedback.
endif::[]

For more details, see the https://www.keycloak.org/server/configuration-production[Configuring Keycloak for production] guide.

= Breaking Fix for Windows in Loopback Hostname Verification

This release introduces a breaking change for Windows users: setups that previously relied on custom machine names or non-standard hostnames for loopback (e.g., `127.0.0.1` resolving to a custom name) may require updates to their trusted domain configuration. Only `localhost` and `*.localhost` are now recognized for loopback verification.

Keycloak now consistently normalizes loopback addresses to `localhost` for domain verification across all platforms. This change ensures predictable behavior for trusted domain checks, regardless of the underlying OS.

18 changes: 2 additions & 16 deletions docs/guides/securing-apps/token-exchange.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<#import "/templates/guide.adoc" as tmpl>
<#import "/templates/links.adoc" as links>
<#import "/templates/features.adoc" as features>

<@tmpl.guide
title="Configuring and using token exchange"
Expand Down Expand Up @@ -327,22 +328,7 @@ s|Subject impersonation (including direct naked impersonation) | Not implement
[[_legacy-token-exchange]]
== Legacy token exchange

:tech_feature_name: Token Exchange
:tech_feature_id: token-exchange

[NOTE]
====
{tech_feature_name} is
*Preview*
and is not fully supported. This feature is disabled by default.

To enable start the server with `--features=preview`
ifdef::tech_feature_id[]
or `--features={tech_feature_id}`
endif::[]

{tech_feature_name} is *Technology Preview* and is not fully supported.
====
<@features.techpreview feature="token-exchange"/>

[NOTE]
====
Expand Down
22 changes: 22 additions & 0 deletions docs/guides/server/configuration-production.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<#import "/templates/guide.adoc" as tmpl>
<#import "/templates/kc.adoc" as kc>
<#import "/templates/links.adoc" as links>
<#import "/templates/features.adoc" as features>

<@tmpl.guide
title="Configuring {project_name} for production"
Expand Down Expand Up @@ -87,4 +88,25 @@ export JAVA_OPTS_APPEND="-Djava.net.preferIPv4Stack=false -Djava.net.preferIPv6A

See <@links.server id="caching" anchor="network-bind-address"/> for more details.

== Preview of enhanced HTTP performance
<@features.techpreview feature="http-optimized-serializers" additionalCommunityText="We gather more feedback on this feature to promote it to supported. Please, share your feedback about any issue in https://github.com/keycloak/keycloak/discussions/43484[this discussion]."/>

In production environments, the performance of the HTTP layer is critical.
Every request passes through it, making it a key factor in overall system responsiveness, scalability, and user experience.

This feature improves how {project_name} handles JSON data in HTTP requests and responses.
The result is a more efficient runtime with measurable benefits:

- ~5% increase in throughput
- More stable response times
- Reduced system resource usage
Comment thread
mabartos marked this conversation as resolved.
Resolved

These improvements help ensure smoother, more predictable performance at scale while also lowering the operational cost of running production systems.

The only known tradeoff is that build time increases by ~6% as certain actions were moved to the build time instead of runtime.

You can enable this feature as follows:

<@kc.start parameters="--features=http-optimized-serializers"/>

</@tmpl.guide>
23 changes: 23 additions & 0 deletions docs/guides/templates/features.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,26 @@
</#list>
|===
</#macro>

<#macro techpreview feature additionalCommunityText="">
<#assign profileFeature = ctx.features.getFeature(feature)>

[NOTE]
====
${profileFeature.description} is
ifeval::[{project_product}==true]
*Technology Preview*
endif::[]
ifeval::[{project_community}==true]
*Preview*
endif::[]
and is not fully supported. This feature is disabled by default.

ifeval::[{project_community}==true]
${additionalCommunityText!""}
endif::[]

To enable start the server with <#if profileFeature.type != "PREVIEW_DISABLED_BY_DEFAULT">`--features=preview` or </#if>`--features=${profileFeature.name}`

====
</#macro>
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public List<Feature> getUpdatePolicyRollingNoUpgrade() {
return features.stream().filter(f -> f.profileFeature.getUpdatePolicy() == Profile.FeatureUpdatePolicy.ROLLING_NO_UPGRADE).collect(Collectors.toList());
}

public Feature getFeature(String featureId) {
return features.stream().filter(f -> f.getName().equals(featureId)).findAny()
.orElseThrow(() -> new IllegalArgumentException("Cannot find the '%s' feature for guides".formatted(featureId)));
}

public static class Feature {

private final Profile.Feature profileFeature;
Expand All @@ -67,7 +72,7 @@ public String getUpdatePolicy() {
return profileFeature.getUpdatePolicy().toString();
}

private Profile.Feature.Type getType() {
public Profile.Feature.Type getType() {
return profileFeature.getType();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,4 @@ public enum ClientAuth {
.description("Service level objectives for HTTP server requests. Use this instead of the default histogram, or use it in combination to add additional buckets. " +
"Specify a list of comma-separated values defined in milliseconds. Example with buckets from 5ms to 10s: 5,10,25,50,250,500,1000,2500,5000,10000")
.build();

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.quarkus.vertx.http.runtime.options.TlsUtils;
import io.smallrye.config.ConfigSourceInterceptorContext;

import org.keycloak.common.Profile;
import org.keycloak.common.crypto.FipsMode;
import org.keycloak.config.HttpOptions;
import org.keycloak.config.SecurityOptions;
Expand All @@ -23,6 +24,7 @@

import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalKcValue;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalValue;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromFeature;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;

public final class HttpPropertyMappers implements PropertyMapperGrouping {
Expand Down Expand Up @@ -154,6 +156,9 @@ public List<PropertyMapper<?>> getPropertyMappers() {
fromOption(HttpOptions.HTTP_METRICS_SLOS)
.isEnabled(MetricsPropertyMappers::metricsEnabled, MetricsPropertyMappers.METRICS_ENABLED_MSG)
.paramLabel("list of buckets")
.build(),
fromFeature(Profile.Feature.HTTP_OPTIMIZED_SERIALIZERS)
.to("quarkus.rest.jackson.optimization.enable-reflection-free-serializers")
.build()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
import java.util.function.Function;
import java.util.stream.Stream;

import org.keycloak.common.Profile;
import org.keycloak.config.DeprecatedMetadata;
import org.keycloak.config.Option;
import org.keycloak.config.OptionBuilder;
import org.keycloak.config.OptionCategory;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import org.keycloak.quarkus.runtime.cli.ShortErrorMessageHandler;
Expand Down Expand Up @@ -547,6 +549,22 @@ public static <T> PropertyMapper.Builder<T> fromOption(Option<T> opt) {
return new PropertyMapper.Builder<>(opt);
}

/**
* Create a property mapper from a feature.
* The mapper maps to external properties the state of the feature.
* <p>
* If the feature is enabled, it returns {@code true}. Otherwise {@code null}.
*/
public static PropertyMapper.Builder<Boolean> fromFeature(Profile.Feature feature) {
final var option = new OptionBuilder<>(feature.getKey() + "-hidden-mapper", Boolean.class)
.buildTime(true)
.hidden()
.build();
return new Builder<>(option)
.isEnabled(() -> Profile.isFeatureEnabled(feature))
.transformer((v, ctx) -> Boolean.TRUE.toString()); // we know the feature is enabled due to .isEnabled()
}

public void validate(ConfigValue value) {
if (validator != null) {
validator.accept(this, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -966,4 +966,16 @@ public void healthEnabledRequired() {
assertEquals(CommandLine.ExitCode.USAGE, nonRunningPicocli.exitCode);
assertThat(nonRunningPicocli.getErrString(), containsString("Available only when health is enabled"));
}

@Test
public void httpOptimizedSerializers() {
var nonRunningPicocli = pseudoLaunch("start-dev");
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
assertExternalConfigNull("quarkus.rest.jackson.optimization.enable-reflection-free-serializers");
onAfter();

nonRunningPicocli = pseudoLaunch("start-dev", "--features=http-optimized-serializers");
assertEquals(CommandLine.ExitCode.OK, nonRunningPicocli.exitCode);
assertExternalConfig("quarkus.rest.jackson.optimization.enable-reflection-free-serializers", "true");
}
}
Loading
0