Consistent Discovery of external Connections #51105
Replies: 5 comments 16 replies
-
Beta Was this translation helpful? Give feedback.
-
Note Elasticsearch is currently being handled by @marko-bekhta . He's adding support for named clients.
This is the kind of things we should heavily push back against IMO. It's fundamentally incompatible with AOT compilation in particular and moving work to build-time in general. We need a closed world.
This is very interesting. I've been thinking for a while that it's very confusing that e.g. you need to set The devil is in the details though:
Note there are problems with this, most notably the difference between "enable the extension" and "enable the datasource/client". See #42244.
I would be in favor of improving our support for multi-tenancy and customization first. See e.g. #11861. The problem with APIs to create clients is users generally call those APIs at runtime, and supporting that reliably would mean a lot more work and testing around native compilation, because more code would execute at (native) runtime.
+100, I would much prefer
See also #43656. The current solution we have for datasources is great because it exists, but UX isn't great and it's confusing people. We might want to come up with a better UX before generalizing the solution. |
Beta Was this translation helpful? Give feedback.
-
But because
So the issue here is if there is no injection point, you don't know which synthetic bean to keep for the creation? In that case, couldn't we keep both beans and defer to runtime, which one is going to be used by looking into the runtime configuration? |
Beta Was this translation helpful? Give feedback.
-
Certainly. I think we are in agreement that we need to offer a way to enable / disable clients at build time. If we consider something like:
So Obviously, we need to align this behaviour across the board. |
Beta Was this translation helpful? Give feedback.
-
|
Ok, so I think we are all in agreement to support |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
We have many extensions that require connections to external systems. Probably the most popular is JDBC and Datasources, but we have many other extensions that fall in the same category, like Redis, Mongo, Artemis, and many more. When we started, all of these extensions only supported a single (default) client, and we have been slowly adding support for multi-tenancy (which has been requested on many occasions from our users).
To summarize, these extensions conceptually have to do the following:
Unfortunately, all of these extensions do different things and behave differently to accomplish the previous steps. Since the JDBC extension is the most popular and one of the most heavily invested in, let's look at what's happening there:
JDBC Extension
The JDBC Extension is really straightforward. Each named client, including defaut, requires setting a build time configuration
quarkus.datasource.[name].db-kindto discover the names at build time. From this list, we can easily generate the CDI Beans forDataSource, start DevServices, and enable or disable theDataSourceat runtime.Other Extensions
Other extensions do not mandate a build-time property to express the client name. In the case of the Redis and Mongo extensions, they check for specific injection annotations to derive the names. Additionally, Redis, queries runtime configuration names to discover additional clients.
The Artemis extension duplicates the runtime configuration in a build-time mapping to behave a bit like the JDBC extension, but this is far from ideal, because it relies on runtime configuration at build time.
To summarize:
There are also other inconsistencies:
These may not seem like much, but they become increasingly annoying when you have to use each of these extensions. Also, other extensions that I haven't looked at, but fall into the same patterns, may include: ElasticSearch, Infinispan, Kafka, and probably more in the Quarkiverse.
Fundamentally, one big difference between JDBC and other extensions is that you can easily add more clients through configuration, while others may require a code change (by a new client injection). Consider:
In the JDBC extension, I can do this to get as many Datasources as required. Yes, the app still needs a rebuild to discover new clients, since the name is set at build time.
In others extension, take Redis for instance:
This is the only reliable way to do it. Remember, runtime configuration may not be available at build time, and even if it is just for the purpose of extracting the name, we need to drop those patterns because it is not reliable, and we cannot ask users to place such configuration at build time.
Proposal
Ideally, we should follow the same pattern that we already have implemented in the JDBC extension. Discover the names through configuration and use them across the board.
Obviously, this is not feasible, since it would be a big breaking change. We probably need something in the middle, a build-time configuration that we can use to define the client names, while still discovering names in injection points. The final list of names is a merge of both. We have been using
*.enabledto enable / disable features at build time, so maybe this could be a good candidate.We would also remove all lookups to the runtime config to discover additional clients, keeping only the runtime lookups to check if a DevServices is required (this is a whole other topic).
Additionally, ensure that we follow consistent configuration options:
Other things to consider
Where should we create, track, and destroy clients? For instance, Redis does it in the recorder, while Mongo is in a CDI bean. It is not exactly the same because the order and time at which these operations execute may be meaningful. There may be other implications (see next).
Should we offer specific APIs for consumers to create their clients? Yes, it can be done by doing the work yourself, but you are missing all the configuration and other things we do. As an example, Mongo Liquibase requires a separate client connection to run the schema updates, and of course, you want to create that client with the same configuration used by the application client (this is how it works currently). We have seen some users requesting the ability to create clients dynamically at runtime. While injection wouldn't work in these cases, it would make it less painful.
Named clients across extensions use a custom annotation to inject by name:
@DataSouce,@RedisClientName,@MongoClientName. I find it annoying that I have to search for which annotation to use. For instance, Artermins uses an annotation we added in SR Common,@Identifier. While we cannot cut support for the annotations we currently support, I think we could standardize the way we inject named "things".There is really no good way to enforce whatever comes out of this discussion. We can create a couple of shared configuration classes to lock down the structure a bit, but ultimately, we would need to code review all of these to ensure it is consistent across the board.
Related issues:
Beta Was this translation helpful? Give feedback.
All reactions