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

Enable Hibernate ORM multitenancy IT on CI #9390

Merged
merged 5 commits into from
May 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/ci-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ on:
env:
# Workaround testsuite locale issue
LANG: en_US.UTF-8
NATIVE_TEST_MAVEN_OPTS: "-B --settings .github/mvn-settings.xml --fail-at-end -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-native-image:19.3.1-java11 -Dtest-postgresql -Dtest-elasticsearch -Dtest-keycloak -Dtest-amazon-services -Dtest-mysql -Dtest-mariadb -Dmariadb.url='jdbc:mariadb://localhost:3308/hibernate_orm_test' -Dtest-mssql -Dtest-vault -Dtest-neo4j -Dtest-kafka -Dnative-image.xmx=5g -Dnative -Dformat.skip install"
JVM_TEST_MAVEN_OPTS: "-e -B --settings .github/mvn-settings.xml -Dtest-postgresql -Dtest-elasticsearch -Dtest-mysql -Dtest-mariadb -Dmariadb.url='jdbc:mariadb://localhost:3308/hibernate_orm_test' -Dtest-mssql -Dtest-amazon-services -Dtest-vault -Dtest-neo4j -Dtest-kafka -Dtest-keycloak -Dformat.skip"
NATIVE_TEST_MAVEN_OPTS: "-B --settings .github/mvn-settings.xml --fail-at-end -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-native-image:19.3.1-java11 -Dtest-postgresql -Dtest-elasticsearch -Dtest-keycloak -Dtest-amazon-services -Dtest-mysql -Dtest-mariadb -Dmariadb.base_url='jdbc:mariadb://localhost:3308' -Dmariadb.url='jdbc:mariadb://localhost:3308/hibernate_orm_test' -Dtest-mssql -Dtest-vault -Dtest-neo4j -Dtest-kafka -Dnative-image.xmx=5g -Dnative -Dformat.skip install"
JVM_TEST_MAVEN_OPTS: "-e -B --settings .github/mvn-settings.xml -Dtest-postgresql -Dtest-elasticsearch -Dtest-mysql -Dtest-mariadb -Dmariadb.base_url='jdbc:mariadb://localhost:3308' -Dmariadb.url='jdbc:mariadb://localhost:3308/hibernate_orm_test' -Dtest-mssql -Dtest-amazon-services -Dtest-vault -Dtest-neo4j -Dtest-kafka -Dtest-keycloak -Dformat.skip"
DB_USER: hibernate_orm_test
DB_PASSWORD: hibernate_orm_test
DB_NAME: hibernate_orm_test
Expand Down Expand Up @@ -144,7 +144,7 @@ jobs:
MYSQL_USER: hibernate_orm_test
MYSQL_PASSWORD: hibernate_orm_test
MYSQL_DATABASE: hibernate_orm_test
MYSQL_RANDOM_ROOT_PASSWORD: true
MYSQL_ROOT_PASSWORD: secret
ports:
- 127.0.0.1:3308:3306
mssql:
Expand Down Expand Up @@ -320,6 +320,7 @@ jobs:
jpa-mssql
jpa-derby
jpa-without-entity
hibernate-tenancy
- category: Data2
mysql: "true"
postgres: "true"
Expand Down Expand Up @@ -476,7 +477,7 @@ jobs:
- name: Maria DB Service
run: |
docker run --rm --publish 3308:3306 --name build-mariadb \
-e MYSQL_USER=$DB_USER -e MYSQL_PASSWORD=$DB_PASSWORD -e MYSQL_DATABASE=$DB_NAME -e MYSQL_RANDOM_ROOT_PASSWORD=true \
-e MYSQL_USER=$DB_USER -e MYSQL_PASSWORD=$DB_PASSWORD -e MYSQL_DATABASE=$DB_NAME -e MYSQL_ROOT_PASSWORD=secret \
-d mariadb:10.4
if: matrix.mariadb
- name: MS-SQL Service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ private static AgroalDataSource tenantDataSource(JPAConfig jpaConfig, String ten

private static class TenantConnectionProvider extends QuarkusConnectionProvider {

private static final long serialVersionUID = 1L;

private final String tenantId;

public TenantConnectionProvider(String tenantId, AgroalDataSource dataSource) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* @author Michael Schnell
*
*/
public class HibernateCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
public final class HibernateCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver {

private static final Logger LOG = Logger.getLogger(HibernateCurrentTenantIdentifierResolver.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* @author Michael Schnell
*
*/
public class HibernateMultiTenantConnectionProvider extends AbstractMultiTenantConnectionProvider {
public final class HibernateMultiTenantConnectionProvider extends AbstractMultiTenantConnectionProvider {

private static final Logger LOG = Logger.getLogger(HibernateMultiTenantConnectionProvider.class);

Expand All @@ -33,12 +33,14 @@ protected ConnectionProvider getAnyConnectionProvider() {
}

@Override
protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) {
protected ConnectionProvider selectConnectionProvider(final String tenantIdentifier) {
LOG.debugv("selectConnectionProvider({0})", tenantIdentifier);

ConnectionProvider provider = providerMap.get(tenantIdentifier);
if (provider == null) {
return providerMap.computeIfAbsent(tenantIdentifier, tid -> resolveConnectionProvider(tid));
final ConnectionProvider connectionProvider = resolveConnectionProvider(tenantIdentifier);
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure you want to do this? It could introduce a race condition, because multiple parallel requests will use the same object. I think this is not thread safe.

Copy link
Member Author

Choose a reason for hiding this comment

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

It seems fine to me - this is indeed a race condition but it's benign, as the end result doesn't change.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, this is correct. I just thought the "computeIfAbsent" is a more accurate usage of the API and does not cause such questions, because it is defined by the API of the hash map implementation.

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 agree with you - your solution was more elegant. But we try to avoid lambdas for efficiency reasons. Of course we're not extreme about it: lambdas are a fine solution when they are a good solution, but I still tend to prefer avoiding them in such situations.

providerMap.put(tenantIdentifier, connectionProvider);
return connectionProvider;
}
return provider;

Expand Down
10 changes: 9 additions & 1 deletion integration-tests/hibernate-tenancy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,22 @@ You can then run the tests as follows (either with `-Dnative` or not):
mvn clean install -Dtest-mariadb
```

If you have specific requirements, you can define a specific connection URL with `-Djdbc:mariadb://localhost:3306/hibernate_orm_test`.
If you have specific requirements, you can define a specific connection URL with `-Dmariadb.base_url=jdbc:mariadb://...`.
Note that this specific integration test module module requires permissions to create additional users and databases, hence the `mariadb.base_url` variable
should not include the database name: check the `application.properties` to see how it's used.

To run the MariaDB server "manually" via command line for testing, the following command line could be useful:

```
docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name quarkus_test_mariadb -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=secret -p 3306:3306 mariadb:10.4
```

or if you prefer podman, this won't need root permissions:

```
podman run --rm=true --net=host --memory-swappiness=0 --tmpfs /var/lib/mysql:rw --tmpfs /var/log:rw --name mariadb_demo -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=secret -p 3306:3306 mariadb:10.4
```

N.B. it takes a while for MariaDB to be actually booted and accepting connections.

After it's fully booted, you can run all integration tests via
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/hibernate-tenancy/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<description>Module that contains Hibernate Multitenancy related tests running with the MariaDB database</description>

<properties>
<mariadb.url>jdbc:mariadb://localhost:3306</mariadb.url>
<mariadb.base_url>jdbc:mariadb://localhost:3306</mariadb.base_url>
<mariadb.image>mariadb:10.4</mariadb.image>
</properties>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
# Hibernate ORM settings
quarkus.hibernate-orm.database.generation=none
quarkus.hibernate-orm.multitenant=DATABASE
quarkus.hibernate-orm.validate-tenant-in-current-sessions=false

# Maria DB URL
mariadb.url=jdbc:mariadb://localhost:3306

# Default DB Configuration
quarkus.datasource.db-kind=mariadb
quarkus.datasource.username=root
quarkus.datasource.password=secret
quarkus.datasource.jdbc.url=${mariadb.url}/hibernate_orm_test
quarkus.datasource.jdbc.url=${mariadb.base_url}/hibernate_orm_test
quarkus.datasource.jdbc.max-size=1
quarkus.datasource.jdbc.min-size=1
quarkus.flyway.migrate-at-start=true
#Reset Flyway metadata at boot, as the database might have been tainted by previous integration tests:
quarkus.flyway.clean-at-start=true
quarkus.flyway.locations=classpath:database/default

# DATABASE Tenant 'base' Configuration
quarkus.datasource.base.db-kind=mariadb
quarkus.datasource.base.username=jane
quarkus.datasource.base.password=abc
quarkus.datasource.base.jdbc.url=${mariadb.url}/base
quarkus.datasource.base.jdbc.url=${mariadb.base_url}/base
quarkus.datasource.base.jdbc.max-size=1
quarkus.datasource.base.jdbc.min-size=1
quarkus.flyway.base.migrate-at-start=true
quarkus.flyway.base.clean-at-start=true
quarkus.flyway.base.locations=classpath:database/base

# DATABASE Tenant 'mycompany' Configuration
quarkus.datasource.mycompany.db-kind=mariadb
quarkus.datasource.mycompany.username=john
quarkus.datasource.mycompany.password=def
quarkus.datasource.mycompany.jdbc.url=${mariadb.url}/mycompany
quarkus.datasource.mycompany.jdbc.url=${mariadb.base_url}/mycompany
quarkus.datasource.mycompany.jdbc.max-size=1
quarkus.datasource.mycompany.jdbc.min-size=1
quarkus.flyway.mycompany.migrate-at-start=true
quarkus.flyway.mycompany.clean-at-start=true
quarkus.flyway.mycompany.locations=classpath:database/mycompany

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
CREATE DATABASE base;
CREATE USER 'jane'@'%' IDENTIFIED BY 'abc';
CREATE DATABASE IF NOT EXISTS base;
CREATE USER IF NOT EXISTS 'jane'@'%' IDENTIFIED BY 'abc';
GRANT ALL privileges ON base.* TO 'jane'@'%';

CREATE DATABASE mycompany;
CREATE USER 'john'@'%' IDENTIFIED BY 'def';
CREATE DATABASE IF NOT EXISTS mycompany;
CREATE USER IF NOT EXISTS 'john'@'%' IDENTIFIED BY 'def';
GRANT ALL privileges ON mycompany.* TO 'john'@'%';

FLUSH PRIVILEGES;
6 changes: 6 additions & 0 deletions integration-tests/jpa-mariadb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ To run the MariaDB server "manually" via command line for testing, the following
docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name quarkus_test_mariadb -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_RANDOM_ROOT_PASSWORD=true -p 3306:3306 mariadb:10.4
```

or if you prefer podman, this won't need root permissions:

```
podman run --rm=true --net=host --memory-swappiness=0 --tmpfs /var/lib/mysql:rw --tmpfs /var/log:rw --name mariadb_demo -e MYSQL_USER=hibernate_orm_test -e MYSQL_PASSWORD=hibernate_orm_test -e MYSQL_DATABASE=hibernate_orm_test -e MYSQL_ROOT_PASSWORD=secret -p 3306:3306 mariadb:10.4
```

N.B. it takes a while for MariaDB to be actually booted and accepting connections.

After it's fully booted, you can run all integration tests via
Expand Down