Skip to content

Instantly share code, notes, and snippets.

@SIMULATAN
Last active March 31, 2024 20:16
Show Gist options
  • Save SIMULATAN/f2575b70a22fb9da0feb4e491237381b to your computer and use it in GitHub Desktop.
Save SIMULATAN/f2575b70a22fb9da0feb4e491237381b to your computer and use it in GitHub Desktop.
quarkus sucks, here's why

Introduction

Yes, I know, a lot of these problems may not be caused by quarkus but rather other tools in the stack. However, I still cound them here since quarkus makes it extra hard to fix them.

Problems

reactive everything

Reactive as a concept sounds quite appealing. Yay, performance, am i right? In practice however, this rarely is worth the hassle. Just look at this abomination of a function.

And, unsurprisingly, I'm not the only one with this opinion! So, yeah, if you're just starting out, DO NOT use reactive programming (at least for databases, RESTEasy reactive is probably fine). Just use virtual threads.

deserializing an entity doesn't work when only one field is present

however, it is fixed when adding another field.

Epic fetching problems

  • "show more" button for quarkus.hibernate-orm.database.generation doesn't do anything
  • FetchType.EAGER fucks up application as it tries to acquire a JDBC connection despite using the reactive driver
  • fetching throws "unsaved transient instance" error..?
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(nullable = false)
    var owner: Person = Person(),
    @ManyToOne(fetch = FetchType.EAGER, optional = false)
    var contest: Contest = Contest(),
    @ManyToMany(fetch = FetchType.EAGER)
    var members: MutableSet<Person> = mutableSetOf()

=> works fine

then, changing the FetchType to LAZY for members makes the field null ok, understandable, RIGHT?

well, changing the type to LAZY for contest doesn't make it null, no, it THROWS AN EXCEPTION: object references an unsaved transient instance - save the transient instance before flushing : Contest.organizer -> Person

FINALLY changing owner to EAGER MAKES IT THROW ANOTHER EXCEPTION org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connection [Not using JDBC] [n/a] I DO NOT USE JDBC LMAO

manually triggered validation hangs whole application

Invoking sessionFactory.schemaManager.validateMappedObjects() works on the initial application startup, however, subsequent calls cause the WHOLE APPLICATION to hang entirely. Even the web worker pool is blocked!

kubernetes-config starts kubernetes in dev mode despite only loading maps in prod

For some reason, the devs thought it'd be a great idea to start a whole fucking kubernetes cluster as a devservices when all you tried to do was load some config maps in prod.

~~duplicated kubernetes-config.config-maps and kubernetes.env.configmaps

The docs don't state anything valuable either, should probably paste it in here but cba to do that rn tbh.~~ Turns out they have a different use case. One of them loads them using the kubernetes API and the other one with a native envFrom

neither flyway nor liquibase work with the reactive driver

mostly an upstream issue but still annoying AF for a project that is meant to provide ootb integrations this leads to custom code that needs to be written in order to execute the migrations over a temporary JDBC connection (which in turn requires the JDBC driver for the whole application... yikes)

Kubernetes extension uses wrong metrics port when management interface is used

See quarkusio/quarkus#37904 Essentially, when using the management interface, the prometheus metrics route is exposed there. However, the Kubernetes resource generator doesn't respect that and still provides the main application port, thus causing scrapes to fail.

Not setting the Authorization header bypasses authentication for websockets

HUGE security vulnerability that allows you to bypass any type of authentication / authorization entirely by just omitting the Authorization header. With the header, it correctly yeets you, without, quarkus shits itself and doesn't actually disconnect you. Originally came up in June of 2022 and is still a problem as of very late 2023. How the fuck? See quarkusio/quarkus#13565

Shitty Java 21 support

It took me several hours to setup Java 21 support for quarkus. It took them AGES to officially support it. Required me to manually set the quarkus.jib.base-jvm-image, override the kotlin BOM and manually depend on the kotlin stdlib.

Outdated Postgres version

The postgres devservice is using version 14... That $hit came out back in 2021... For a "revolutionary" and "modern" framework, this is a no-go.

"Amazingly fast boot time, incredibly low RSS memory (not just heap size!) offering near instant scale up and high density memory utilization in container orchestration platforms like Kubernetes."

image image this is a VERY SIMPLE application with ZERO handled requests btw...

"Zero config, live reload in the blink of an eye and streamlined code for the 80% common usages, flexible for the remainder 20%."

yeah, about that zero config... actually, even to JUST USE HIBERNATE REACTIVE, MANUALLY DISABLING JDBC IN THE PROPERTIES IS REQUIRED. This in itself is a huge problem and perfectly represents what's wrong with the whole framework.

hibernate reactive doesn't work per default or something

honestly, can't remember the exact issue, but i did find this cool screenshot: image I think I fixed it by setting quarkus.datasource.jdbc=false

all-open plugin doesn't include entity classes

See quarkusio/quarkus#36638 and quarkusio/quarkus-quickstarts#1343 This LITERALLY BROKE projects downloaded from the OFFICIAL BOOTSTRAP SITE. You seriously can't make this shit up

zero logging in prod

Just to see an error message, I had to use a tracing tool to dig into the traces generated (which, frankly, did generate reasonably well, although the error message was cut off causing me to MISS THE WHOLE FUCKING REASON). Without this tool, I would be absolutely SCREWED as I wouldn't have been able to find out that I misconfigured the hostname of the datasource... How is this shit not logged??

kotlin compiler plugin not applied in dev mode

See quarkusio/quarkus#37109

more JDBC issues

image i still DO NOT use JDBC... wtf? also thanks for the absolutely helpful stacktrace that doesn't contain a single class of mine apparently this is caused by using Uni.combine().all()

terrible error messages

image again, sometimes an upstream issue. still, quarkus' error messages often times don't help you debug problems AT ALL

using enviroment variables in config values causes a cryptic runtime crash

After exporting my application and deploying it, the pod crashed instantly on startup. The full stack trace:

Caused by: java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "org.eclipse.microprofile.config.ConfigValue.getValue()" is null
	at io.quarkus.runtime.configuration.ConfigRecorder.handleConfigChange(ConfigRecorder.java:45)
	at io.quarkus.deployment.steps.ConfigGenerationBuildStep$checkForBuildTimeConfigChange1532146938.deploy_22(Unknown Source)
	at io.quarkus.deployment.steps.ConfigGenerationBuildStep$checkForBuildTimeConfigChange1532146938.deploy(Unknown Source)
	... 11 more

wow, incredibly helpful!

To debug this issue further (as there were no logs either), I attached my debugger to the remote instance (as it worked fine in dev). When the exception was thrown, the breakpoint triggered and I found this: IntelliJ Debugger showing the problems (result is currentValue) Turns out that quarkus sets build-time properties to null when evaluation doesn't work (as the environment variables obviously aren't set at build time) BUT fails to check for those at runtime.

Flyway enabled is a build-time config value

This breaks the flyway job on kubernetes (even when 1:1 following the guide!) See Issue #37040 I've since gotten rid of it as it just introduced problems and, due to a separate bug (being that the kubernetes extension uses the wrong config key), it didn't even do any good anyway.

EntityManager#merge with 0 and null doesn't update anything

When updating an existing entity with two fields and the values 0 and null, nothing actually is changed. Setting the first to 1 works, setting the second to something nonnull works too. Just this combination is flawed. Probably some shitty "performance optimization" that fails miserably. This is exactly why i make my own shit: because it just works and doesn't have these incredibly weird problems

@ObservesAsync doesn't fire

fun onStartup(@ObservesAsync startupEvent: StartupEvent) {
	// never called
}

fun onStartup(@Observes startupEvent: StartupEvent) {
	// this works though!
}
@SIMULATAN
Copy link
Author

Inb4 anyone complains about the swearing;
image

@funkrusher
Copy link

funkrusher commented Mar 29, 2024

i liked reading the article!
i still think quarkus is not alone to blame... hibernate is overhyped, but sadly quarkus (or everyone else?) seems to love hibernate (how can you love it), otherwise they would not present it on a silver tablet, like its the best thing ever.

@SIMULATAN
Copy link
Author

i still think quarkus is not alone to blame... hibernate is overhyped, but sadly quarkus (or everyone else?) seems to love hibernate (how can you love it), otherwise they would not present it on a silver tablet, like its the best thing ever.

That is a great point.
"A toolkit is only as good as the tools it consists of" ~ me, just now

Quarkus seems to be a highly opinionated framework, for ex. for Databases, you don't really have a choice on what to use. I mean, sure, you could try to manually use JetBrains Exposed etc. and attempt to get the configurations from quarkus, but do you really want to do that?
I know I probably wouldn't as past attempts to integrate anything with quarkus (even the official supported tools!) were rather painful. Had to create a startup hook reading the raw configuration parameters manually just so I can set the global S3 authentication..

Coming back to hibernate, I think that just using another JPA implementation wouldn't solve it either as some of the problems I have with it stem from the very specification (mostly the query language)

I'm curious, what is your favorite ORM / Hibernate replacement?

@funkrusher
Copy link

funkrusher commented Mar 31, 2024

@SIMULATAN i had success in my company for 10 years with using jOOQ instead of ORM / Hibernate. I feel fortunate to be able to have used it, because it seems now that im back on the job market, that frameworks like JPA / Hibernate are (strangely) the norm. I now started to try using Hibernate but i struggeled to even get Many-To-Many relationships to work. Sometimes the annotations do what they should and sometimes not, and it is just unclear, why something works and why not. I can really recommend using a framework like jOOQ that is very close to SQL, because you will be able to use the full power of what your database can give you. If you like you can check out my little demo project, where i use jOOQ in a quarkus project (and also: as soon as you get yourself accomodated to the workflow of using the jooq-codegen, and using database-migrations like liquibase/flyway together with the codegen, afterwards everything just clicks, and is very simple to use).

what keeps me hyped for quarkus are the very fast (promised) response-times and speed. In the past i worked in a team where we build our own endpoints and it was nice... we were able to optimize what we really need perfectly and were not restricted by a framework like quarkus or spring, but it took some work to set it up, and in typical development-teams this time is often not given, so such frameworks are chosen instead, where the developer soon finds himself behindered by the shortcomings of the frameworks.

@funkrusher
Copy link

@SIMULATAN what im currently missing in Quarkus is AssistedInject Annotation, that Google Guice Dependency Injection Framework supported. The Quarkus team has its own DI Framework/Thing called ArC. It is easy to use and has some helpful stuff and annotations, but it seems they are not thinking that someone would need to Build Factories that contain of Injections and Constructor Parameters that are not injected, because they do not need that for their typical Hibernate/Panache App i guess, so they never miss those. I think it is a wrong choice by the quarkus team to push panache so much, because they do not seem to realize that talking with the Database in SQL is simply the best, and every Wizard-Stuff Frameworks like Hibernate or Panache do, will only make the developers unable to solve problems for themselve, as they obscure them.

@SIMULATAN
Copy link
Author

@funkrusher ah, jOOQ seems quite natural and nice! I too love things close to SQL, especially as I already got to learn the powers of processing and low level queries with just SQL alone. Hence, I really enjoy JetBrains Exposed which gives you a similar developer experience as jOOQ, just using Kotlin's infix functions (basically, you can execute methods like this: myColumn eq true and it'll build that to SQL, basically 1:1, without any overly bloated queries like hibernate does, you just query exactly what you need without expensive mappings).
CompSciGuy made a great video on this: https://www.youtube.com/watch?v=tbfKZy7Y1pc
(Don't let my comment fool you, I was mainly referring to low-level abstractions that more or less just build SQL queries from exactly what you specified, as jOOQ and exposed)
Writing raw SQL would be nice and I think I'd probably enjoy it. However, that introduces the problem of mapping entities manually and, most importantly, safety and having to use a specific database (like when using postgres only types)

I think quarkus' popularity is mainly due to microservices: teams don't want to spend hours programming a base as it is, especially in Java, really tedious and requires people to document everything they can. Also, to be fair, the basics are really, really fast to program. Simple CRUD app with some nice integrations with keycloak etc? Give it a few minutes, done. It only gets complicated once you actually start to use it 😂

Regarding the speed of quarkus: I honestly have no clue how they even get remotely where they claim to be. I really want to conduct benchmarks comparing it with, say, Ktor. Weirdly enough, in several benchmarks, quarkus (or vert.x) seem to take the lead.. I'd guess this is mostly due to the high levels of optimization these technologies went through (the underlying code is sometimes just batshit insane and unreadable) but it still seems very odd considering the insane amount of bloat and unnecessary work these frameworks due. Like, in what world would a bloated web framework spamming objects and loading whole entities and mapping them to java objects outpace a low-level framework that does exactly what's necessary to achieve the desired outcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment