kubernetes-client: Race condition triggered in java-generator for very big schemas

Describe the bug

Using different versions of Java produce different errors with the java-generator

Fabric8 Kubernetes Client version

SNAPSHOT

Steps to reproduce

  • curl -Ls https://raw.githubusercontent.com/istio/istio/master/manifests/charts/base/crds/crd-all.gen.yaml -o istio.yaml
  • sdk use java 11.0.17-tem
  • jbang io.fabric8:java-generator-cli:6.3.1 --add-extra-annotations --source istio.yaml --target ./src

Produces a stack-trace like:

Exception in thread "main" java.lang.AssertionError
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:603)
        at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:678)
        at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:737)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
        at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:661)
        at io.fabric8.java.generator.CRGeneratorRunner.runOnSingleSource(CRGeneratorRunner.java:82)
        at io.fabric8.java.generator.CRGeneratorRunner.run(CRGeneratorRunner.java:66)
        at io.fabric8.java.generator.cli.GenerateJavaSources.run(GenerateJavaSources.java:77)
        at picocli.CommandLine.executeUserObject(CommandLine.java:2026)
        at picocli.CommandLine.executeHelpRequest(CommandLine.java:2012)
        at picocli.CommandLine.executeHelpRequest(CommandLine.java:1983)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2268)
        at picocli.CommandLine$RunLast.execute(CommandLine.java:2417)
        at picocli.CommandLine.execute(CommandLine.java:2170)
        at io.fabric8.java.generator.cli.GenerateJavaSources.main(GenerateJavaSources.java:81)
Caused by: java.lang.AssertionError
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:603)
        at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:678)
        at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:737)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
        at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:661)
        at io.fabric8.java.generator.CRGeneratorRunner.lambda$runOnSingleSource$2(CRGeneratorRunner.java:93)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
        at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
        at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
        at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
        at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Caused by: java.lang.AssertionError: I am not a child of my parent.
        at com.github.javaparser.printer.DefaultPrettyPrinterVisitor.printOrphanCommentsBeforeThisChildNode(DefaultPrettyPrinterVisitor.java:1799)
        at com.github.javaparser.printer.DefaultPrettyPrinterVisitor.visit(DefaultPrettyPrinterVisitor.java:1584)
        at com.github.javaparser.printer.DefaultPrettyPrinterVisitor.visit(DefaultPrettyPrinterVisitor.java:53)
        at com.github.javaparser.ast.expr.NormalAnnotationExpr.accept(NormalAnnotationExpr.java:74)
        at com.github.javaparser.printer.DefaultPrettyPrinterVisitor.printMemberAnnotations(DefaultPrettyPrinterVisitor.java:94)
        at com.github.javaparser.printer.DefaultPrettyPrinterVisitor.visit(DefaultPrettyPrinterVisitor.java:268)
        at com.github.javaparser.printer.DefaultPrettyPrinterVisitor.visit(DefaultPrettyPrinterVisitor.java:53)
        at com.github.javaparser.ast.body.ClassOrInterfaceDeclaration.accept(ClassOrInterfaceDeclaration.java:98)
        at com.github.javaparser.printer.DefaultPrettyPrinterVisitor.visit(DefaultPrettyPrinterVisitor.java:215)
        at com.github.javaparser.printer.DefaultPrettyPrinterVisitor.visit(DefaultPrettyPrinterVisitor.java:53)
        at com.github.javaparser.ast.CompilationUnit.accept(CompilationUnit.java:133)
        at com.github.javaparser.printer.DefaultPrettyPrinter.print(DefaultPrettyPrinter.java:95)
        at com.github.javaparser.ast.Node.toString(Node.java:322)
        at io.fabric8.java.generator.WritableCRCompilationUnit.writeAllJavaClasses(WritableCRCompilationUnit.java:58)
        at io.fabric8.java.generator.CRGeneratorRunner.lambda$runOnSingleSource$1(CRGeneratorRunner.java:93)
        ... 10 more

Additional issues can be experienced by mixing and matching Java and kubernetes-client versions, another example stack trace is:

java.lang.ArrayIndexOutOfBoundsException
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:603)
        at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:678)
        at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:737)
        at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
        at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
        at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:661)
        at io.fabric8.java.generator.FileJavaGenerator.runOnSingleSource(FileJavaGenerator.java:87)
        at io.fabric8.java.generator.FileJavaGenerator.run(FileJavaGenerator.java:71)
        at io.fabric8.java.generator.cli.GenerateJavaSources.lambda$run$0(GenerateJavaSources.java:132)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
        at io.fabric8.java.generator.cli.GenerateJavaSources.run(GenerateJavaSources.java:132)
        at picocli.CommandLine.executeUserObject(CommandLine.java:2026)
        at picocli.CommandLine.executeHelpRequest(CommandLine.java:2012)
        at picocli.CommandLine.executeHelpRequest(CommandLine.java:1983)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2268)
        at picocli.CommandLine$RunLast.execute(CommandLine.java:2417)
        at picocli.CommandLine.execute(CommandLine.java:2170)
        at io.fabric8.java.generator.cli.GenerateJavaSources.main(GenerateJavaSources.java:136)
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 8 out of bounds for length 7
        at java.base/java.util.ArrayList.add(ArrayList.java:487)
        at java.base/java.util.ArrayList.add(ArrayList.java:499)
        at com.github.javaparser.ast.Node.setParentNode(Node.java:435)
        at com.github.javaparser.ast.NodeList.setAsParentNodeOf(NodeList.java:559)
        at com.github.javaparser.ast.NodeList.own(NodeList.java:81)
        at com.github.javaparser.ast.NodeList.add(NodeList.java:73)
        at com.github.javaparser.ast.nodeTypes.NodeWithAnnotations.addAnnotation(NodeWithAnnotations.java:59)
        at io.fabric8.java.generator.nodes.JObjectExtraAnnotations.addExtraAnnotations(JObjectExtraAnnotations.java:36)
        at io.fabric8.java.generator.nodes.JObject.generateJava(JObject.java:156)
        at io.fabric8.java.generator.nodes.JObject.generateJava(JObject.java:168)
        at io.fabric8.java.generator.nodes.JArray.generateJava(JArray.java:51)
        at io.fabric8.java.generator.nodes.JObject.generateJava(JObject.java:168)
        at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
        at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
        at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
        at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
        at io.fabric8.java.generator.CRGeneratorRunner.validateAndAggregate(CRGeneratorRunner.java:112)
        at io.fabric8.java.generator.CRGeneratorRunner.generate(CRGeneratorRunner.java:97)
        at io.fabric8.java.generator.FileJavaGenerator.lambda$runOnSingleSource$3(FileJavaGenerator.java:95)
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
        at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
        at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
        at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
        at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)

Expected behavior

The generator correctly run and generate the sources.

Runtime

other (please specify in additional context)

Kubernetes API Server version

1.25.3@latest

Environment

Windows, Linux, macOS

Fabric8 Kubernetes Client Logs

No response

Additional context

No response

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 20 (10 by maintainers)

Most upvoted comments

Thanks to @mariofusco that found out that the race condition is happening on this node incorrectly encoded: https://github.com/fabric8io/kubernetes-client/pull/4849/files#diff-a297bc569285c2dbea4f4c5d02362681ee8ea3be3156d3df20ea6d71b3dbb712R45

Thanks again for the support to everyone involved!

Thanks a lot for this honest explanation @jlerbsc and for staying involved in this issue/discussion, much appreciated!

Tomorrow both me and @mariofusco are probably going to spend a few more hours trying to narrow down this issue. If nothing comes up my proposed action plan would be to:

  • go ahead on top of #4849
  • remove this parallelStream in favor of sequential processing
  • sequentially expand, in memory, the CompilationUnits here directly rendering the String content
  • keep the fully parallel write to disk to not bottleneck on IO | edit: a possible alternative to this would be to make the writing to disk async, trigger it in the sequential loop, and collect the completed futures afterward

This action is surely going to impact the performance, but, at this stage, we should favor the correctness and get back to performance when we see the need.