Steps to configure a Liferay multi-tenancy environment
If you’ve read my previous article Liferay Saas solution – handling multi-tenancy where I describe the principles used to handle a multi-tenancy installation for Liferay, you are probably wondering about the technical details of how to actually configure a Liferay multi-tenancy environment. Well, here is a description of what we did. Hope you find it useful. If you have any questions please leave a comment an I will try to help.
In short these are the modifications made to allow Liferay to lazy connect to the shards and allow a fast start-up no matter the number of shards involved:
- Explicitly set the Hibernate dialect – this is done in the portal-ext.properties file;
- Database connection information and data source settings – dynamically create data sources and make necessary adjustments such that actual connection is delayed as much as possible
- Handling the Hibernate session factory – initialize Hibernate configuration only once, not for each shard
- Prevent start up initializations
- Initializations not made on start up should be made on first access of each shard / instance
- Initializations needed for Control Panel functions
1. Explicitly set the Hibernate dialect
[LIFERAY-HOMEDIR]/portal-ext.properties
This file is actually overwrites the default settings of the Portal. The default Portal settings are stored in the [LIFERAY-HOMEDIR]/tomcat/webapps/ROOT/portal-imp.jar. After following the documentation of Liferay this file looked somthing like
spring.configs=\ META-INF/base-spring.xml,\ \ META-INF/hibernate-spring.xml,\ META-INF/infrastructure-spring.xml,\ META-INF/management-spring.xml,\ \ META-INF/util-spring.xml,\ \ META-INF/jpa-spring.xml,\ \ META-INF/audit-spring.xml,\ META-INF/cluster-spring.xml,\ META-INF/editor-spring.xml,\ #META-INF/jcr-spring.xml,\ portal-shards-jcr.xml,\ META-INF/ldap-spring.xml,\ META-INF/messaging-core-spring.xml,\ META-INF/messaging-misc-spring.xml,\ META-INF/poller-spring.xml,\ META-INF/rules-spring.xml,\ META-INF/scheduler-spring.xml,\ META-INF/scripting-spring.xml,\ META-INF/search-spring.xml,\ META-INF/workflow-spring.xml,\ \ META-INF/counter-spring.xml,\ META-INF/document-library-spring.xml,\ META-INF/mail-spring.xml,\ META-INF/portal-spring.xml,\ META-INF/portlet-container-spring.xml,\ \ META-INF/ext-spring.xml,\ \ portal-shards.xml shard.selector=com.liferay.portal.dao.shard.ManualShardSelector shard.available.names=default,c1,c2,........,c15000 hibernate.dialect=org.hibernate.dialect.MySQLDialect
In this file we have placed the sharding settings according to the Liferay documentation. We have also added the hibernate.dialect explicitly as this was one of the reasons for which the portal tried to connect to each and every shard in order to detect the Hibernate dialect to use.
Also, notice that we have added a reference to portal-shards.xml which is actually a copy of the [LIFERAY-HOMEDIR]/tomcat/webapps/ROOT/WEB-INF/lib/portal-impl.jar/META-INF/shard-data-source-spring.xml. We did this so that we can modify the shards configuration without having to change the content of the jar everytime. Instead now, the portal looks for the file in [LIFERAY-HOMEDIR]/tomcat/webapps/ROOT/WEB-INF/classes/.
To keep things clearer can add this to the file
include-and-override=portal-ext-extra-shards.properties
What this does is indicate to Liferay that it should load the file portal-ext-extra-shards.properties along side the portal-ext.properties. This allows us to separately store settings related to the sharding.
2. Database connection information and data source settings
One important thing to note is that for sharding one needs to add connection information in the portal-ext.properties file in the form of
jdbc.[shard-name].driverClassName=com.mysql.jdbc.Driver jdbc.[shard-name].url=[Connection string] ...
This information is used to create the DataSource objects that the application uses. The DataSource objects are defined in the Spring context, namely in the portal-shards.xml file created as indicated above. This instantiates a few singletons org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy with a target data source set as com.liferay.portal.dao.jdbc.util.DataSourceFactoryBean which in turn has as a parameter named parameterPrefix indicating the prefix used to retrieve the connection information from the portal-ext.properties file. LazyConnectionDataSourceProxy is actually a very useful choice in our situation as this type of data source will actually connect to the database only upon the first creation of a statement. So far we did not change anything from the default configuration, but to support the 15000 clients / portal instances we need to add 15000 connection information groups in the portal-ext.properties file and 15000 Spring beans in the portal-shards.xml file. This can be done by generating these entries with some script, but one thing we chose to do is to aviod overfilling the portal-ext.properties file which is more often used for changing portal configurations and what we did, we chose to replace the default com.liferay.portal.dao.jdbc.util.DataSourceFactoryBean from portal-shards.xml with our own implementation which does not look at the 15000 groups of connection information lines, but dynamically generates connections based on some rules for schema names, database user names and passwords.
In short we created a org.springframework.beans.factory.config.AbstractFactoryBean and put it in place of the com.liferay.portal.dao.jdbc.util.DataSourceFactoryBean. Our class createInstance creates one com.mchange.v2.c3p0.ComboPooledDataSource object for each entry in the Spring context and uses the propertyPrefix value to create the connection information. As we can see we used C3P0 as our connection pool provider as this is the default provider used by the Portal.
IMPORTANT NOTE: Through testing we noted that even though the portal uses LazyConnectionDataSourceProxy which only initializes the connection pool upon first creating a statement, when we started the Portal it was creating connections thus slowing down the start up time. This was because LazyConnectionDataSourceProxy will try to connect on initialization if the defaultAutoCommit and defaultTransactionIsolation properties are not set. So we have set them such.
<bean id="shardDataSource0" lazy-init="true"> <property name="defaultAutoCommit" value="true"/> <property name="defaultTransactionIsolation" value="4"/> <property name="targetDataSource"> <bean lazy-init="true"> <property name="propertyPrefix" value="jdbc.default."/> </bean> </property> </bean>
3. Handling the Hibernate session factory
In the Portal shard configuration we notice that the Hibernate SessionFactory used is com.liferay.portal.dao.shard.ShardSessionFactoryTargetSource which in turn uses a key/value map with one com.liferay.portal.spring.hibernate.PortalHibernateConfiguration object for each shard. The latter is a LocalSessionFactoryBean responsible for the Hibernate configurations and mappings. This is all fine as is, but again, for 15000 it’s not ideal as for every shard the system will load the Hibernate configuration files 15000 times which means a long start up time.
What we did to avoid this is extend the com.liferay.portal.spring.hibernate.PortalHibernateConfiguration and override the newConfiguration method to only create the Configuration object once. This means that the Portal Hibernate mappings files are only loaded once. Through testing we also noted that the Portal still wanted to connect to each shard upon start up and found that it was due to Hibernate that needed to access the database metadata. There is a hibernate property that determines this and that is hibernate.temp.use_jdbc_metadata_defaults which defaults to true. So, we set this property to false in our version of the PortalHibernateConfiguration class.
Also in this PortalHibernateConfiguration we noticed that the buildSessionFactory method was called for each shard so we also made an override of that as the SessionFactory objects created were the same excepting the data source. We therefore created a method that creates a SessionFactory and sets a data source. Please see 6. Initializations needed for Control Panel functions where we used this method.
Here’s how our PortalHibernateConfiguration version looks like
..... extends com.liferay.portal.spring.hibernate.PortalHibernateConfiguration { private static Configuration _config = null; private static SessionFactory _sessionFactory = null; @Override protected Configuration newConfiguration() { if (_config == null) { _config = super.newConfiguration(); _config.getProperties().setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false"); } return _config; } @Override protected SessionFactory buildSessionFactory() throws Exception { if (_sessionFactory == null) { _sessionFactory = super.buildSessionFactory(); } return _sessionFactory; } public SessionFactory buildSessionFactory(DataSource dataSource) throws Exception { this.setDataSource(dataSource); return super.buildSessionFactory(); }
Don’t forget replace all occurrences of com.liferay.portal.spring.hibernate.PortalHibernateConfiguration with your implementation in the portal-shards.xml. Also, place your implementation in a .jar file and drop it in the [LIFERAY-HOMEDIR]/tomcat/webapps/ROOT/WEB-INF/lib folder.
4. Prevent start up initializations
Up to this point we have configured the Liferay Spring context to delay and avoid connecting to all the shards on context initialization with the though in mind that only when a user actually accesses their shard / instance the connection will actually be made.
Also through testing we realized that this was not enough, because the Portal does some initializations upon start up which involve accessing data form all shards. So, we had to find a way to prevent those initializations to take place before the Portal actually finished starting. Step by step, we were able to identify the initialization tasks that were performed on start up and tried to disable them. The idea was actually to disable them on start up, but perform them at a later time – on user access to the shard.
We identified that the initialization of each shard / instance was made inside the com.liferay.portal.servlet.MainServlet servlet on the initCompanies method, so we decided to override this method to only init the main shard. Here’s how our implementation looks like
... extends MainServlet { @Override protected void initCompanies() throws Exception { ServletContext servletContext = getServletContext(); String[] webIds = PortalInstances.getWebIds(); PortalInstances.initCompany(servletContext, webIds[0]); } }
In order for the new servlet to be used you have to replace com.liferay.portal.servlet.MainServlet with your implementation in the [LIFERAY-HOMEDIR]/tomcat/webapps/ROOT/WEB-INF/web.xml file.
If everything was set up right then the Portal should start with this configuration in a reasonable amount of time (for me it takes around 80 seconds).
IMPORTANT NOTE: Though the Portal now starts quickly only the default shard will function properly. Remember that we have deactivated the start up initialization. We still have to initialize each shard upon access.
5. Initializations not made on start up should be made on first access of each shard / instance
In the previous step we have replaced the initialization of all the shards with the initialization of the default shard only. We would now like to have this initialization taking place upon first access of a shard. In order to do that we have decided to put the initialization code inside one of the filters of the Portal ROOT webapp. Namely, we have overridden the processFilter method of the com.liferay.portal.servlet.filters.virtualhost.VirtualHostFilter class. This filter is used to provide virtual host functionality, that is when a user accesses a certain virtual host pointing to the Liferay portal server it will route the user to the correct shard / instance corresponding to the virtual host name. This filter was appropriate for our goals and also it is the first filter in the list of filters giving it priority over the others.
In our implementation of the filter we identify the shard / instance accessed by the name of the virtual host, we call the initialization code for a shard / instance (just like in the original com.liferay.portal.servlet.MainServlet.initCompanies() method). We also store the hosts for which the initialization has been made such that we only initialize one shard upon first access and not every time.
The resulting code for the filter looks something like this
... extends VirtualHostFilter { private static List lstInitializedHosts = new ArrayList(); @Override protected void processFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws Exception { String host = PortalUtil.getHost(request); if (!lstInitializedHosts.contains(host)) { lstInitializedHosts.add(host); Company company = CompanyLocalServiceUtil.getCompanyByVirtualHost(host); PortalInstances.initCompany(request.getSession().getServletContext(), company.getWebId()); } super.processFilter(request, response, filterChain); } }
Again, in order for the new filter to be used you have to replace com.liferay.portal.servlet.filters.virtualhost.VirtualHostFilter with your implementation in the [LIFERAY-HOMEDIR]/tomcat/webapps/ROOT/WEB-INF/web.xml file.
Place your implementation in a .jar file (can be the same as for the servlet) and drop it in the [LIFERAY-HOMEDIR]/tomcat/webapps/ROOT/WEB-INF/lib folder.
With these settings the Portal will start just as quick as with only one shard / instance and upon each access of an existing shard it will create a database connection and then initialize the shard correctly such that everything will work as expected.
6. Initializations needed for Control Panel functions
Even though the Portal shards / instances work correctly, if you go to the Control Panel and try to define a new instance, the Portal will start to give errors. This is because as you remember each instance is only initialized when directly accessed by a user through a virtual host name. So why does it give an error, because in the default shard’s Control Panel things don’t stay local only on the current shard, but the Portal accesses all sorts of other information from other shards. Those shards might not be yet initialized and therefore the error. Actually the error had to do with the fact that the SessionFactory was not correctly built.
To fix this problem we have turned to the Aspect Oriented configuration used by the portal. What this does is wrap around Portal API calls such that it detects to which shard / instance the call refers to and sets creates the conditions for the call to get executed on the correct shard. We decided that this might be a good place to detect if calls to other instances than the default instance are made such that we initialize them before making the call so that the errors do not appear anymore.
We have extended the ShardAdvice class of the portal in this way
... extends ShardAdvice { private static List lstInitializedShards = new ArrayList(); private static ShardSelector _shardSelector; static { try { _shardSelector = (ShardSelector) Class.forName(PropsValues.SHARD_SELECTOR).newInstance(); } catch (Exception e) { } } /** * @param shardName * @throws Exception */ @SuppressWarnings("unchecked") private void buildSessionFactory(String shardName, String methodCall) throws Exception { if (PropsValues.SHARD_DEFAULT_NAME.equals(shardName) || lstInitializedShards.contains(shardName)) { return; } lstInitializedShards.add(shardName); ShardSessionFactoryTargetSource shardSessionFactoryTargetSource = (ShardSessionFactoryTargetSource) PortalBeanLocatorUtil.locate("shardSessionFactoryTargetSource"); Class cls = ShardSessionFactoryTargetSource.class; Field fld = cls.getDeclaredField("_sessionFactories"); fld.setAccessible(true); Map _sessionFactories = (Map) fld .get(shardSessionFactoryTargetSource); int dataSourceIndex = 0; if (!"default".equals(shardName)) { dataSourceIndex = Integer.parseInt(StringUtils.replace(StringUtils.replace(shardName, "c", ""), ".", "")); } DataSource dataSource = (DataSource) PortalBeanLocatorUtil.locate("shardDataSource" + dataSourceIndex); PortalHibernateConfiguration sessionFactory = new PortalHibernateConfiguration(); _sessionFactories.put(shardName, sessionFactory.buildSessionFactory(dataSource)); } @Override public Object invokeByParameter(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { String methodSignature = _getSignature(proceedingJoinPoint); if (!"ResourceCodeLocalServiceImpl.checkResourceCodes()".equals(methodSignature)) { Object[] arguments = proceedingJoinPoint.getArgs(); long companyId = (Long) arguments[0]; Shard shard = ShardLocalServiceUtil.getShard(Company.class.getName(), companyId); String shardName = shard.getName(); buildSessionFactory(shardName, "invokeByParameter: " + methodSignature); } return super.invokeByParameter(proceedingJoinPoint); } @Override public Object invokeCompanyService(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { String methodName = proceedingJoinPoint.getSignature().getName(); Object[] arguments = proceedingJoinPoint.getArgs(); String shardName = PropsValues.SHARD_DEFAULT_NAME; if (methodName.equals("addCompany")) { String webId = (String) arguments[0]; String virtualHost = (String) arguments[1]; String mx = (String) arguments[2]; shardName = (String) arguments[3]; shardName = _getCompanyShardName(webId, virtualHost, mx, shardName); } else if (methodName.equals("checkCompany")) { String webId = (String) arguments[0]; if (!webId.equals(PropsValues.COMPANY_DEFAULT_WEB_ID)) { if (arguments.length == 3) { String mx = (String) arguments[1]; shardName = (String) arguments[2]; shardName = _getCompanyShardName(webId, null, mx, shardName); } try { Company company = CompanyLocalServiceUtil.getCompanyByWebId(webId); shardName = company.getShardName(); } catch (NoSuchCompanyException nsce) { } } } else if (methodName.startsWith("update")) { long companyId = (Long) arguments[0]; Shard shard = ShardLocalServiceUtil.getShard(Company.class.getName(), companyId); shardName = shard.getName(); } buildSessionFactory(shardName, "invokeCompanyService: " + _getSignature(proceedingJoinPoint)); return super.invokeCompanyService(proceedingJoinPoint); } private String _getCompanyShardName(String webId, String virtualHost, String mx, String shardName) { Map shardParams = new HashMap(); shardParams.put("webId", webId); shardParams.put("mx", mx); if (virtualHost != null) { shardParams.put("virtualHost", virtualHost); } shardName = _shardSelector.getShardName(ShardSelector.COMPANY_SCOPE, shardName, shardParams); return shardName; } private String _getSignature(ProceedingJoinPoint proceedingJoinPoint) { String methodName = StringUtil.extractLast(proceedingJoinPoint.getTarget().getClass().getName(), StringPool.PERIOD); methodName += StringPool.PERIOD + proceedingJoinPoint.getSignature().getName() + "()"; return methodName; } }
What the code does is build SessionFactory objects with the right data source and set them in the right place. Please note that the code relies on the presumption that the shards are identified as default, c1, c2, c3, …
Place your implementation in a .jar file (can be the same as for the servlet and filter) and drop it in the [LIFERAY-HOMEDIR]/tomcat/webapps/ROOT/WEB-INF/lib folder.
In order for your implementation to be used by the Portal you have to replace the entry in the portal-shards.xml
replace this
<bean id="com.liferay.portal.dao.shard.ShardAdvice">
with this
<bean id="your.implementation.of.ShardAdvice">
[…] See technical details on how to configure Liferay for multi-tenancy. […]
Nice Article
Hi John,
I have customs portlets and I am using service builder to generate persistence layer. Table are only only created for default jdbc connection in case of custom portlets and other two shard databases are unaffected. What I am missing here?
Hi,
Is your question related to the setup I’m describing in the post? Please note that this setup makes use of lazy connections, so only the connection
to the default database is created at first, so probably Hibernate’s hbm2ddl=auto will not work. Anyway, this setting is highly unadvised for production environment.
I don’t know if you use the service builder for the persistence layer if that’s setup for working with shards. I think you should test your custom portlets on an instance other than the default, and if it gives errors that it cannot find the tables try to create the tables manually in that shard.
For my production environment I’m using liquibase for database source control.
Here is my portal-ext.properties file
###############################
#Liferay Portal-ext.properties
###############################
# MySQL Default Shard Database Connector
jdbc.default.driverClassName=com.mysql.jdbc.Driver
jdbc.default.url=jdbc:mysql://localhost/lportal?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false
jdbc.default.username=
jdbc.default.password=
# MySQL Shard SpotPay Database Connector
jdbc.one.driverClassName=com.mysql.jdbc.Driver
jdbc.one.url=jdbc:mysql://localhost/lportal1?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false
jdbc.one.username=
jdbc.one.password=
# MySQL Shard WellsFargo Database Connector
jdbc.two.driverClassName=com.mysql.jdbc.Driver
jdbc.two.url=jdbc:mysql://localhost/lportal2?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false
jdbc.two.username=
jdbc.two.password=
# For multitenancy purpose
#Spring configuration files to be loadded. By adding shard-data-source-spring.xml in the list database sharding feature
#can be enabled
spring.configs=\
META-INF/base-spring.xml,\
\
META-INF/hibernate-spring.xml,\
META-INF/infrastructure-spring.xml,\
META-INF/management-spring.xml,\
\
META-INF/util-spring.xml,\
\
META-INF/jpa-spring.xml,\
\
META-INF/executor-spring.xml,\
\
META-INF/audit-spring.xml,\
META-INF/cluster-spring.xml,\
META-INF/editor-spring.xml,\
META-INF/jcr-spring.xml,\
META-INF/ldap-spring.xml,\
META-INF/messaging-core-spring.xml,\
META-INF/messaging-misc-spring.xml,\
META-INF/mobile-device-spring.xml,\
META-INF/notifications-spring.xml,\
META-INF/poller-spring.xml,\
META-INF/rules-spring.xml,\
META-INF/scheduler-spring.xml,\
META-INF/scripting-spring.xml,\
META-INF/search-spring.xml,\
META-INF/workflow-spring.xml,\
\
META-INF/counter-spring.xml,\
META-INF/mail-spring.xml,\
META-INF/portal-spring.xml,\
META-INF/portlet-container-spring.xml,\
META-INF/staging-spring.xml,\
META-INF/virtual-layouts-spring.xml,\
META-INF/shard-data-source-spring.xml,\
META-INF/ext-spring.xml
shard.selector=com.liferay.portal.dao.shard.ManualShardSelector
shard.available.names=default,one,two
hibernate.dialect=org.hibernate.dialect.MySQLDialect
shard.default.name=default
After I run the server, default table(liferay’s table) are populated to all the shards perfectly. But when i deployed my custom portlets, it only reflects to default shard and other two shards are remains as it is. I am wondering if I missed any step.
Appreciate your suggestion.
Thanks
Bhuwan
Hi,
Custom portlets make use of the same connections as the portal. Did you deploy your custom portlets on all the shards?
What errors do they give when accessing them? It might be that you have to manually create the tables for the custom portlets
on the secondary shards.
It would be very helpful if you can provide some standard suggestion on the multi-tenancy part.
Yeah it worked !! Thanks for your time.
Glad I could help
I followed https://www.liferay.com/community/wiki/-/wiki/Main/Database+Sharding article and implemented 6 DB and working fine. But after some time it is showing suddenly
com.liferay.portal.kernel.events.ActionException: com.liferay.portal.NoSuchLayoutSetException: No LayoutSet exists with the key {groupId=10422, privateLayout=false}
com.liferay.portal.events.ServicePreAction.run(ServicePreAction.java:145)
Really i cud not move to production.
I used Liferay605/tomcat/mysql/Ubuntu
Hi Ahmed,
I think that the error is that after a while the portal’s cache expires and when the service layer tries to retrieve some data, it looks for the data in the cache, doesn’t find it because the cache expired and then tries to query the database, only it queries the wrong database due to a bug in the cache implementation (I really hate the cache implementation myself).
Can you try the following:
1. login on one shard (not the default)
2. leave the window open until the session expires (this usually triggers a clearing of the cache)
3. try to access the shard again (this should trigger the exception)
I have run into this scenario in my code doing some shard operation and managed to get around by specifically selecting which shard to use.
same exatly i am facing this issue what you are telling. what to do?any solution for this… Please help
Hi Negotia,
I am facing same above problem only . Kindly please help how to solve this issue. Waiting for your reply please.
it is happening in without login(guest) scenario only not in logged-in scenario.
if i leave a page idle some mins then if i refresh that page getting above exception on page and console.Pls help
can you send me the full stack trace?
did you configure any custom PreAction or install any plugin/hook/extension?
Caused by: com.liferay.portal.NoSuchLayoutSetException: No LayoutSet exists with the key {groupId=10563, privateLayout=false}
at com.liferay.portal.service.persistence.LayoutSetPersistenceImpl.findByG_P(LayoutSetPersistenceImpl.java:969)
at sun.reflect.GeneratedMethodAccessor341.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
at com.liferay.portal.dao.shard.ShardAdvice.invokePersistence(ShardAdvice.java:245)
at sun.reflect.GeneratedMethodAccessor100.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy28.findByG_P(Unknown Source)
at com.liferay.portal.service.impl.LayoutSetLocalServiceImpl.getLayoutSet(LayoutSetLocalServiceImpl.java:114)
at sun.reflect.GeneratedMethodAccessor340.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at com.liferay.portal.dao.jdbc.aop.DynamicDataSourceTransactionInterceptor.invoke(DynamicDataSourceTransactionInterceptor.java:44)
at com.liferay.portal.spring.aop.ChainableMethodAdvice.invoke(ChainableMethodAdvice.java:58)
at com.liferay.portal.spring.aop.ChainableMethodAdvice.invoke(ChainableMethodAdvice.java:58)
at com.liferay.portal.spring.aop.ChainableMethodAdvice.invoke(ChainableMethodAdvice.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy165.getLayoutSet(Unknown Source)
at com.liferay.portal.service.LayoutSetLocalServiceUtil.getLayoutSet(LayoutSetLocalServiceUtil.java:245)
at com.liferay.portal.model.impl.LayoutImpl.getLayoutSet(LayoutImpl.java:283)
at com.liferay.portal.events.ServicePreAction.servicePre(ServicePreAction.java:1245)
at com.liferay.portal.events.ServicePreAction.run(ServicePreAction.java:142)
… 141 more
04:40:26,249 ERROR [jsp:684] User ID null
04:40:26,250 ERROR [jsp:685] Current URL /web/guest
04:40:26,251 ERROR [jsp:686] Referer null
04:40:26,252 ERROR [jsp:687] Remote address 127.0.0.1
04:40:26,252 ERROR [jsp:689] com.liferay.portal.kernel.events.ActionException: com.liferay.portal.NoSuchLayoutSetException: No LayoutSet exists with the key {groupId=10563, privateLayout=false}
com.liferay.portal.kernel.events.ActionException: com.liferay.portal.NoSuchLayoutSetException: No LayoutSet exists with the key {groupId=10563, privateLayout=false}
at com.liferay.portal.events.ServicePreAction.run(ServicePreAction.java:145)
at com.liferay.portal.events.EventsProcessorImpl.processEvent(EventsProcessorImpl.java:81)
at com.liferay.portal.events.EventsProcessorImpl.process(EventsProcessorImpl.java:58)
at com.liferay.portal.events.EventsProcessorUtil.process(EventsProcessorUtil.java:53)
at com.liferay.portal.servlet.MainServlet.processServicePre(MainServlet.java:1064)
at com.liferay.portal.servlet.MainServlet.service(MainServlet.java:449)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:126)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:126)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.strip.StripFilter.processFilter(StripFilter.java:309)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:126)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.gzip.GZipFilter.processFilter(GZipFilter.java:137)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.secure.SecureFilter.processFilter(SecureFilter.java:182)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.autologin.AutoLoginFilter.processFilter(AutoLoginFilter.java:254)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302)
at com.liferay.portal.servlet.FriendlyURLServlet.service(FriendlyURLServlet.java:133)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:126)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:126)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.strip.StripFilter.processFilter(StripFilter.java:261)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:126)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.gzip.GZipFilter.processFilter(GZipFilter.java:126)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.secure.SecureFilter.processFilter(SecureFilter.java:182)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.i18n.I18nFilter.processFilter(I18nFilter.java:221)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:126)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.cache.CacheFilter.processFilter(CacheFilter.java:385)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.etag.ETagFilter.processFilter(ETagFilter.java:45)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.autologin.AutoLoginFilter.processFilter(AutoLoginFilter.java:254)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.sso.ntlm.NtlmPostFilter.processFilter(NtlmPostFilter.java:81)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.sharepoint.SharepointFilter.processFilter(SharepointFilter.java:179)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.virtualhost.VirtualHostFilter.processFilter(VirtualHostFilter.java:239)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:126)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:126)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:738)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:196)
at com.liferay.portal.servlet.filters.threadlocal.ThreadLocalFilter.processFilter(ThreadLocalFilter.java:35)
at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:123)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:465)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Unknown Source)
Caused by: com.liferay.portal.NoSuchLayoutSetException: No LayoutSet exists with the key {groupId=10563, privateLayout=false}
at com.liferay.portal.service.persistence.LayoutSetPersistenceImpl.findByG_P(LayoutSetPersistenceImpl.java:969)
at sun.reflect.GeneratedMethodAccessor338.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
at com.liferay.portal.dao.shard.ShardAdvice.invokePersistence(ShardAdvice.java:245)
at sun.reflect.GeneratedMethodAccessor96.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy28.findByG_P(Unknown Source)
at com.liferay.portal.service.impl.LayoutSetLocalServiceImpl.getLayoutSet(LayoutSetLocalServiceImpl.java:114)
at sun.reflect.GeneratedMethodAccessor337.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at com.liferay.portal.dao.jdbc.aop.DynamicDataSourceTransactionInterceptor.invoke(DynamicDataSourceTransactionInterceptor.java:44)
at com.liferay.portal.spring.aop.ChainableMethodAdvice.invoke(ChainableMethodAdvice.java:58)
at com.liferay.portal.spring.aop.ChainableMethodAdvice.invoke(ChainableMethodAdvice.java:58)
at com.liferay.portal.spring.aop.ChainableMethodAdvice.invoke(ChainableMethodAdvice.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy165.getLayoutSet(Unknown Source)
at com.liferay.portal.service.LayoutSetLocalServiceUtil.getLayoutSet(LayoutSetLocalServiceUtil.java:245)
at com.liferay.portal.model.impl.LayoutImpl.getLayoutSet(LayoutImpl.java:283)
at com.liferay.portal.events.ServicePreAction.servicePre(ServicePreAction.java:1245)
at com.liferay.portal.events.ServicePreAction.run(ServicePreAction.java:142)
… 141 more
Hi John,
I found performance degradation after the multi-tenant implementation. I didn’t have a multi-tenant environment previously in my application. At that time the application was taking around 8 sec to complete one of the major job in the application.
Currently I have 3 instances one.com, two.com, and three.com. Once I browse two.com and try to run the same major job, it is now taking 50 sec.
I read that the application takes time to initialize the instance at first and after that the performance will be same as with only one shards(ie. without multi-tenant scenerio) configuration.
I would like to know your thought on causes to the performance in a multi-tenant configuration.
Thanks in advance
Bhuwan
Hi Bhuwan,
the multi-tenant implementation has indeed the disadvantage that upon first access of an instance the database connections pool needs to be initialized because it is no longer initialized at start-up. So, one thing would be to give me some more details about what you mean by major job: do you access a certain instance and then you start some process, or do you run a script from the main instance?
Also, I think that the 50 seconds are too much, so my guess is that the configuration for the connection pool is not ideal. You should check the settings for the initial pool size and set it lower, otherwise upon first accessing an instance the system will wait for the connections in the pool to initialize.
hope this helps,
john
Hi John,
Here is the scenerio:
Case 1: Running with only one database configuration(ie. without multi-tenant environment)
I have a service method which hits around 10 tables and it is taking around 10 sec.
Case 2: Multi-tenant env(just change in the portal-ext file) and the same service of #1 is taking 50 sec after accessing the instance.
Any Idea?
Thanks
Bhuwan
Do you mean that the service is using data only from one instance (shard)? or multiple shards. Please give me more details about what the service is doing
Hi John,
Yes, it is using only once instance. I have a one service which calls other 5 services and which hits 5 different database. Also there is a loop to populate the 3 years of data. This all is happening on the same instance. It was taking 8 sec earlier and now it is taking 50 sec. Any clue?
Thanks
Bhuwan
Hi,
in your spring configuration, where you define the shardDataSourceX bean, try and set the following properties for the connection pool
minPoolSize = 0
acquireIncrement = 0
initialPoolSize = 0
maxPoolSize = 20
this will ensure that upon first usage of a shard will only initialize 1 connection, therefore taking less time.
let me know how it turns out
Hi John,
The other problem after multi-tenant configuration is that the transaction management is not working. It was working earlier with the non multi-tenant configuration.
Need your help.
Thanks
Bhuwan
Hi Jon,
The performance issue after multi-tenant was due to the transaction management. Since the transaction management is not working, while saving 1095 times it will try to execute the commit statement everytime I think and it is taking time.
So I really need to know what is the case in the multi-tenanat configuration that transaction management is not working.
Thanks
Bhuwan
Hi John,
Thanks for your support. I have resolved both the performance issue and the transaction management issue in multi-tenant setup. Actually after the implementation of transaction management(JTA) performance issue is automatically gets resolved.
The reason was if you do not have TM in application then your db will try to execute the commit stmt everytime you hit to the database. But in case of TM it will hit commit at the end only.
Thanks
Bhuwan
Hi Gautam,
glad to hear that everything works. Can you tell me what you did to implement transaction management?
thanks,
John
Hi John,
I’ve noticed you’re really experienced with Liferay and I’m currently building an environment using Liferay and I never used it before so I wonder if you can help me 🙂
Currently we try to configure 2 test tenants but we plan to configure more than 100 so we’ll need to optimize it , however we experience some issues when creating a new portal instance.
We are using a Jboss with Liferay 6.1.2GA3 on a RH machine that connects to a database server(pgsql 9.2).
We created a custom portal-shards.xml and placed it under WEB-INF/classes, a portal-ext.properties file where we include a second file where the define the spring.configs list and some shard configs. The last two are placed on a separate config folder.
The server starts ok we can access the control panel via the default shard but when we try to add a new portal instance the following error occur(similar with the one described here):
com.liferay.portal.NoSuchResourcePermissionException: No ResourcePermission exists with the key {companyId=1, name=com.liferay.portal.model.Layout, scope=4, primKey=26, roleId=10154}
The odd part is that we configured same Jboss/Liferay using exactly the same config files and locations on a WIN 8 machine that connects remotely to the db server and it works just fine. Do you have any idea why this is happening and what we should try? I would really appreciate the help
Thanks,
Gabi
Hi Gabi,
The error indicates that it cannot find a record in the resourcepermission table in the database.
It might be that it’s looking for it in the wrong database. You can manually check the database for the record and see if you can find it.
It looks a bit strange that the companyId = 1, because in Liferay 6.0 companyIds started at 10801 or something, maybe this changed in 6.1
Hi John
In my case, I too had to manually run script to generate custom tables in each of the additional shards as it did not automatically create the custom portlet tables. Not sure why.
But I am running into a Hibernate error :
com.liferay.portal.kernel.dao.orm.ORMException: org.hibernate.MappingException: Unknown entity: com.xxxxxx.xxx.xxx.MyEntityImpl
The same setup works just fine if I turn off sharding in the portlet-ext.properties.
any guesses ?
Hi John,
Sorry for the late reply. Following are the steps to implement JTA on Liferay+JBoss environment.
Java Transaction api(JTA)
The Java Transaction API(JTA), one of the java Enterprise Edition (java EE) APIs, enables distributed transactions to be done across mutliple X/OpenXA resources in a java environment. JTA is a specification developed under the java community process as JSR 907.
The JTA API consists of classes in two Java packages:
javax.transaction
javax.transaction.xa
Configuration steps:
Download jta-1_1-classes.zip from link below and rename it to jta.jar. Add jta.jar in $JBOSS_HOME/jboss-eap-.1/modules/system/layers/base/com/liferay/portal/main/ inside module base directory and change module.xml http://download.oracle.com/otndocs/jcp/jta-1.1-classes-oth-JSpec/?submit=Download
module.xml
Use xa-datasource which is recommended for clustered servers and jta environment
file: standalone.xml or standalone-ha.xml
xa-datasource
database_url
database_user
database_password
mysql
TRANSACTION_READ_COMMITTED
Note : for sharded configuration make additional xa-datasource as required.
change mysql driver to use MysqlXADatasource class
file: standalone.xml or standalone-ha.xml
mysql_driver
com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
enable jta transaction manager
liferay by default uses hibernate transaction manager
default tx manager
transaction.manager.impl=com.liferay.portal.spring.hibernate.LastSessionRecorderHibernateTransactionManager
transaction.manager.property.globalRollbackOnParticipationFailure=false
liferay provides following properties to switch from hibernate transaction manager to jta transaction manager
liferay provided jta properties
transaction.manager.impl=org.springframework.transaction.jta.JtaTransactionManager
transaction.manager.property.allowCustomIsolationLevels=true
transaction.manager.property.globalRollbackOnParticipationFailure=true
but above configuration is not sufficient so we need to make change on spring configuration file and above properties can be skipped. Transaction manager can be changed directly on spring bean configuration inside hibernate-spring.xml.
file : hibernate-spring.xml
default config :
default tx manager
modified config:
modified
Note: Only xml changes is sufficient here and no need to add properties in portal-ext.properties
Disable autocommit true
Liferay by default sets autocommit true which is not allowed on JTA environment. We need to change a set of spring configuration file to disable autocommit property for datasource which is metioned below;
change counter-spring.xml
default config:
default
modified config:
modified
change dynamic-data-source-spring.xml
default config:
default
modified config:
modified
change infrastructure-spring.xml
default config:
default
modified config:
modified
change shard-data-source-spring.xml
default config:
default
modified config:
modified
Note: Above liferay-spring configuration files can be retrieved from liferay-source
Now above spring files will be placed inside folder ROOT.war/WEB-INF/classes/META-INF and will configured in portal-ext.properties to override default configuration which will already be done together with Sharded configuration.
spring config in portal-ext.properties
spring.configs=\
META-INF/base-spring.xml,\
META-INF/hibernate-spring.xml,\
META-INF/infrastructure-spring.xml,\
META-INF/management-spring.xml,\
META-INF/util-spring.xml,\
META-INF/jpa-spring.xml,\
META-INF/executor-spring.xml,\
META-INF/audit-spring.xml,\
META-INF/cluster-spring.xml,\
META-INF/editor-spring.xml,\
META-INF/jcr-spring.xml,\
META-INF/ldap-spring.xml,\
META-INF/messaging-core-spring.xml,\
META-INF/messaging-misc-spring.xml,\
META-INF/mobile-device-spring.xml,\
META-INF/notifications-spring.xml,\
META-INF/poller-spring.xml,\
META-INF/rules-spring.xml,\
META-INF/scheduler-spring.xml,\
META-INF/scripting-spring.xml,\
META-INF/search-spring.xml,\
META-INF/workflow-spring.xml,\
META-INF/counter-spring.xml,\
META-INF/mail-spring.xml,\
META-INF/portal-spring.xml,\
META-INF/portlet-container-spring.xml,\
META-INF/staging-spring.xml,\
META-INF/virtual-layouts-spring.xml,\
#META-INF/dynamic-data-source-spring.xml,\
META-INF/shard-data-source-spring.xml,\
#META-INF/memcached-spring.xml,\
#META-INF/monitoring-spring.xml,\
META-INF/ext-spring.xml
Hi John,
I have Successfully implemented JTA on Liferay+JBoss but still I am having one problem regarding the database changes. When there is a change in the database( new table or other changes) then I have to disable JTA to reflect those changes to the database. Once the changes are reflect then I need to turn on JTA again.
Do you know any workaround?
HI john ,
i want to enable add document feature in one of the shard instances how to do ?
[…] in the Spring configuration of Liferay – if you are interested you can read more about configuring Liferay with shards. Depending on how the server is configured, shards can be assigned to portal instances manually or […]