mirror of
https://github.com/keycloak/keycloak.git
synced 2025-12-21 06:20:05 -06:00
[PERF] Jackson reflection-free serialization/deserialization (#42946)
* [PERF] Jackson reflection-free serialization/deserialization Closes #42945 Signed-off-by: Martin Bartoš <mabartos@redhat.com> * Update docs/guides/server/configuration-production.adoc Co-authored-by: Alexander Schwartz <alexander.schwartz@gmx.net> Signed-off-by: Martin Bartoš <mabartos@redhat.com> * Docs improvements Signed-off-by: Martin Bartoš <mabartos@redhat.com> * Update docs/guides/server/configuration-production.adoc Co-authored-by: Václav Muzikář <vaclav@muzikari.cz> Signed-off-by: Martin Bartoš <mabartos@redhat.com> * Polish the features template macros Signed-off-by: Martin Bartoš <mabartos@redhat.com> --------- Signed-off-by: Martin Bartoš <mabartos@redhat.com> Co-authored-by: Alexander Schwartz <alexander.schwartz@gmx.net> Co-authored-by: Václav Muzikář <vaclav@muzikari.cz>
This commit is contained in:
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
||||
@@ -148,6 +148,8 @@ public class Profile {
|
||||
|
||||
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>.
|
||||
*/
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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]
|
||||
====
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -43,6 +43,11 @@ public class Features {
|
||||
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;
|
||||
@@ -67,7 +72,7 @@ public class Features {
|
||||
return profileFeature.getUpdatePolicy().toString();
|
||||
}
|
||||
|
||||
private Profile.Feature.Type getType() {
|
||||
public Profile.Feature.Type getType() {
|
||||
return profileFeature.getType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,5 +150,4 @@ public class HttpOptions {
|
||||
.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();
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.quarkus.runtime.util.ClassPathUtils;
|
||||
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;
|
||||
@@ -23,6 +24,7 @@ import java.util.Optional;
|
||||
|
||||
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 {
|
||||
@@ -154,6 +156,9 @@ public final class HttpPropertyMappers implements PropertyMapperGrouping {
|
||||
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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,8 +33,10 @@ import java.util.function.Consumer;
|
||||
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;
|
||||
@@ -547,6 +549,22 @@ public class PropertyMapper<T> {
|
||||
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);
|
||||
|
||||
@@ -966,4 +966,16 @@ public class PicocliTest extends AbstractConfigurationTest {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user