Reputation: 113
My Application does Write operations (Create/Update/Delete) on two different databases (Oracle and Cassandra).
I want the write operation to go through in Oracle even if the cassandra cluster goes down.
This is why all my write operations in Cassandra are Asynchronous and will execute in a different thread than that of the Oracle ones.
In my Config file I'm loading my CassandraClusterFactoryBean using the @Lazy Annotation. I've also surrounded it with a try-catch so it doesn't throw an exception there when it isn't able to connect to the cluster.
@Bean
@Lazy
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = null;
try{
cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints(CASSANDRA_CONTACTPOINT);
cluster.setPort(CASSANDRA_PORT);
cluster.setUsername(CASSANDRA_USERNAME);
cluster.setPassword(CASSANDRA_PASSWORD);
}catch(Exception e){
logger.info("Unable to contact Cassandra Cluster.");
}
return cluster;
}
I've created an AsyncCompnent which performs all the write operations for Cassandra Asynchronously.
@Component
public class AsyncComponent {
@Autowired
private OneWalletCassandraRepo oneWalletCassandraRepo;
@Autowired
private OneWalletProfileByCustUserIdRepo oneWalletProfileByCustUserIdRepo;
@Autowired
private OneWalletProfileByBillingCanRepo oneWalletProfileByBillingCanRepo;
public static Logger logger = LogManager.getLogger(AsyncComponent.class);
@Async
public Future<Tonewalletprofile> saveCassandraAsync(Tonewalletprofile cassandraProfile) throws InterruptedException {
logger.info("Executing Method asynchronously : " + Thread.currentThread().getName());
Tonewalletprofile savedCassandraProfile=oneWalletCassandraRepo.save(cassandraProfile);
return new AsyncResult<>(savedCassandraProfile);
}
@Async
public Future<Tonewalletprofile> findByPmtAccountRefIdAsync(int pmtAccountRefId) {
logger.info("Executing Method asynchronously : " + Thread.currentThread().getName());
Tonewalletprofile foundCassandraProfile=oneWalletCassandraRepo.findByPmtAccountRefId(pmtAccountRefId);
return new AsyncResult<>(foundCassandraProfile);
}
@Async
public Future<List<TonewalletprofileByCustUserId>> findByCustUserIdAsync(String custUserId) {
logger.info("Executing Method asynchronously : " + Thread.currentThread().getName());
List<TonewalletprofileByCustUserId> foundByCustUserId=oneWalletProfileByCustUserIdRepo.findByCustUserId(custUserId);
return new AsyncResult<>(foundByCustUserId);
}
@Async
public Future<List<TonewalletprofileByBillingCan>> findByBillingAcctNumAsync(String billingAcctNum) {
logger.info("Executing Method asynchronously : " + Thread.currentThread().getName());
List<TonewalletprofileByBillingCan> foundByBillingAcctNum=oneWalletProfileByBillingCanRepo.findByBillingAcctNum(billingAcctNum);
return new AsyncResult<>(foundByBillingAcctNum);
}
@Async
public void deleteCassandraAsync(Tonewalletprofile cassandraProfile) {
logger.info("Executing Method asynchronously : " + Thread.currentThread().getName());
oneWalletCassandraRepo.delete(cassandraProfile);
}
}
I then auto-wire this component in my Service class using the @Lazy Annotation.
For Sanity's sake I've also autowired my Threadpoolexceutor and AsyncExceptionHanlder with @Lazy
@Override
@Lazy
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("AsyncThread-");
executor.initialize();
return executor;
}
@Override
@Lazy
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
When I put the cassandra cluster down, and when I start-up my application it still tries to look for the contact points and it fails. (Click to Expand)
I'm not sure why this happens.
Basically what I want is even if the cluster goes down the Write operation on the Oracle DB should go ahead without a hitch.
Upvotes: 2
Views: 1051
Reputation: 181
You can do this:
Disable the spring data cassandra autoconfigure:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
@SpringBootApplication(exclude = {
CassandraDataAutoConfiguration.class
})
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Create your own CassandraTemplate with @Lazy and its dependencies:
import com.datastax.driver.core.Cluster;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.cassandra.config.CassandraTemplateFactoryBean;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
/**
* @author fabiojose
*/
@Configuration
public class Config {
@Bean
@Lazy
public Cluster cassandra() {
return
Cluster.builder()
.addContactPoint("localhost")
.withoutMetrics()
.withPort(9042)
.build();
}
@Bean
@Lazy
public CassandraMappingContext mappingContext() {
return new CassandraMappingContext();
}
@Bean
@Lazy
public CassandraConverter converter() {
return new MappingCassandraConverter(mappingContext());
}
@Bean
@Lazy
public CassandraTemplateFactoryBean cassandraTemplate(Cluster cassandra,
CassandraConverter converter) {
CassandraTemplateFactoryBean factory = new CassandraTemplateFactoryBean();
factory.setConverter(converter);
factory.setSession(cassandra.newSession());
return factory;
}
}
Upvotes: 3
Reputation: 18119
Spring Data Cassandra components operate on a Session
object that is initialized during startup. Creating an initialized Session
requires remote interaction with Cassandra hosts and therefore you see failures if the hosts are not reachable.
You can spin up your own Session
and lazily initialize it whenever a Session
is requested through org.springframework.data.cassandra.SessionFactory
. Both, CqlTemplate
and CassandraTemplate
, accept SessionFactory
as constructor args.
Upvotes: 2