Quarkus - реактивный режим гибернации и мультиарендность

Я пытаюсь разработать мультитенантное приложение с quarkus, hibernate-reactive и postgres.

Hibernate-reactive поддерживает мультиарендность, позволяя реализовать ReactiveConnectionPool :

http://hibernate.org/reactive/documentation/1.0/reference/html_single/#_custom_connection_management_and_multitenancy

Определение hibernate.vertx.pool.classв application.properties, похоже, игнорируется.

Интегрирована ли эта функция hibernate-reactive в quarkus?

Кто-нибудь раньше пользовался этой функцией?

2 ответа

Решение

Мультиарендность для Hibernate Reactive еще не интегрирована в Quarkus.

Я только что создал для него проблему: https://github.com/quarkusio/quarkus/issues/15959

Он не поддерживается на довольно глубоком уровне, поэтому никак (или, по крайней мере, я не смог его найти) как-то добавить его, установив собственное расширение.

То, что я сделал, это маленькая пакость - мое использование@ReactiveTransactionalс обычаем@MyAppTransactional(называйте, как хотите) перехватчик.

Это решение очень специфично для Postgres и использует метод многопользовательской схемы.

      @Interceptor
@MyAppTransactional
// right after @ReactiveTransactional
@Priority(Interceptor.Priority.PLATFORM_BEFORE + 200 + 1)
@RequiredArgsConstructor
class MyAppTransactionalInterceptor {

    // this is a request-scoped bean that holds my tenant id
    @Inject
    TenantId tenantId;

    @Inject
    Mutiny.Session session;

    @Inject
    Validator validator;

    @Inject
    MultiTenancyConfig multiTenancyConfig;

    @AroundInvoke
    Object intercept(InvocationContext ic) throws Exception {
        if (!multiTenancyConfig.enabled()) {
            return ic.proceed();
        }

        Class<?> returnType = ic.getMethod().getReturnType();

        if (returnType != Uni.class) {
            throw new RuntimeException("only Uni return types are supported with transactional methods");
        }

        if (!session.isOpen()) {
            // just a sanity check - inheritance from @ReactiveTransactional makes sure that transaction is active by now
            throw new IllegalStateException(
                "unexpected state: Hibernate session is not active in tenant transaction interceptor");
        }

        String sessionTenantId = tenantId.get();
        if (!validator.validate(sessionTenantId).isEmpty()) {
            // double check just in case to avoid potential SQL injection
            // (hint appreciated: how to properly escape postgres string literal here instead?)
            throw new IllegalStateException(
                "unexpected state: Hibernate session is not active in tenant transaction interceptor");
        }

        return session.createNativeQuery("SET LOCAL SCHEMA '" + tenantId.get() + "'")
            .executeUpdate()
            .flatMap((ignore) -> {
                try {
                    return (Uni<?>) ic.proceed();
                } catch (Exception e) {
                    return Uni.createFrom().failure(e);
                }
            });
    }
}

Сама аннотация:

      @InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
@ReactiveTransactional
public @interface MyAppTransactional {

}

Примечание для себя: необходимо улучшить это, чтобы избежать ненужных вызовов «SET SCHEMA» с вызовами вложенных транзакционных методов.

Другие вопросы по тегам