Technote (FAQ)
Question
IBM WebSphere Portlet Factory applications require good design practices just like any other application in order to be easily maintained, to provide enhancements and to achieve good performance results. This technote provides tips on how to troubleshoot specific performance issues and how to improve overall application performance.
Answer
NOTE: Not all logging and techniques described in this technote are available in earlier versions of Portlet Factory.
| Analysis Tools & Techniques |
The first step to improving the performance of your WebSphere Portlet Factory application is to determine the current performance level of the application. It is important to gather initial measurements so that you will know if your changes have resulted in an improvement or not.
| Model Actions |
Run Model Actions logging on a single model or Portlet in your application. It is important to look at pieces of the application to make the analysis easier.
How to configure Model Actions logging:
1. Open the WEB-INF/config/log4j.properties file.
2. Find the line: log4j.logger.bowstreet.system.modelActions=WARN,ModelActions
3. Set the modelActions property to DEBUG.
4. The line should now be: log4j.logger.bowstreet.system.modelActions=DEBUG,ModelActions
5. Restart the server.
Once you configure Model Actions, when you run any model from Portlet Factory Designer or any portlet in the Portal, Model Actions will be logged to the modelActions.txt file. The file can be found in the deployed application's WEB-INF\logs directory.
Again, you should work on a single model or portlet at a time. Run the model or portlet as a normal user would and execute the actions that you would like to analyze. It is a good idea to run through as much of the functionality as you can for the first pass to get a record of each of the actions in the model.
The modelActions.txt file will contain information about how long each action in a model takes. An example entry will look like this:
Look at the line for Method: main. Note that the first column has the value of 80 milliseconds and the second has the value of 571 milliseconds. This means that the main action itself took only 80 milliseconds, but the main action and all of the child actions took a total of 571 milliseconds.
80+240+31+20+60+30+30+30+10+30+40=571
Note also that an action with no dots preceding it is a top level action. An action with one dot is one level below, and action with two dots is two levels below and so on. In the case of the main method in the example above, it has only one child, Page:StartingPage. The Page: StartingPage then has several child actions.
If the logging of Model Actions indicates a lengthy regen, there are several possible causes to explore. Start by checking external systems. Check to see if there may be a network issue between the portal server and back end systems. The Portlet Factory application may be trying to get schema information from a back-end system and problems such as a slow or disconnected network connection will cause a long regen time. Another cause for a slow regen is the absence of schema caching. See the section below on Schema Caching. As a last resort, analyzing builder calls logging can provide some information about the lengthy regen.
| Session Size |
Collecting Session Size information can help you spot areas where you might be adding too much data to the session. Usually this is done in cases where you are pulling data from a back-end and perhaps you are pulling too much data at once or have placed data in session that is needed only for a short time and you are not removing the data when you are done with it.
You will find some helpful suggestions under the section "Correct Performance Issues" in this technote.
NOTE: The session size functionality is broken in WebSphere Portlet Factory version 6.0.1.1 (Fixpack 1).
Prior to enabling session size trace, make note of the following:
- - After enabling the session size trace, it is very important to have just a single user access the application being traced in order to analyze the various facets [objects, variables, etc] of the application in the session.
- - Running the session size trace for too long can have performance implications on the runtime of the application. It is strongly advised that you disable the trace after gathering the necessary session size information.
How to enable Session Size trace
[1] In the WEB-INF/config/bowstreet.properties file of your portlet application, set the following parameters:
-
a) Set bowstreet.diagnostic.trace.enabled=true
b) Uncomment and set the bowstreet.diagnostic.trace.sessionSize.interval=60
The trace information will be written to the sessionsize.csv file in the WEB-INF/logs directory of your WebApp. This will gather the trace every 60 seconds and append the information to the sessionsize.csv file only if there is a change in the session variables.
c) Set the bowstreet.diagnostic.trace.sessionSize.userName parameter with the username [shortname] for the user whose session you want this trace to be gathered for.
For example: bowstreet.diagnostic.trace.sessionSize.userName=wpsadmin
[2] Restart the application server to trigger the trace.
[3] Run the scenario that leads to the high memory usage.
[4] Review the sessionsize.csv file which will have the information regarding all the objects in the user's session in the following format:
-
"*-- <Date and Timestamp> --*"
Session Size Info
Model,Name,Type,Size,StringSize
- The 'Size' value is computed by serializing the object and then counting the bytes.
- The 'StringSize' value is computed by counting the number of characters returned by the toString() method.
- For XML data, the Size value estimates (using serialization or "toString") are much smaller than the actual in-memory size. The in-memory size may be around 5x the sizes shown in the table.
- The variables listed in the session size tracing can also include information about the scope. The following are the possible variable scopes:
0 - persisted for failover
1 - in session but not persisted for failover
2 - shared, read-only
3 - request scoped
- Variables other than those scoped as 0 or 1 are not in the user session. This means that when analyzing the session size tracing, you should focus on those scoped as 0 or 1 since these are the variables that are taking up memory in session.
The following is additional information:
| -- Read-Write - This setting prompts the model to save the current value for the variable in the case of a failover event. This failover functionality does incur a small performance hit, so only use the default setting when necessary. -- Read-Write but not persisted for failover - This setting allows your variable's value to change, but its value at the time of failover will not be preserved. -- Read-only: shared across all users - This setting makes your variable behave like a constant. Its value will never change from the initial setting. As a result, some performance gains are realized because a copy of the variable is not made during generation and the variable is not processed during the failover event. -- Request scoped - This setting will store the variable value in a request attribute rather than in a class (WebAppInstanceData). Doing this can improve performance for large variables that are set and read within a single request, and not needed across multiple requests. Note: Do not use request scoped for items such as XML variables that are used in multiple requests. Doing so would clone and initialize the request copy of that variable for every use. Use request scoped for infrequently used variables and variables that hold large objects or hierarchies of objects (such as XML). A typical use of request scoped might be for a variable that contains data for a table that is displayed on only page of a multi-page application. |
How to disable Session Size trace
[1] In WEB-INF/config/bowstreet.properties, set bowstreet.diagnostic.trace.enabled= false
[2] Restart the application server.
| Server Stats |
Server statistics are recorded in a log file in deployed .war files whenever the server is running. The server stats logging records statistics about requests to the Portlet Factory application every five minutes. This information can provide valuable information about the "health" and performance of the application.
The server statistics are written to WEB-INF/logs/serverStats.txt. Here are some key things to look for in server stats to help analyze application performance.
| Sessions Description Sessions: 3 This shows that there are three portlet sessions in memory, if running as a portlet. If running as a servlet, there are three user sessions active in this web application. You can also get an approximation of how many user sessions are in memory, as described above. |
||||||
| Regens RegensFromCache Description RegensFromCache: 15 This shows that 21 new requests for models were made. 15 of those were taken from cache, so there were 6 actual regens during the 5-minute snapshot interval. |
||||||
Description: MemTotal is the total heap size reported by Runtime.getRuntime().totalMemory(). MemFree is the amount of free memory reported by Runtime.getRuntime().freeMemory(). MemInUse is simply MemTotal minus MemFree.
|
||||||
Description: Example: Look for: |
||||||
Description: These entries show information about all top-level requests to Portlet Factory models, for normal requests and for SOAP requests (when a model is being used as a WSDL web service provider). The log includes the number of requests and the average latency for those requests. The latency numbers are in milliseconds. These log entries include a total for all requests, and a breakdown for each model and each top-level action in the model. Example: WebAppRequests: 19 Latency: 772 WebAppRequests/employees_demo/portlets/Summary: 6 Latency: 720 WebAppRequests/employees_demo/portlets/Summary WebAppRequests/employees_demo/portlets/Summary WebAppRequests/employees_demo/portlets/EmployeeDepartmentsSummary: 1 Latency: 1593 WebAppRequests/employees_demo/portlets/EmployeeDepartmentsSummary WebAppRequests/employees_demo/portlets/Detail: 6 Latency: 130 WebAppRequests/employees_demo/portlets/Detail WebAppRequests/employees_demo/portlets/Detail/EmployeeDetail: 2 WebAppRequests/employees_demo/portlets/EmployeePortlet: 6 WebAppRequests/employees_demo/portlets/EmployeePortlet
|
||||||
Description: These entries are used to track information about calls to external systems, for SAP, Domino, Portal Document Manager, and SQL (relational database). Similar to WebAppRequests, these entries show a breakdown for different instances or servers, and they show latency on the server also. The latency is the average for the number of requests shows, in milliseconds. Note that the SQL statistics are not logged by default. To enable them, you have to select the "Log Server Stats" option in the SQL Call builder. Example: DominoViewRequests: 7 Latency: 187 DominoViewRequests/localhost:80: 7 Latency: 187 DominoViewRequests/localhost:80/DominoTutorialEmployees.nsf: 7 Latency: 187 This shows that there were 7 calls made to get data from Domino. The average latency for those calls was 187 milliseconds. You can also see that in this case, all Domino data came from the same server (localhost:80), and from the same Domino database (DominoTutorialEmployees.nsf). Look for: Long latency times or an excessive number of calls. |
The below statistics have been provided for your reference only. They are not used as often as the ones above.
Description: This shows how many times sessions were restored from a failover. This will be zero unless failover is enable and there have been actual failover events (where it was necessary to reconstruct a session from one machine on another machine). Example: RestoredSessions: 0 |
|||
| ModelCacheRegenEntries Description: This is how many entries are currently in the regen cache of the server application. The default cache size is 2000, set with the following property in bowstreet.properties: bowstreet.cache.model.size=2000 Example: ModelCacheRegenEntries: 7 This indicates that there are 7 entries in the regen cache. If multiple profiles are in use, there will be an entry for each profile. |
|||
| OutputCacheHits OutputCacheMisses Description: This is a combination of schema caching information and caching used by Cache Control builder (and related functionality such as the caching option in Service Operation and Lookup Table builders). If Cache Control isn't being used, this shows just the caching statistics for schemas. Example: OutputCacheHits: 304 OutputCacheMisses: 2 Since Cache Control was not being used in this example application, this shows that there were 306 times when a schema was requested from source, and all but 2 of those came from cache. |
|||
| PeakSessions Description: This is usually the same as "Sessions". Example: PeakSessions:3 |
|||
| ParallelModelRequests Description: This shows information related to a fairly obscure builder, the Concurrent Model Initializer. Example: ParallelModelRequests: 0 |
|||
| WebAppMethodClassWritten WebAppJSPSourceWritten Description: These show how many times Java source file and JSP files were written to disk. Java and JSP files generated by Portlet Factory are written to disk only when needed, and are not written if the current file on disk matches the requested/generated file contents. Example: WebAppMethodClassWritten: 1 WebAppJSPSourceWritten: 1 This shows that during the 5-minute snapshot interval, one Java class was written and compiled, and one JSP file was written and compiled. |
|||
| ProfileSet cache hits ProfileSet cache misses Profile cache hits Profile cache misses Profile Set cache hits Profile Set cache misses Description: These entries show information about cache hits and misses in the Portlet Factory profiling system. Normally these will show a very good ratio of hits to misses, unless profile values are being updated very frequently. Example: ProfileSet cache hits: 0 ProfileSet cache misses: 0 Profile cache hits: 6 Profile cache misses: 1 Profile Set cache hits: 18 Profile Set cache misses: 0 |
The following is a sample entry from the serverStats.txt log.
-
*-- [datetime] --*
Category: bowstreet.system.server.logging.serverStats.default
Priority: INFO
Thread: ServerStatsThread
Msg: Sessions: 3
RestoredSessions: 0
ModelCacheRegenEntries: 7
Regens: 21
RegensFromCache: 15
OutputCacheHits: 304
OutputCacheMisses: 2
MemTotal: 759495168
MemFree: 111456936
MemInUse: 648038232
ErrorsLogged: 0
SevereErrorsLogged: 0
WarningsLogged: 0
PeakSessions: 3
ParallelModelRequests: 0
WebAppRequests: 19 Latency: 772
WebAppRequests/employees_demo/portlets/Summary: 6 Latency: 720
WebAppRequests/employees_demo/portlets/Summary/_bowstreet_show_current_page: 4 Latency: 1077
WebAppRequests/employees_demo/portlets/Summary/_gen_call_pbAction_EmployeeDetail: 2 Latency: 7
WebAppRequests/employees_demo/portlets/EmployeeDepartmentsSummary: 1 Latency: 1593
WebAppRequests/employees_demo/portlets/EmployeeDepartmentsSummary/_bowstreet_show_current_page: 1 Latency: 1593
WebAppRequests/employees_demo/portlets/Detail: 6 Latency: 130
WebAppRequests/employees_demo/portlets/Detail/_bowstreet_show_current_page: 4 Latency: 148
WebAppRequests/employees_demo/portlets/Detail/EmployeeDetail: 2 Latency: 94
WebAppRequests/employees_demo/portlets/EmployeePortlet: 6 Latency: 1330
WebAppRequests/employees_demo/portlets/EmployeePortlet/_bowstreet_show_current_page: 6 Latency: 1330
WebAppSOAPRequests: 0
WebAppMethodClassWritten: 1
WebAppJSPSourceWritten: 1
WebAppsInstantiated: 15
ProfileSet cache hits: 0
ProfileSet cache misses: 0
Profile cache hits: 6
Profile cache misses: 1
Profile Set cache hits: 18
Profile Set cache misses: 0
SAPFunctionCalls: 0
ContentModelFindDocuments: 0
ContentModelGetDocument: 0
DominoViewRequests: 7 Latency: 187
DominoViewRequests/localhost:80: 7 Latency: 187
DominoViewRequests/localhost:80/DominoTutorialEmployees.nsf: 7 Latency: 187
The log4j.properties file contains properties for setting the logging level (defaults to INFO), setting the File that the information is logged to (serverStats.txt by default) and the format of the log entries.
From log4j.properties:
# these configure Server Stats logging
# dont inherit the Console
log4j.additivity.bowstreet.system.server.logging.serverStats= false
log4j.logger.bowstreet.system.server.logging.serverStats= INFO,ServerStats
# -------------- The ServerStats appender sends its output to serverStats.txt --------------
log4j.appender.ServerStats=org.apache.log4j.FileAppender
log4j.appender.ServerStats.File=${bowstreet.rootDirectory}/logs/serverStats.txt
# Truncate the logging file if it already exists.
log4j.appender.ServerStats.Append=true
# Appender ServerStats uses the PatternLayout.
log4j.appender.ServerStats.layout=org.apache.log4j.PatternLayout
log4j.appender.ServerStats.layout.ConversionPattern=*-- TIME: [%d] --* %nCategory: %c%nPriority: %p%nThread: %t%nMsg: %m%n%n%n
The logging.properties file also contains server stats related properties. The file contains properties for enabling server stats logging, setting the snapshot interval and setting filters. The server stats logging is enabled by default and the snapshot interval is set to 300 seconds (5 minutes).
From the logging.properties file:
#--------------------------
# Server Stats Properties
#--------------------------
# do you want to enable serverStats logging?
logging.serverStats.enabled=true
# time interval to poll serverStats information
logging.serverStats.snapshotInterval=300
| Builder Calls |
After using Model Actions to analyze a model or portlet, if you discover that the model has a lengthy regeneration time and you cannot determine the cause of the slowness, you can use the Builder Calls logging to analyze regeneration. If the regeneration time is acceptable you can skip this logging.
The logging should be configured in the same way as the Model Actions logging:
1. Open the WEB-INF/config/log4j.properties file.
2. Find the line: log4j.logger.bowstreet.system.builderCalls=WARN,Console,ModelActions
3. Set the builderCalls property to DEBUG.
4. The line should now be: log4j.logger.bowstreet.system.builderCalls=DEBUG,Console,ModelActions
5. Restart the server
When a model regenerates, information will be written to the builderCalls.txt file. The file can be found in the deployed application's WEB-INF\logs directory. It is a good idea to clean out this file between regenerations since a single regeneration logs a significant amount of information.
To get regeneration information for a model written to the builderCalls.txt file, first close all other models in the designer, then close the designer and reopen it. This ensures that you are logging only the regeneration for a single model and that you are logging a first time regeneration. Subsequent regenerations of the model by clicking the regen button will only regenerate changes in the model.
The builderCalls.txt file is more difficult to analyze by simply looking at the raw output. The attached model can be used to analyze a builderCalls.txt file. Run the attached model and point it at a builderCalls.txt that has logged a single regeneration.
| Techniques for Improving Performance and Memory Use |
Performance cannot be corrected by a silver bullet. Improving performance of any application whether or not it uses WebSphere Portlet Factory can be a time consuming task. The performance of your application will not be improved much by a single change to the application. Instead, if you can identify several areas of the application that are performing poorly, this can make a noticeable difference.
This section of the technote will provide you with some design suggestion to improve performance. You should also consider reviewing Portal technotes that provide information for improving the performance of your Portal configuration.
If you have not already done some initial analysis using the techniques mentioned in the previous sections, you should do it now, so that you will have something to measure against once you make changes.
| Use Small Data Sets |
When retrieving data from a back-end, it is important to avoid fetching large data sets. The larger the data set, the more memory your application will use.
Instead of fetching all possible records, provide your user with a UI that will allow the user to fetch only the records they want to see. This reduces your data set and makes it easier on the user by limiting the number of records they need to browse through.
You can also take advantage of paging. This allows you to work with a larger data set, but in smaller chunks. Rather than fetching the data set all at once, the data is fetched a chunk at a time as the user pages through the data.
The Service Operation builder provides a setting to enable paging. Currently the SQL Call builder, the Domino Data Access builder and the Domino View & Form builder provide implementations for pageable data resources.
| Call a Method Once |
A widely used technique in WebSphere Portlet Factory applications is to use an indirect reference to supply a value for a page element. While this functions well, it can cause an unnecessary performance drain.
If you are using an indirect reference to a method to supply a value to a builder that appears on a page, this means that every time that page is rendered by every user, that method will be executed. If the value(s) generated by the method does not change during the running of the portlet, consider calling the method once and assigning the results to a variable. Then use an indirect reference to the variable instead. The method can be called in your main action, or if a builder is creating the main action you can add an event handler that calls your method OnWebAppLoad.
| Use AJAX |
WebSphere Portlet Factory 6.0.1 and WebSphere Dashboard Framework 6.0 both have new AJAX builders.
These builders can be used to improve performance and to make your application UI more interactive. For example, you can design application UIs that provide the user with instant feedback as they complete fields in a form, rather than waiting for the entire form to be completed.
You should be familiar with AJAX technology before trying to implement it in your WPF portlets however. AJAX will not work automatically in all cases and you should understand the limitations before you begin working with the AJAX builders.
The following are some known limitations:
- Portlet Factory and Dashboard Framework AJAX builders make use of a separate servlet to update pieces of a page rather than an entire page. This means that Portal APIs are not available in areas of the page enabled to use AJAX.
- Content Model Access is not available in an area of the page that is AJAX enabled.
- Portal URL creation is not available in an area of the page that is AJAX enabled.
- WebSphere Portal Link must be outside AJAX regions
- WebSphere Cooperative Portlet Source must be outside AJAX regions.
| Use Runtime Caching |
Runtime caching is probably one of the best ways to improve runtime performance of Portlet Factory portlets. The idea here is to enable your portlet actions to get data once and hold onto that copy rather than fetching the data each time it is needed. The down side of course is that you will not have the most current version of your data when using caching. Therefore as the application designer, you must decide where it makes sense to enable runtime caching in your application and where you need real-time data.
There are several different ways to enable caching in a Portlet Factory application.
The easiest way to enable caching is to do so in the Service Operation builder. (If you are not already using a service provider model to get your back-end data, you should consider implementing a provider/consumer design.) The Service Operation builder has a checkbox in the Result Caching section. Enabling this checkbox will turn on caching for this service and allow you to specify a refresh interval.
Use the Cache Control builder to cache the results of any WebSphere Portlet Factory method. These can be methods generated by WebSphere Portlet Factory builders or methods that you have written yourself. The one requirement here is that your method must return a value. Methods that update a variable and return a void will not work with the Cache Control builder.
The Lookup Table builder also allows you to cache information retrieved from a database call.
| Use Schema Caching |
Builder calls that provide integration to a back-end get information from the back-end at design time about the data's structure. These builder calls obtain a schema from the back end. This schema provides information about the data's structure. By default, the schema is obtained once when the builder call is first created and then the schema is cached. Caching the schema eliminates the need to get the schema from the back-end during regeneration. This improves performance of the model regeneration. If you find that your model is taking a long time to regenerate, verify that you have schema caching enabled.
The following builders allow you to turn on schema caching:
- SAP Function Call
- SAP View and Form
- Content Model Access
- Domino Data Access
- Domino View and Form
- PeopleSoft Component Interface
- Siebel Business Component
- SQL Transform to XML Schema (set Generate Schema = Only when SQL builder changes are detected)
- SQL Call (set Schema Regen Time = Only when SQL builder changes are detected)
| Changing the Scope of Model Variables |
You can determine the failover behavior for variables by choosing a setting for the Persist Choice property in the Variable builder call editor. You can choose from the following values:
Read-Write. This setting prompts the model to save the current value for the variable in the case of a failover event, provided that the model's Enable Failover property is checked. This failover functionality does incur a small performance hit, so only use the Default setting when necessary.
Read-Write but not persisted for failover - This setting allows your variable's value to change, but its value at the time of failover will not be preserved.
Read-only: shared across all users - This setting makes your variable behave like a constant. Its value will never change from the initial setting. As a result, some performance gains are realized because a copy of the variable is not made during generation and the variable is not processed during the failover event.
Request Scoped - This setting will store the variable value in a request attribute rather than in a class (WebAppInstanceData). Doing this can improve performance for large variables that are set and read within a single request, and not needed across multiple requests.
Note: Do not use request scoped for items such as XML variables that are used in multiple requests. Doing so would clone and initialize the request copy of that variable for every use. Use request scoped for infrequently used variables and variables that hold large objects or hierarchies of objects (such as XML). A typical use of request scoped might be for a variable that contains data for a table that is displayed on only one page of a multi-page application.
| Logging |
WebSphere Portlet Factory is handled by log4j, which gives the advantage of allowing you to produce debug logging with the change of a single property rather than having to comment out or delete several Syste.out.println() statements.
If you have been using System.out.println() statements to debug your code, be sure to remove them to increase performance.
If you have set any of the WebSphere Portlet Factory logging to a DEBUG level, consider setting these logging properties to a higher level to eliminate unnecessary logging. Refer to the WebSphere Portlet Factory Help for more details about log4j.
| Service Definition Stub |
Creating a Service Stub model is very useful during development as it allows UI development to be done without a connection to the back-end systems.
IMPORTANT: When using the Service Operation builder to create a stub, avoid using the automatic feature. If the automatic feature is used, it could introduce a performance problem where the Stub model is generated whenever the provider model regenerates.
To avoid this, enter a name for the stub model and click the generate stub button. This will cause the stub model to be generated once when the button is clicked and it will not be generated again unless the button is clicked again.
| XML Schema Issue |
If you are using a version of WebSphere Portlet Factory prior to version 6.0.1, then you may be affected by a regeneration performance issue related to the XML schema. See the following technote for details: "Models and Portlets take over a minute to load the first time" (#1257017)
| Rerun Performance Logging |
After each adjustment you make, consider running performance logging again. This will allow you to see the significance of each change you make.
It is also suggested that you use a performance load test tool to simulate real world usage of the application. This can help you to identify issues with your application design, portal configuration or hardware configuration.
Related information
Upgrade Central: Planning your upgrade to WebSphere Por
MustGather: WebSphere Portlet Factory version 6.0.x
WebSphere Portlet Factory product support
Rate this page:
Copyright and trademark information
IBM, the IBM logo and ibm.com are trademarks of International Business Machines Corp., registered in many jurisdictions worldwide. Other product and service names might be trademarks of IBM or other companies. A current list of IBM trademarks is available on the Web at "Copyright and trademark information" at www.ibm.com/legal/copytrade.shtml.