More databases at the same time?
Closed this issue · 4 comments
I wonder whether the spring-boot-starter is designed to allow access to more than one arangodb database.
What I tried was to create two implementations of ArangoConfiguration
interfaces, annotated with @Configuration
and
@EnableArangoRepositories
. Each scanning packages with its own ArangoRepository
.
@Configuration
@EnableArangoRepositories(basePackages = { "my.package.repository.car" })
public class CarArangoConfig implements ArangoConfiguration {
@Bean(name = "carArangoTemplate")
@Override
public ArangoOperations arangoTemplate() throws Exception {
return ArangoConfiguration.super.arangoTemplate();
}
@Override public ArangoDB.Builder arango() { ... }
@Override public String database() { return "cars"; }
}
@Configuration
@EnableArangoRepositories(basePackages = { "my.package.repository.train" })
public class TrainArangoConfig implements ArangoConfiguration {
@Bean(name = "trainArangoTemplate")
@Override
public ArangoOperations arangoTemplate() throws Exception {
return ArangoConfiguration.super.arangoTemplate();
}
@Override public ArangoDB.Builder arango() { ... }
@Override public String database() { return "trains"; }
}
First problem was with @Bean
on ArangoConfiguration.arangoTemplate()
. It made it impossible to instantiate the ArangoConfiguration more than once, giving this nasty exception:
The bean 'arangoTemplate', defined in class path resource ..., could not be registered. A bean with that name has already been defined in class path resource ...
I was forced to clone the ArangoConfiguration
class, and remove the @Bean
annotation.. but it worked.
Second problem was with @Autowired
on ArangoRepositoryFactoryBean.setArangoOperations()
. As there is no option for providing qualifier, it throws this exception:
Parameter 0 of method setArangoOperations in com.arangodb.springframework.repository.ArangoRepositoryFactoryBean required a single bean, but 2 were found:
carArangoTemplate
trainArangoTemplate
Sure I could use @primary to force one in, but it would render both factories using the same arangoTemplate, which would only make one of the databases accessible.
So my question is relatively simple - is there any recommended way to access multiple databases from the same spring boot app?
Thanks in advance for any input :)
The library is designed to deal with a single instance of each bean registered in ArangoConfiguration
, so even if you fix the beans registrations I suspect you will get anyway additional issues.
As a workaround I suggest you using a SPEL expression in the database name, so that it will be evaluated dynamically at each invocation. Eg.:
@Configuration
@EnableArangoRepositories(basePackages = { "my.package.repository" })
public class ArangoConfig implements ArangoConfiguration {
// ...
@Override public String database() {
return "#{databaseNameProvider.getName()}";
}
}
@Component
public class DatabaseNameProvider {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and use it this way:
// ...
@Autowired
ArangoOperations operations;
@Autowired
DatabaseNameProvider databaseNameProvider;
// ...
databaseNameProvider.setName("cars");
// perform operations on cars db
databaseNameProvider.setName("trains");
// perform operations on trains db
Well, it works the way it should :-) Even under parallel load, tried with 20 concurent requests at the time.
It scares me.. as DatabaseNameProvider
is not thread safe in any way, so there exists theoretical possibility of database name being changed by other thread.
// ...
@Autowired
private CarRepository carRepository;
@Autowired
DatabaseNameProvider databaseNameProvider;
// ...
// switch to the right DB
databaseNameProvider.setName("cars");
// ???
// another request changes DB to "trains" in parallel thread
// fire query
carRepository.searchByName("Audi A4")
Is this somehow covered by how ArangoOperations
works under the hood? Do you think any additional steps towards thread security should be made, or it's not necessary?
My suggestion was intended just as a workaround to address your needs, not as a best practise to use the library.
I think you can solve concurrent modifications using request scope for DatabaseNameProvider
or using a thread local variable to store the db name.
As reference for this approach you can look at https://github.com/arangodb/spring-data/blob/df545096e1d478557858327c3420c08da6c65b06/src/test/java/com/arangodb/springframework/core/mapping/MultiTenancyDBLevelMappingTest.java
Tried both @Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS )
and private ThreadLocal<String> String name
in DatabaseNameProvider
component, both solutions worked as expected.
Many thanks Michele!