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

Support multitenancy with multiple persistence units #11772

Merged
merged 2 commits into from Sep 2, 2020

Conversation

gsmet
Copy link
Member

@gsmet gsmet commented Sep 1, 2020

I have to add more tests to fully test it but given the timeframe, it would be nice if @geoand and @machi1990 could have a look to what I did.

/cc @michael-schnell
Michael, I have some questions for you, could you please ping me when you're available? Thanks!

@boring-cyborg boring-cyborg bot added the area/hibernate-orm Hibernate ORM label Sep 1, 2020
* if not set.
*/
@ConfigItem
public Optional<String> multitenantSchemaDatasource;
Copy link
Member Author

Choose a reason for hiding this comment

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

@michael-schnell @Sanne I'm not entirely sure this is useful now that we can define a datasource for a PU. Or in the case of the schema do you want to use another datasource when the tenant is defined?

Choose a reason for hiding this comment

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

AFAICT there is actually zero difference between SCHEMA and DATABASE in Hibernate multitenancy, so I don't think you need two things.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't know about Hibernate ORM but it makes a difference here as things are handled in the Quarkus extension. I'm unclear though if we really want this.

I agree this needs more discussions and thoughts.

Copy link
Contributor

Choose a reason for hiding this comment

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

There might not be a difference within Hibernatate between SCHEMA and DATABASE, but there is one if you need to generate to database schema with Flyway.

AgroalDataSource dataSource = Arc.container().instance(AgroalDataSource.class).get();

if (multiTenancySchemaDataSourceName == null) {
AgroalDataSource dataSource = getDataSource(dataSourceName);
return createFrom(dataSource.getConfiguration());
Copy link
Member Author

Choose a reason for hiding this comment

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

@michael-schnell @Sanne I'm not sure I understand the need to somehow copy the existing datasource?

configurator.addQualifier().annotation(DotNames.NAMED)
.addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done();
configurator.addQualifier().annotation(PersistenceUnit.class)
.addValue("value", persistenceUnitDescriptor.getPersistenceUnitName()).done();
Copy link
Contributor

@geoand geoand Sep 1, 2020

Choose a reason for hiding this comment

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

Shouldn't this be "name"?

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah OK, I thought it was the javax.persistence annotation

Copy link
Member Author

Choose a reason for hiding this comment

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

I decided against it as they are quite confusing.

Copy link
Member Author

Choose a reason for hiding this comment

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

And also I needed it to be a qualifier.

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense

@geoand
Copy link
Contributor

geoand commented Sep 1, 2020

I can't comment on the Hibernate specific stuff, but the Quarkus specific stuff looks great :)

@gavinking
Copy link

gavinking commented Sep 1, 2020

Alright, @Sanne, after looking over this stuff, and because coincidentally I happened to be working on multitenancy in HR yesterday, so I was forced to take a close look at how that worked, I gotta say this looks kinda wrong to me.

If I understand correctly (I may not!) You've added a second layer of Quarkus-specific tenant resolvers and connection providers on top of the resolvers Hibernate already has I think because you want these to be CDI components, rather than being configured by properties. But the code is really nonsimple and essentially introduces new Quarkus-specific APIs that mirror APIs already in Hibernate. (Again, assuming I understand correctly.)

I think this is all just the wrong way to go about it.

First of all, I think Hibernate's multitenancy stuff is almost useless in this context. It doesn't actually really do anything except for allowing you to write code to assign connections to tenant ids. It's essentially reasonable in Hibernate where we don't assume any sort of control over the container or non-container environment, but I don't see how it's reasonable here.
The Hibernate APIs aren't actually contributing any value here, since the user winds up having to write Quarkus-specific code anyway.

(Note tangentially that the whole MultiTenancyStrategy enum is misleading: there's only one strategy that's actually supported: writing code to assign a connection to a tenant id.)

There are significant problems with doing multitenancy at the level of the Hibernate extension, including that any non-Hibernate code doesn't automatically run in the context of the current tenant.

What I think should happen here is that multitenancy should be an aspect of the Quarkus datasource, and it should bypass Hibernate multitenancy entirely.

If we do want to support Hibernate's builtin multitenancy APIs, we should do that in a way that is compatible with existing code that users have, and just let them configure it by properties as they usually would in Hibernate.

@gsmet
Copy link
Member Author

gsmet commented Sep 1, 2020

FWIW, I have absolutely no opinion about how things were done as I was not involved in the initial multitenancy patch.

I just made the existing code work with multiple persistence units.

If we want to rewrite this, it's something for 1.9 and is orthogonal to this PR.

@gavinking
Copy link

Yes @gsmet I know that, I just don't have anywhere else to write this feedback.

geoand
geoand previously requested changes Sep 1, 2020
Copy link
Contributor

@geoand geoand left a comment

Choose a reason for hiding this comment

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

Let's just make sure the latest commit (a test commit) isn't added.

Please dismiss once the commit is removed

@gsmet
Copy link
Member Author

gsmet commented Sep 2, 2020

I'm merging this one as I want it tested as part of CR1.

I suggest we start a more general discussion about multitenancy in the mailing list.

@gsmet gsmet merged commit 2466510 into quarkusio:master Sep 2, 2020
@gsmet gsmet added this to the 1.8.0.CR1 milestone Sep 2, 2020
@michael-schnell
Copy link
Contributor

FWIW, I have absolutely no opinion about how things were done as I was not involved in the initial multitenancy patch.

I just made the existing code work with multiple persistence units.

If we want to rewrite this, it's something for 1.9 and is orthogonal to this PR.

Sorry, was off for some days.

@gsmet You should take a closer look at the history of #8545 where you were explicitly asked to do a review..,

@michael-schnell
Copy link
Contributor

I think because you want these to be CDI components, rather than being configured by properties.

Yes, that was the idea.

But the code is really nonsimple and essentially introduces new Quarkus-specific APIs that mirror APIs already in Hibernate. (Again, assuming I understand correctly.)

Why is it not simple? It is exactly doing what is described in the Hibernate manuals:
https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#multitenacy-separate-database
https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#multitenacy-separate-schema

I think this is all just the wrong way to go about it

Unfortunately the Hibernate Multitenancy feature isn't finalized since years. As stated in the manual "The JPA expert group is in the process of defining multitenancy support for an upcoming version of the specification".

First of all, I think Hibernate's multitenancy stuff is almost useless in this context. It doesn't actually really do anything except for allowing you to write code to assign connections to tenant ids.

What is wong about it?

The Hibernate APIs aren't actually contributing any value here, since the user winds up having to write Quarkus-specific code anyway.

Might be right. Any suggestions to do it better are highly welcome.

(Note tangentially that the whole MultiTenancyStrategy enum is misleading: there's only one strategy that's actually supported: writing code to assign a connection to a tenant id.)

That is not correct. The information is also used to generate the schema with Flyway, which is different for database and schema based multitenany (see Quickstart example)

There are significant problems with doing multitenancy at the level of the Hibernate extension, including that any non-Hibernate code doesn't automatically run in the context of the current tenant.

This would not be any way better if you let the user "configure it by properties as they usually would in Hibernate".

What I think should happen here is that multitenancy should be an aspect of the Quarkus datasource, and it should bypass Hibernate multitenancy entirely.

Please go ahead and make a suggestion on how to do it.

If we do want to support Hibernate's builtin multitenancy APIs, we should do that in a way that is compatible with existing code that users have, and just let them configure it by properties as they usually would in Hibernate.

I think the current solution is very easy for end users. Simply add the resolver and configure your data sources. But I'm open to any suggestions on how to do it better.

@gavinking
Copy link

Sorry, I should have mentioned that I opened #11861 to discuss this idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/hibernate-orm Hibernate ORM
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants