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

fix(gateway-vertx): use cache for non-shared JDBC client #2570

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

volkflo
Copy link
Member

@volkflo volkflo commented Jul 19, 2023

Description

Adding the default set of apiman jdbc drivers to vert.x. In addition cache non-shared clients (e.g. clients for basic-auth-policy) instead of creating a new once per request.

This is maybe not the perfect solution but I did not wanted to refactor the whole client logic as this may introduce additional bugs/breaking stuff.

Fixes #2569

Checklist

  • I have added a changelog entry.
  • If I've added a new feature/enhancement, I have added documentation for it.
  • I have tested this change manually, as well as as passing tests.

Adding the default set of apiman jdbc drivers to vert.x.
In addition cache non-shared clients (e.g. clients for basic-auth-policy) instead of creating a new once per request.

Closes: #2569
@msavy
Copy link
Member

msavy commented Jul 19, 2023

Thanks. I considered this approach but discounted it for a few reasons:

  1. It increases the size;
  2. It forces a particular JDBC driver version which is difficult for the users to replace/substitute;
  3. It includes drivers which have licenses which are not compatible with the license of the main project;

I've backported a solution for my 3.x customers that allows them to include the JDBC driver externally via a workaround and some additional code, but I am not adding new features to 3.x upstream at this point.

This will be fixed in the upstream properly with Apiman 4 (probably).

The cache change is useful though by itself.

@msavy
Copy link
Member

msavy commented Jul 19, 2023

Maybe a compromise is just to include the Postgres driver for now, but I'm still wary about compatibility issues and forcing a particular driver version.

@cypress
Copy link

cypress bot commented Jul 19, 2023

Passing run #957 ↗︎

0 17 0 0 Flakiness 0

Details:

Merge 5db905b into c441939...
Project: Apiman Commit: 61638cb259 ℹ️
Status: Passed Duration: 01:44 💡
Started: Jul 19, 2023 9:05 AM Ended: Jul 19, 2023 9:07 AM

This comment has been generated by cypress-bot as a result of this project's GitHub integration settings.

@volkflo
Copy link
Member Author

volkflo commented Jul 19, 2023

Thanks for the feedback Marc!
I already thought that the drivers are problematic for the upstream, I just copied them over from the wildfly pom to test everything.

The main problem we encountered was the client that was created again and again.
So the cache was useful in our case. I just tried to use the same libs you used in other places for this.

@msavy
Copy link
Member

msavy commented Jul 19, 2023

Thanks for the feedback Marc! I already thought that the drivers are problematic for the upstream, I just copied them over from the wildfly pom to test everything.

The main problem we encountered was the client that was created again and again. So the cache was useful in our case. I just tried to use the same libs you used in other places for this.

Yeah, the way we get away with it is that we never distribute the project or code with those license-incompatible drivers. They are referenced only for test or development purposes such as generating DDL, etc.

I'm probably going to drop some of them for upstream anyway, which should hopefully fix that issue entirely.

Yet more things for me to consider in Apiman 4 I suppose. I'm not getting to spend as much time on it as I would like, but still trying to push ahead.

@msavy
Copy link
Member

msavy commented Jul 19, 2023

OK, let's split this topic.

I'll commit the cache enhancement separately, and then we can think separately about whether we should include any drivers in 3.x or just wait for Apiman 4 to avoid complicating things.

//private static Map<>
private JDBCClient jdbcClient;
private static final Cache<String, JDBCClient> cache = Caffeine.newBuilder()
.maximumSize(20)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is 20 just a magic number?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I thought it would maybe make scene to make this configurable but not sure if there is really a need for it.
Currently it would cache 20 clients (20 different db configs, I am not sure if we really have the need for more.)
If you would use the same db table for different basic-auth-policy configs it would be always the same client.

@msavy
Copy link
Member

msavy commented Jul 19, 2023

I've thought of a potential solution that's even better than the workaround I use for customers to allow an external JDBC driver, so I'll experiment with that later when I have some time 🤞. Posting this as a reminder to myself.

@volkflo
Copy link
Member Author

volkflo commented Jul 19, 2023

OK, let's split this topic.

I'll commit the cache enhancement separately, and then we can think separately about whether we should include any drivers in 3.x or just wait for Apiman 4 to avoid complicating things.

I think that is a good idea. I can remove the drives from my commit if you want?

@msavy
Copy link
Member

msavy commented Jul 19, 2023

Whichever you prefer, sounds good to me 🙏

@msavy
Copy link
Member

msavy commented Jul 19, 2023

I'm also trying to get rid of the old ES libraries that were dragged in during a contribution a few years ago which has massively bulked out the gateway. That's a WIP for Apiman 4 (usual caveats apply for time and money, etc).

@volkflo
Copy link
Member Author

volkflo commented Jul 19, 2023

Whichever you prefer, sounds good to me pray

Okay I will do it and directly add the changelog here as well :)

@msavy msavy changed the title fix(gateway-vertx): add jdbc driver to vert.x gateway fix(gateway-vertx): use cache for non-shared JDBC client Jul 19, 2023
@msavy
Copy link
Member

msavy commented Jul 19, 2023

Okay, related thought... Is there any reason not to use a Vert.x shared connection pool any more? Can we generate a key for the Vert.x shared connection instead of caching our own?

Thinking out loud here.

@msavy
Copy link
Member

msavy commented Jul 19, 2023

For example, if we are not sure we can reliably create a key that will guarantee we don't mix up the wrong clients (which could be a problem with this enhancement anyway I guess).

@msavy
Copy link
Member

msavy commented Jul 19, 2023

Also, should users of the JDBC client not be closing the connection themselves? Do we not make that available to them.

@volkflo
Copy link
Member Author

volkflo commented Jul 19, 2023

Mhh you may be right about that, my vert.x knowledge is not that deep.
We could do it in the same way like we do it here:

jdbcClient = JDBCClient.createShared(vertx, parseConfig(config), dsName);

I was not totally sure if we really need a new instance of the client all the time:

Subsequent calls will return a new client instance that uses the same data source, so the configuration won’t be used.
https://vertx.io/docs/3.9.16/vertx-jdbc-client/java/#_using_default_shared_data_source

@volkflo
Copy link
Member Author

volkflo commented Jul 19, 2023

Also, should users of the JDBC client not be closing the connection themselves? Do we not make that available to them.

Currently I cannot see in the logs that a client is close.
Only when the close is called I see the following log:

 HikariDataSource - HikariPool-1 - Shutdown completed.

@msavy
Copy link
Member

msavy commented Jul 19, 2023

Yeah, this requires a careful look because if you get the cache key wrong, then you may end up mixing together unrelated SQL calls and that will break things in extremely confusing ways.

I recall I avoided implementing caching at the time for that reason. That's why the comment references "jsonConfigs" as I was trying to figure out what needed to be included in the cache key to guarantee uniqueness.

It might actually be easier to change the code calling this impl, not sure...

I will try to look soon.

@volkflo
Copy link
Member Author

volkflo commented Jul 19, 2023

I checked that as well and it seems to work as well.
The only things is that we would also need to provide a unique key (even for the shared).

Without a dsName it uses a default name (which than would return the same client for all configs)

static JDBCClient createShared(Vertx vertx, JsonObject config) {
        return new JDBCClientImpl(vertx, config, "DEFAULT_DS");
}

@msavy
Copy link
Member

msavy commented Jul 19, 2023

Question - why is the calling code not setting a dsName of their own, to ensure it is shared. From a quick review, it seems we're just doing that out of precaution that we're not sure what the correct cache key is (same problem again!).

Couple of thoughts:

Wonder if we could just set a hashcode and equals function in JdbcOptionsBean and use that as the safest possible cache key, and always use a shared client? We'd need to ensure we account for unlikely hashcode collisions in our naming I suppose, but we could do something like...

Map<JdbcOptionsBean, JdbcConnection>  CACHE... 

JDBCClient.createShared(<usual options>, "apiman-" + RandomUUID); <-- we'll manage the mapping ourselves via the cache/map above, so key doesn't matter and we can avoid collision issues

In worst case it would end up creating more pools than needed if people pass in lots of different configuration permutations, but it should avoid ever accidentally getting caught out (e.g. user passes an extra option that means it's no longer compatible with the simple jdbcUrl+username cache key scheme).

Thoughts?

@volkflo
Copy link
Member Author

volkflo commented Jul 19, 2023

I think if we would use the complete config as cache key then we would´t need a shared client anyway?
Then we could use a normal client (which ensures its created with a unique id).

Only question is what happens with all the clients in the map? There would be no limit of different clients then?
Not sure if this could cause problems?


I think if we would create a unique dsName already here, it would be solved as well:

  public IJdbcClient createStandalone(JdbcOptionsBean config) {
        return new VertxJdbcClientImpl(vertx, config);
    } 

@msavy
Copy link
Member

msavy commented Jul 19, 2023 via email

@msavy
Copy link
Member

msavy commented Jul 19, 2023 via email

@volkflo
Copy link
Member Author

volkflo commented Jul 19, 2023

Ah, I did not explained it in a good way.
I thought about a combination.
Maybe its not a good idea just thinking loud :D

  1. implement hashCode/equlas
  2. use the hashCode as unique dsName

So we would create a shared client which is still unique and vertx would handle it for us.
Then we may do not even need an additional Map<config,clients>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Vert.x Basic Authentication JDBC - Driver missing and pooling problems
2 participants