Technical Blog

2 Posts tagged with the oracle tag

If some of the recent performance-related posts by our much cherished Get Elastic blogger extraordinaire Linda are starting to worry you, fear not! With the newly released Elastic Path 6.2, the Product Development and Performance teams at Elastic Path have done a fantastic job ramping up the standard performance of the product. I'll let them brag about the numbers at a later date, but today's post is to dig into the guts of the caching introduced into the system, and how you can start tweaking some of the configuration settings to squeek out every millisecond for page responses.

 

Within Elastic Path, there are now two caches that are available to be tweaked:

  • Application-level: Sitting between the view layer and the data access layer
  • Persistence-level: A Level-2 cache, within the OpenJPA ORM framework

 

Application Level Cache

All products loaded within the Storefront application via the StoreProductService will be from an Ehcache-backed cache by default. Each application is responsible for loading products via a ProductRetrieveStrategy. You'll notice storefront has two new configurations to facilitate using Ehcache:

 

Cache.xml:
     <bean id="productCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
          <property name="timeToLive" value="600"/>
          <property name="timeToIdle" value="600"/>
     </bean>

     <bean id="cachingProductRetrieveStrategy" class="com.elasticpath.sfweb.service.impl.EhCacheProductRetrieveStrategyImpl">
          <property name="productService" ref="productService" />
          <property name="cache" ref="productCache" />
     </bean>

 

ServiceSF.xml:
    <alias name="cachedSettingsReader" alias="settingsReader"/>
    <alias name="cachingProductRetrieveStrategy" alias="productRetrieveStrategy"/>

 

You'll note that the storefront is now using aliases in the Spring configuration to override the same bean definitions in the default core service.xml. This allows the storefront to setup caching-specific classes. For tweaking purposes, the productCache bean definition should be updated to optimize the Ehcache settings. By default, Spring's EhCacheFactoryBean will initialize the cache to allow overflow to disk, use LRU eviction and limit the in-memory size to 10k objects. For catalogs with a larger product mix, these settings should be optimized and potentially moved to an distributed cache via Terracotta to keep the JVM heap size to a reasonable level.

 

You can choose to add new properties here to tweak these values, or add an ehcache.xml configuration file to the classpath and define a specific cache name as part of the productCache definition to use. A quick tip on monitoring the cache statistics for tweaking the settings during load test is to setup JMX monitoring for the storefront cache. This can be done via two steps:

 

Running the appserver with remote JMX enabled (no authentication for a non-production environment):

-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=6969 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false

 

Configuring the JMX beans for Ehcache in cache.xml. Note in this case we are explicitly setting up a cache manager, which we can also use to specify a custom Ehcache configuration file instead of the default ehcache.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">

    <bean id="productCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
        <property name="timeToLive" value="600"/>
        <property name="timeToIdle" value="600"/>
        <property name="cacheManager" ref="cacheManager" />
    </bean>

    <bean id="cachingProductRetrieveStrategy" class="com.elasticpath.sfweb.service.impl.EhCacheProductRetrieveStrategyImpl">
        <property name="productService" ref="productService" />
        <property name="cache" ref="productCache" />
    </bean>

    <bean id="cacheManager"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    </bean>

    <!-- Spring initialization of ehCache's mbeans -->
    <bean id="ehCacheMBeanRegistration"
        class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod"
            value="net.sf.ehcache.management.ManagementService.registerMBeans" />
        <property name="arguments">
            <list>
                <ref bean="cacheManager" />
                <ref bean="mbeanServer" />
                <value>true</value>
                <value>true</value>
                <value>true</value>
                <value>true</value>
            </list>
        </property>
    </bean>

    <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
        <property name="locateExistingServerIfPossible" value="true" />
    </bean>

</beans>

 

Connecting up with jConsole lets us check the cache settings are properly configured, and to check the statistics, as per the below image. We should see updates to statistics as you browse the storefront and load more products:

screen-capture-1.png

 

 

Persistence Level Cache

As part of the Elastic Path 6.2 release, the included OpenJPA library has been upgraded to a 1.2.1 version, which overcomes some data cache issues in the previous 1.0.1 version. Please see the OpenJPA 1.2.1 documentation for all details on the native OpenJPA datacache. We'll go over some of the changes that enable the data cache using the native implementation.

 

Annotations:

All transactional persistent entities (anything submitted or updated regularly as an online transaction such as orders, payments and customers) have a new data cache annotation so that they are non-cacheable. By default, entities are enabled in the data cache unless this annotation is present and explicitly disabled the caching. All relatively static data, such as catalog entities should be part of the cache and thus will be missing this new annotation.

 

@DataCache(enabled = false)

 

Persistence.xml Configuration:

As part of the persistence unit configuration, three new properties are configured by default:

            <property name="openjpa.DataCache" value="true"/>
            <property name="openjpa.RemoteCommitProvider" value="sjvm"/>
            <property name="openjpa.DataCacheTimeout" value="1000"/>

 

These values are used for:

  • openjpa.DataCache - enabling the cache, and specifying the cache properties. We recommend tweaking this value to accomodate the cache size according to the size of the underlying data: (true(CacheSize=25000, SoftReferenceSize=0))
  • openjpa.RemoteCommitProvider - specifying the commit provider. For a clustered setup, evictions should be handled via configuring this setting to use JMS or TCP-based evictions.
  • openjpa.DataCacheTimeout - maximum time to live for entities in the cache

 

Also suggested is to tune the query cache, which is enabled with default values when the DataCache property is set to true:

<property name="openjpa.DataCache" value="true(CacheSize=25000, SoftReferenceSize=0)"/>
<property name="openjpa.RemoteCommitProvider" value="sjvm"/>
<property name="openjpa.QueryCache" value="CacheSize=25000, SoftReferenceSize=0"/>

 

Tip: Catching Cache Hits and Misses

If caching is enabled and you're still seeing a large amount of database queries from the storefront server, you can enable data cache Log4j tracing and grep out the hits and misses logged to track down which entities and queries are mistakingly hitting the database. Most of the times, these can be tracked down to accidental evictions or entities within the inheritance hierachy being disabled from cache.

 

log4j.category.openjpa.DataCache=TRACE

 

This spits out some logs similar to below that's easily greppable to count and track which entities are creating problems:

DEBUG 2009-09-10 12:56:33,918 org.apache.renamed.openjpa.lib.log.CommonsLogFactory$LogAdapter.trace(CommonsLogFactory.java:76) - Cache hit while looking up key "com.elasticpath.domain.attribute.impl.ProductTypeProductAttributeImpl-1".
DEBUG 2009-09-10 12:56:33,926 org.apache.renamed.openjpa.lib.log.CommonsLogFactory$LogAdapter.trace(CommonsLogFactory.java:76) - Cache miss while looking up key "org.apache.renamed.openjpa.datacache.QueryKey@b7ee9cab[query:[SELECT ps.skuCodeInternal FROM ProductSkuImpl ps

 

OpenJPA Cache Plugins

The native data cache within OpenJPA is architected to be swappable via plugin configuration. This allows the ability to swap in alternative caching technologies like Ehcache or Oracle Coherence to support extensive scalability requirements, as both Ehcache/Terracotta and Oracle Coherence support distributed caching setups. We've tested Ehcache and Coherence plugins internally with favourable results.

 

An Ehcache plugin is provided by the Ehcache group, however this version must be repackaged to match the custom package names of the Elastic Path specific org.apache.renamed.openjpa jar. Once a repackaged instance of the Ehcache-OpenJPA jar is in the classpath, the configuration changes to:

<property name="openjpa.DataCacheManager" value="ehcache"/>

 

Similarily enough, OpenJPA commiter Pinaki has posted the initial workings of an Oracle Coherence plugin on his blog, along with some additional JPA caching insights. Elastic Path with OpenJPA and Oracle Coherence is probably worth an entire blog entry in itself (coming soon!), but the same configuration settings apply, along with the need to repackage the code to point at the Elastic Path OpenJPA jar:

 

<property name="openjpa.DataCacheManager" value="coherence"/>

 

So there it is, two new caching mechanisms to tweak and fine-tune as part of load tests that should favourably reflect storefront page load times, and hopefully conversion rates and green dollar signs. Feel free to ask away about some of our load testing and tuning war stories. We'd be happy to talk about our hands on experience with the recent caching work.

6 Comments Permalink


The performance team at Elastic Path has recently gone through Oracle RAC validation with Elastic Path 6.1.1 and made it out the other side unscathed. And the best part, is that there are no code changes required on EP out-of-the-box to fully support Oracle RAC with Fast-Connection-Failover (FCF).


The benefits of Oracle RAC (Real Application Clusters, or Oracle database clustering in simple terms) are three-fold: performance, scalability, and reliability. Which one matters the most to you depends on your needs, but usually having the assurance of database failover is the most valuable, with scalability and performance coming a close second.


In our testing, we used WebLogic 10.0.1 and Oracle 11g (Release 1) on physical machines using Intel Xeon quad-core 2.5Ghz CPUs and 8GBs RAM. The OS was 64-bit RedHat EL 5.  In-house, we are typically able to push a single Oracle node to capacity with 3 EP storefront nodes. For our validation testing, with a four storefront configuration, we were utilizing roughly 50-60% capacity on a two node RAC configuration. The following is a rough guide for setting up RAC for EP.

 

RAC Configuration w/ EP

Deployment and configuration of Oracle Clusterware and Oracle 11g was fairly straight-forward and required no special configuration with Elastic Path, other than the standard RAC connection config outlined below. Oracle's online documentation for the Clusterware  set up is excellent and very detailed when you need to drill down.


Once the Clusterware and database are up and running, and your data has been populated, there are many different ways to set up RAC with WebLogic. See the WebLogic documentation for details. WebLogic recommends the use of multi data sources to connect to the RAC nodes. This method supports failover and load-balancing at the application level which is more effective as WebLogic's health monitors can be used and failover is done more quickly than Connect-Time failover or allowing the cluster-ware to handle this. It is recommended to set up a data source for each RAC node. Below is a configuration example for the data source XML; it is based on a two node setup (a data source for each node) and the DS pool.



WebLogic Data Source Example XML

<jdbc-data-source>

<name>jdbcPool</name>

<jdbc-driver-params>

  <url>jdbc:oracle:thin:@lcqsol24:1521:snrac1</url>

  <driver-name>oracle.jdbc.OracleDriver</driver-name>

  <properties>

   <property>

    <name>user</name>

    <value>wlsqa</value>

   </property>

  </properties>

  <password-encrypted>{3DES}aP/xScCS8uI=</password-encrypted>

</jdbc-driver-params>

<jdbc-connection-pool-params>

  <test-connections-on-reserve>true</test-connections-on-reserve>

  <test-table-name>SQL SELECT 1 FROM DUAL</test-table-name>

</jdbc-connection-pool-params>

<jdbc-data-source-params>

  <jndi-name>jdbcDataSource</jndi-name>

</jdbc-data-source-params>

</jdbc-data-source>


<jdbc-data-source>

<name>jdbcPool2</name>

<jdbc-driver-params>

  <url>jdbc:oracle:thin:@lcqsol25:1521:SNRAC2</url>

  <driver-name>oracle.jdbc.OracleDriver</driver-name>

  <properties>

   <property>

    <name>user</name>

    <value>wlsqa</value>

   </property>

  </properties>

  <password-encrypted>{3DES}aP/xScCS8uI=</password-encrypted>

</jdbc-driver-params>

<jdbc-connection-pool-params>

  <test-connections-on-reserve>true</test-connections-on-reserve>

  <test-table-name>SQL SELECT 1 FROM DUAL</test-table-name>

</jdbc-connection-pool-params>

<jdbc-data-source-params>

  <jndi-name>jdbcDataSource2</jndi-name>

  <global-transactions-protocol>OnePhaseCommit</global-transactions-protocol>

</jdbc-data-source-params>

</jdbc-data-source>


<jdbc-data-source>

<name>jdbcNonXAMultiPool</name>

<jdbc-data-source-params>

  <jndi-name>jdbcDataSource</jndi-name>

  <algorithm-type>Failover</algorithm-type>

  <data-source-list>jdbcPool,jdbcPool2</data-source-list>

  <failover-request-if-busy>true</failover-request-if-busy>

</jdbc-data-source-params>

</jdbc-data-source>


 

Fast-Connection-Failover

WebLogic also supports Fast-Connection-Failover (FCF). This mechanism provides a means to receive event notification from the Oracle RAC nodes such as notification and cleanup of invalid connections, load balancing events, and node failures. In order to enable FCF, you must tweak the Oracle JDBC driver and add a couple additional properties to the data source connection such that it knows how to receive the ONS (Oracle Notification System) messages.

 

To enable FCF on a data source:

  1. In the WebLogic console, under the data source:
    1. In Driver Class Name, set the driver class to oracle.jdbc.pool.OracleDataSource.
    2. In Properties, set the ONS configuration string to subscribe to RAC's ONS messages, for example: ONSConfiguration=nodes=hostname1:port1,hostname2:port2
  2. Finally, make sure that ONS is properly configured on the RAC nodes and you have no blocking firewalls on those ports on either the RAC nodes or the application server nodes.
3 Comments Permalink