I have a spring boot application which has multi-schema multi-tenancy implemented. Without multi-tenancy, same API response time was 300-400 ms. But after implementing multi-tenancy, response time bumped to 6-7 seconds (on same server and same schema).
I understand that additional processing is required to read header, switching database based on header etc. But I feel that it should not be 6-7 seconds. Can someone suggest how can I reduce this response time. Below are the classes added for multitenancy
public class TenantAwareRoutingSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return ThreadLocalStorage.getTenantName();
}
}
public class TenantNameInterceptor extends HandlerInterceptorAdapter {
@Value("${schemas.list}")
private String schemasList;
private Gson gson = new Gson();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String tenantName = request.getHeader("tenant-id");
if(StringUtils.isBlank(schemasList)) {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(gson.toJson(new Error("Tenants not initalized...")));
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return false;
}
if(!schemasList.contains(tenantName)) {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(gson.toJson(new Error("User not allowed to access data")));
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return false;
}
ThreadLocalStorage.setTenantName(tenantName);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ThreadLocalStorage.setTenantName(null);
}
@Setter
@Getter
@AllArgsConstructor
public static class Error {
private String message;
}
}
public class ThreadLocalStorage {
private static ThreadLocal<String> tenant = new ThreadLocal<>();
public static void setTenantName(String tenantName) {
tenant.set(tenantName);
}
public static String getTenantName() {
return tenant.get();
}
}
@Configuration
public class AutoDDLConfig
{
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${schemas.list}")
private String schemasList;
@Value("${db.host}")
private String dbHost;
@Bean
public DataSource dataSource()
{
AbstractRoutingDataSource multiDataSource = new TenantAwareRoutingSource();
if (StringUtils.isBlank(schemasList))
{
return multiDataSource;
}
String[] tenants = schemasList.split(",");
Map<Object, Object> targetDataSources = new HashMap<>();
for (String tenant : tenants)
{
System.out.println("####" + tenant);
tenant = tenant.trim();
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver"); // Change here to MySql Driver
dataSource.setSchema(tenant);
dataSource.setUrl("jdbc:mysql://" + dbHost + "/" + tenant
+ "?autoReconnect=true&characterEncoding=utf8&useSSL=false&useTimezone=true&serverTimezone=Asia/Kolkata&useLegacyDatetimeCode=false&allowPublicKeyRetrieval=true");
dataSource.setUsername(username);
dataSource.setPassword(password);
targetDataSources.put(tenant, dataSource);
LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
emfBean.setDataSource(dataSource);
emfBean.setPackagesToScan("com"); // Here mention JPA entity path / u can leave it scans all packages
emfBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
emfBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.default_schema", tenant);
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
emfBean.setJpaPropertyMap(properties);
emfBean.setPersistenceUnitName(dataSource.toString());
emfBean.afterPropertiesSet();
}
multiDataSource.setTargetDataSources(targetDataSources);
multiDataSource.afterPropertiesSet();
return multiDataSource;
}
}
Snippet from application.properties
spring.datasource.username=<<username>>
spring.datasource.password=<<pssword>>
schemas.list=suncitynx,kalpavrish,riddhisiddhi,smartcity,businesspark
db.host=localhost
########## JPA Config ###############
spring.jpa.open-in-view=false
spring.jpa.show-sql=false
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.database=mysql
spring.datasource.initialize=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.jdbc.time_zone = Asia/Kolkata
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
##############Debug Logging#########################
#logging.level.org.springframework=DEBUG
#logging.level.org.hibernate.SQL=DEBUG
#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
######### HIkari Pool ##############
spring.datasource.hikari.maximum-pool-size=20
######### Jackson ############
spring.jackson.serialization.WRITE_ENUMS_USING_TO_STRING=true
spring.jackson.deserialization.READ_ENUMS_USING_TO_STRING=true
spring.jackson.time-zone: Asia/Kolkata
#common request logger
logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
#Multi part file size
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size = 15MB
Are you sure you maintain the connection pool per tenant?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.