Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConfigGenerationBuildStep.runtimeDefaultsConfig crash when there are too many environment variables. #36394

Closed
ReluXingZeng opened this issue Oct 10, 2023 · 1 comment · Fixed by #35210
Labels
area/config kind/bug Something isn't working

Comments

@ReluXingZeng
Copy link

Describe the bug

ConfigGenerationBuildStep.runtimeDefaultsConfig crashed with "MethodTooLargeException".

This appears on a k8s cluster that has a lot of services.

We were able to remove / add on other services, and observe that remove / add other services in k8s remove / add a lot of environmental variables. When the number of environmental variable within the container goes > 5100, the crash appears. If it's around ~4500, we will not see any crash.

Expected behavior

When running another application that depends on quarkus, there should not be any error messages, regardless of how many environmental variables are there.

Actual behavior

When running some other application that depends on quarkus, on a k8s cluster that has > 5100 environmental variable, I got the following error message:

Updating the configuration and installing your custom providers, if any. Please wait.                                                                 
ERROR: Failed to run 'build' command.                                                                                                                
ERROR: io.quarkus.builder.BuildException: Build failure: Build failed due to errors                                                                           
        [error]: Build step io.quarkus.deployment.steps.ConfigGenerationBuildStep#runtimeDefaultsConfig threw an exception: org.objectweb.asm.MethodTooLargeException: Method too large: io/quarkus/runtime/generated/RunTimeDefaultsConfigSource.<clinit> ()V                                                      
        at org.objectweb.asm.MethodWriter.computeMethodInfoSize(MethodWriter.java:2088)                                                                       
        at org.objectweb.asm.ClassWriter.toByteArray(ClassWriter.java:511)                                                                                    
        at io.quarkus.gizmo.ClassCreator.writeTo(ClassCreator.java:238)
        at io.quarkus.gizmo.ClassCreator.close(ClassCreator.java:249)
        at io.quarkus.deployment.steps.ConfigGenerationBuildStep.generateDefaultsConfigSource(ConfigGenerationBuildStep.java:566)
        at io.quarkus.deployment.steps.ConfigGenerationBuildStep.runtimeDefaultsConfig(ConfigGenerationBuildStep.java:175)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:864)
        at io.quarkus.builder.BuildContext.run(BuildContext.java:282)
        at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
        at java.base/java.lang.Thread.run(Thread.java:833)
        at org.jboss.threads.JBossThread.run(JBossThread.java:501)

ERROR: Build failure: Build failed due to errors
        [error]: Build step io.quarkus.deployment.steps.ConfigGenerationBuildStep#runtimeDefaultsConfig threw an exception: org.objectweb.asm.MethodTooLargeException: Method too large: io/quarkus/runtime/generated/RunTimeDefaultsConfigSource.<clinit> ()V
        at org.objectweb.asm.MethodWriter.computeMethodInfoSize(MethodWriter.java:2088)
        at org.objectweb.asm.ClassWriter.toByteArray(ClassWriter.java:511)
        at io.quarkus.gizmo.ClassCreator.writeTo(ClassCreator.java:238)
        at io.quarkus.gizmo.ClassCreator.close(ClassCreator.java:249)
        at io.quarkus.deployment.steps.ConfigGenerationBuildStep.generateDefaultsConfigSource(ConfigGenerationBuildStep.java:566)
        at io.quarkus.deployment.steps.ConfigGenerationBuildStep.runtimeDefaultsConfig(ConfigGenerationBuildStep.java:175)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:864)
        at io.quarkus.builder.BuildContext.run(BuildContext.java:282)
        at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
        at java.base/java.lang.Thread.run(Thread.java:833)
        at org.jboss.threads.JBossThread.run(JBossThread.java:501)

ERROR: Method too large: io/quarkus/runtime/generated/RunTimeDefaultsConfigSource.<clinit> ()V
For more details run the same command passing the '--verbose' option. Also you can use '--help' to see the details about the usage of the particular command.

How to Reproduce?

I have so far only reproduced it through keycloak, which uses quarkus. The guide for reproducing it via keycloak is shown at:
keycloak/keycloak#23647

I do expect, however, that if calling ConfigGenerationBuildStep.generateDefaultsConfigSource - which is used by ConfigGenerationBuildStep.runtimeDefaultsConfig - with a Map<String, String> defaults with size around ~5100 items, the exception would appear.

This is because, apparently, this section of the line:

MethodCreator clinit = classCreator.getMethodCreator("<clinit>", void.class);
clinit.setModifiers(Opcodes.ACC_STATIC);
clinit.writeStaticField(properties, clinit.newInstance(MethodDescriptor.ofConstructor(HashMap.class)));
ResultHandle map = clinit.readStaticField(properties);
MethodDescriptor put = MethodDescriptor.ofMethod(Map.class, "put", Object.class, Object.class, Object.class);
for (Map.Entry<String, String> entry : defaults.entrySet()) {
clinit.invokeInterfaceMethod(put, map, clinit.load(entry.getKey()), clinit.load(entry.getValue()));
}
clinit.returnVoid();

is trying to create a method, that has one function call, for each entry in the Map.

With ~5100 items here, each items take 13 bytes, it could easy go beyond Java's 64KB method size limit.

Output of uname -a or ver

Linux [REDACTED] 5.15.0-1041-azure #48-Ubuntu SMP Tue Jun 20 20:34:08 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

Output of java -version

openjdk 17.0.8 2023-07-18 LTS

GraalVM version (if different from Java)

No response

Quarkus version or git rev

3.2.5.Final

Build tool (ie. output of mvnw --version or gradlew --version)

No response

Additional information

I first raise this issue in keycloak repo, as I have this issue when using keycloak:

keycloak/keycloak#23647

The keycloak team member, @shawkins , suggest that this section is related to how ConfigGenerationBuildStep.generateDefaultsConfigSource may attempt to fill in all the environmental variable in ConfigGenerationBuildStep.runtimeDefaultsConfig:

defaults.putAll(configItem.getReadResult().getRunTimeDefaultValues());


I currently have a bypass to avoid extra useless environmental variable from causing issue to quarkus:

echo "Number of environment variable before deletion: $(env | wc -l)"
env_list=($(env | grep "SOMETHING_FOR_REMOVAL"))
for var_value in "${env_list[@]}"
do
    var=${var_value%%=*}
    unset $var
done
echo "Number of environment variable after deletion: $(env | wc -l)"

but this may not be the best solution

@ReluXingZeng ReluXingZeng added the kind/bug Something isn't working label Oct 10, 2023
@radcortez
Copy link
Member

#35210 fixes this added in Quarkus 3.3.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/config kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants