Quarkus - реактивный режим гибернации и мультиарендность
Я пытаюсь разработать мультитенантное приложение с quarkus, hibernate-reactive и postgres.
Hibernate-reactive поддерживает мультиарендность, позволяя реализовать
ReactiveConnectionPool
:
Определение
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» с вызовами вложенных транзакционных методов.