Я использую Spring Boot и Spring Data с Cassandra. При запуске приложения Spring устанавливает соединение с базой данных для настройки схемы и инициализации репозиториев данных Spring. Если база данных недоступна, приложение не запустится.
Я хочу, чтобы приложение просто выдавало ошибку и запускалось. Конечно, я больше не могу использовать репозитории, но другие службы (контроллеры отдыха и т. д.), которые не зависят от базы данных, должны работать. Также было бы неплохо увидеть в проверке работоспособности привода, что cassandra не работает.
Для JDBC существует свойство spring.datasource.continue-on-error
. Я не смог найти что-то подобное для Кассандры.
Я также пытался создать пользовательскую конфигурацию cassandra и пытался поймать исключение при создании CqlSession, но не смог добиться желаемого поведения.
EDIT: как было предложено @adutra, я попытался установить advanced.reconnect-on-init, приложение пытается установить соединение, но оно не полностью инициализировано (например, контроллер REST недоступен)
@Configuration
public class CustomCassandraConfiguration extends CassandraAutoConfiguration {
@Bean
public DriverConfigLoaderBuilderCustomizer driverConfigLoaderBuilderCustomizer() {
return builder -> builder.withBoolean(DefaultDriverOption.RECONNECT_ON_INIT, true);
}
}
РЕДАКТИРОВАТЬ 2: теперь у меня есть рабочий пример (запуск приложения, пользовательская проверка работоспособности для cassandra), но если он кажется довольно уродливым:
CustomCassandraAutoConfiguration
@Configuration
public class CustomCassandraAutoConfiguration extends CassandraAutoConfiguration {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
@Bean
public CqlSession cassandraSession(CqlSessionBuilder cqlSessionBuilder) {
try {
return super.cassandraSession(cqlSessionBuilder);
} catch (AllNodesFailedException e) {
logger.error("Failed to establish the database connection", e);
}
return new DatabaseNotConnectedFakeCqlSession();
}
@Bean
public CassandraReactiveHealthIndicator cassandraHealthIndicator(ReactiveCassandraOperations r, CqlSession session) {
if (session instanceof DatabaseNotConnectedFakeCqlSession) {
return new CassandraReactiveHealthIndicator(r) {
@Override
protected Mono<Health> doHealthCheck(Health.Builder builder) {
return Mono.just(builder.down().withDetail("connection", "was not available on startup").build());
}
};
}
return new CassandraReactiveHealthIndicator(r);
}
}
CustomCassandraDataAutoConfiguration
@Configuration
public class CustomCassandraDataAutoConfiguration extends CassandraDataAutoConfiguration {
public CustomCassandraDataAutoConfiguration(CqlSession session) {
super(session);
}
@Bean
public SessionFactoryFactoryBean cassandraSessionFactory(CqlSession session, Environment environment, CassandraConverter converter) {
SessionFactoryFactoryBean sessionFactoryFactoryBean = super.cassandraSessionFactory(environment, converter);
// Disable schema action if database is not available
if (session instanceof DatabaseNotConnectedFakeCqlSession) {
sessionFactoryFactoryBean.setSchemaAction(SchemaAction.NONE);
}
return sessionFactoryFactoryBean;
}
}
DatabaseNotConnectedFakeCqlSession (Fake session implementation)
public class DatabaseNotConnectedFakeCqlSession implements CqlSession {
@Override
public String getName() {
return null;
}
@Override
public Metadata getMetadata() {
return null;
}
@Override
public boolean isSchemaMetadataEnabled() {
return false;
}
@Override
public CompletionStage<Metadata> setSchemaMetadataEnabled( Boolean newValue) {
return null;
}
@Override
public CompletionStage<Metadata> refreshSchemaAsync() {
return null;
}
@Override
public CompletionStage<Boolean> checkSchemaAgreementAsync() {
return null;
}
@Override
public DriverContext getContext() {
return new DefaultDriverContext(new DefaultDriverConfigLoader(), ProgrammaticArguments.builder().build());
}
@Override
public Optional<CqlIdentifier> getKeyspace() {
return Optional.empty();
}
@Override
public Optional<Metrics> getMetrics() {
return Optional.empty();
}
@Override
public <RequestT extends Request, ResultT> ResultT execute( RequestT request, GenericType<ResultT> resultType) {
return null;
}
@Override
public CompletionStage<Void> closeFuture() {
return null;
}
@Override
public CompletionStage<Void> closeAsync() {
return null;
}
@Override
public CompletionStage<Void> forceCloseAsync() {
return null;
}
@Override
public Metadata refreshSchema() {
return null;
}
}
Какие-либо предложения?