Improve Your ADF Fusion Application's Response Time by as Much as 70 Percent Frank Houweling (AMIS, The Netherlands) ODTUG Kscope13 1 Agenda • • • • • • • • • • Why is performance so important ? Overview typical ADF bottlenecks What happens too slowly What happens too often What happens too soon Too little Too big Demo (examples) AMIS ADF Performance Monitor Summary performance tips Customers do not accept slow applications anymore Overview typical bottlenecks (1) What happens too slowly: • ViewObject queries • PL-SQL calls from ApplicationModules • EntityObject DML operations • ApplicationModule passivation & activation • ApplicationModule transactions What happens too often: • ViewObject queries (more than 1 of the same VO during a request) • ViewObject queries caused by unintentionally left iterator bindings in pageDefs • Database roundtrips • HTTP requests • Loading too much database rows in ADF BC memory • ApplicationModule passivation / activation • Logging that could have been turned off (or less) Overview typical bottlenecks (2) What happens too soon: • Taskflow executions • Loading (immediate versus lazy) Too little: • Caching • JVM heap space Too big: • Too big scope for managed beans • Too much HTML sent to browser Agenda • Why is performance so important ? • Overview typical ADF bottlenecks • What happens too slowly Slow database executions • Many bottlenecks are simply caused by slow ViewObject queries, PL-SQL calls or EntityObject DML operations • Often hard to track which queries are slow • Some queries are only slow for certain bind parameters • Some queries are not slow during a development phase but are slow in a production environment (that has much more data) RDBMS Measure execution time of ViewObject queries Override executeQueryForCollection() in project base class during development Output in JDeveloper server log: Other ways to detect slow database executions (1) • Oracle ADF Logger diagnostic tool in JDeveloper Other ways to detect slow database executions (2) • Set up a database trace -Queries from database perspective -Disadvantage: database executions not from ADF application’s perspective, often not easy to relate database trace to ADF executions • AppDynamics (general JEE performance monitor) -Could be useful to monitor slow requests and see slow database executions -Problem: looks at the request URL to distinguish http request and business transactions. ADF uses often the same URL for many http requests Agenda • • • • Why is performance so important ? Overview typical ADF bottlenecks What happens too slowly What happens too often Too often executed ViewObject queries • More than 1 execution of the same VO during a single http request (!) • (Unintentionally) programmed by developer • • • • • • Often caused by: Inefficient set refresh property of iterator binding in pageDef ViewAccessor’s queries that are used for lookup items in af:table, af:treeTable or af:tree components Default implementation of a <af:treetable> or <af:tree> with associations and viewlinks executes each detail VO node in a mini query Custom ADFBC overrides and programmatically executed iterators Nested <af:iterators> in <af:iterators> on iterator bindings that bind to ViewObjects with associations and viewlinks Master-detail-detail behavior with ADFBC with associations and viewlinks PageDef iterator binding refresh property • Refreshing an iterator binding reconnects it with its underlying • • • • • RowSetIterator object (=ViewObject) Default value is deferred. This means ‘on demand’: the binding will not be executed unless its value is accessed In most cases: leave it at default (=deferred) Be very careful with refresh=“IfNeeded” - is the root cause of many unnecessary queries. It means: refresh and re-execute the query when any of the bind parameter values have changed Use RefreshCondition if you want to refresh query conditionally Don’t use refresh=“Always” - the underlying ViewObject will re-execute multiple times during a http request Too often executed ViewObject mini-queries (1) • Default implementation of <af:treetable> or <af:tree> with associations and viewlinks executes each detail VO node query and is a potential bottleneck • Solution: create a custom managed bean responsible for building the hierarchical structure in Java - it retrieves the data from the BindingContainer and manages the page • Replace the multiple queries with one single query with nodeId’s and parentNodeId’s Too often executed ViewObject mini-queries (2) • Nested <af:iterators> in <af:iterators> on iterator bindings that bind to ViewObjects with associations and viewlinks cause multiple mini-queries • Solution: create a custom managed bean responsible for building the hierarchical structure in Java en that retrieves the data from the BindingContainer and manages the page Too often executed ViewObject mini-queries (3) • Custom ADFBC overrides and programmatically execute iterators can have unexpected behavior – many mini queries • Every getter method in a ViewRowImpl can be called multiple times during a request Fetching the data of the Pension fund for the web application < > select * from employers where id = < 324> 1 record 100s records select * from participants where employer_id = < 324> 10s records select * from benefits where participant_id = <#> Reporting on many employers select * from employers 100s records 1 query 10k records select * 100s from participants queries where employer_id = <#> 100k select * records from benefits 10k queries where participant_id = <#> Single bulk retrieve replacing multiple queries • Have the database bulk up the data retrieval • Return Ref Cursor, Types and Collections or JSON/XML Benefits Package select * from employers where id in <some set> select * from participants where employer_id in <some set> select b.* from benefits b join participants p on (p.id = b.participant_id) where p.employer_id in <some set> Unintentionally left iterators in pageDefs • ViewObject queries caused by unintentionally left iterators in pageDefs • Iterator bindings can still be refreshed and the ViewObject unnecessary executed - for example when you have Refresh=“ifNeeded”) Too many database roundtrips (1) • The ViewObject fetch mode and fetch size properties (ViewObject General Tab - Tuning section) controls how many rows will be returned in each round-trip to and from the database Too many database roundtrips (2) • ViewObject fetchMode and fetchSize are underestimated • • • • • properties and have big performance impact The default value is 1 - will give poor performance (unless only one row will be fetched) Set the in Batches of value to gain performance efficiencies. Rule of thumb: If you are displaying n rows at a time in the user interface, set the fetch size to at least n + 1, so that each page of results can be retrieved in a single round-trip to and from the database For selection lists where you need all the records set fetchMode to All at once As you increase this value, however, you also increase the client-side buffer requirements, so you shouldn't just set this number arbitrarily high Too many HTTP Requests (1) • The iterator binding rangesize property represents the current set of objects to be displayed on the page • Rule of thumb: for af:tables, af:treetable and af:tree components set the rangesize to the max number of records that you can display in the browser, not more (usually not more than 50) • If you display 30 records and your rangesize=25 (default), than 2 http request from the client are needed to show the 30 records (!) • For selection lists where you need all records set rangesize to -1 Too many HTTP Requests (2) • Make use of the powerful ADF AJAX capabilities • Set where possible on all af:command<xxx> components (buttons, links, menu-items, etc) partialSubmit="true“ and immediate="true" (check the 18 lessons of ADF and JSF interaction of Steven Davelaar) • If you don’t use partialSubmit="true“ than the whole page is refreshed • More http requests could be sent to the server than needed HTTP Requests Demo • World’s most inefficient HR application ! Too much data in ADFBC memory (1) • Try to avoid loading more database rows than you need • Be careful if you really do need to load a proportional number of database rows Too much data in ADFBC memory (2) • Database rows are cached in the ViewRowStorage and EntityCache • If you query a proportional number of database rows • • • • many database rows memory consumption can be high Use accessmode=Range Paging if you have to display +- more than 250 records in an af:table, af:treeTable or af:tree Query resource intensive data only on demand (BLOBS, CLOBS, etc.) Limit the ViewObject query result rows by bind parameters where possible Use read-only ViewObjects where update is not needed (if EO based: deselect the updatable checkbox Demo ViewObject Range Paging Too frequent ApplicationModule passivation & activation (1) • Application module (AM) pooling enables multiple users to share several application module instances. It may involve saving and retrieving session state data from the database. This mechanism is provided to make the application scalable and becomes very important under high load with many concurrent users Too frequent ApplicationModule passivation & activation (2) • Often during development the AM pooling settings are not considered important and left at their default. • On a production environment, not setting these parameters correct (and leaving them at the default values) can be very inefficient and may cause many unneeded passivations and activations • Carefully read the documentation in the ADF Fusion developers Guide (Ch. 44 of the 11gR2 Fusion Developer Guide) ApplicationModule pooling guidelines (1) Recommended by Oracle Fusion Guide: Oracle’s rule of thumb for setting the AM pool size is to set the maxavailablesize and the recyclethreshold parameters to the expected number of peak concurrent users that perform multiple operations with short think times: • jbo.ampool.maxavailablesize = jbo.recyclethreshold • jbo.ampool.minavailablesize = 80 % of jbo.ampool.maxavailablesize • jbo.ampool.doampooling=true (default) • jbo.doconnectionpooling=false (default) This avoids application module instantiation time when load increases the hit is taken at server startup. This also avoids recycling (passivation cycle) of AM under normal load. Be careful: setting the maxavailablesize and recyclethreshold too high can result in a (too) big memory consumption (Java heap space) and can cause out-of-memory-exceptions. ApplicationModule pooling guidelines (2) Recommended by Duncan Mills: • Increase jbo.ampool.maxinactiveage • Set jbo.ampool.timetolive = -1 Results in more available AMs and avoid passivation/ activation costs More recommended by Oracle Fusion Guide: • If your highest concern and priority is to limit the number of database connections, set jbo.doconnectionpooling=true (in combination with jbo.txn.disconnect_level=1), otherwise leave it at false (default) • Option for ADF applications with proportionally many root AMs, many users and many database connections for each user Too many database connections • Monitor the number of database connections in the Weblogic Console • set jbo.doconnectionpooling=true and jbo.txn.disconnect_level=1 Too much logging on • Switch to SEVERE at the WLS / EM level Agenda • • • • • Why is performance so important ? Overview typical ADF bottlenecks What happens too slowly What happens too often What happens too soon Executed too soon (1) • Example: af:panelTabbed component with in each af: showDetailItem an af:region that starts a taskflow execution Executed too soon (2) .jsff page: PageDefinition: Executed too soon (3) • Use childCreation="lazyUncached“ or childCreation="lazy“ to defer taskflow execution of all tabs until the tab is opened • For popups, use childCreation="deferred” • For tasklows in regions, use activation="conditional" and a RefreshCondition on the taskflow executable Instantiated too soon • Defer the runtime instantiation of ViewObject and nested ApplicationModule instances until the time they are used Loaded too soon Immediate versus lazy Important to consider for the following components: • • • • af:table, af:treeTable, af:tree af:popup af:menu dvt<xxx> (graphs) • Lazy delivery should be used for tables, or other stamped components, which are known to have a slow fetch time (when they show many records, or the query is slow) Design your pages smart • Do not display too much data on a page, keep your page design simple if possible • Some organizations do still have old browsers (IE7) that can handle limited amounts of HTML and JavaScript • Do not unnecessarily query data that is not immediately needed (unopened tree nodes, inactive tabs, invisible popups, unopened dropdown lists, etc.) Example bad practice ADF10g app 30% 15% 5% 35% ? 15% Agenda • • • • • • Why is performance so important ? Overview typical ADF bottlenecks What happens too slowly What happens too often What happens too soon Too little Too little Caching • Use a shared application module to group view instances when you want to reuse lists of static data across the application • Use application scope for application scope managed beans Too little JVM Heap size Recommended by Duncan Mills: • Set (–Xms–Xmx) as large as possible within available physical memory • Generational parallel garbage collection strategy is recommended to maximize throughput: -Xgc:genpar (JRockit) Agenda • • • • • • • Why is performance so important ? Overview typical ADF bottlenecks What happens too slowly What happens too often What happens too soon Too little Too big Too big scope for managed beans • Use as small memory scopes as possible Too much table rows to browser • fetchsize on af:table, af:treeTable and af:tree components defines the number of rows sent to the browser • Should be (and is default) the same as the iterator rangesize in the pageDef • Bad practice: setting a high fetchsize on af:table, af:treeTable or af:tree component. As a result, the browser gets a big http response and has to build up a very big DOM tree (example: 3,9 MB http response (!) ) Too much HTML to the browser (1) Make IDs of the following ADF faces container components as small as possible (max 2 characters): • • • • • • • af:pageTemplate af:region af:panelCollection af:table af:treetable af:tree af:iterator Too much HTML to the browser (2) • Monitor HTTP traffic in browser (for example firebug) • Look for big files HTML Too much HTML to the browser (3) • Make the container component IDs as small as possible, for example: <af:pageTemplate id="t“ > <af:panelCollection id="c“ > <af:treeTable id=”utt” > <af:region id="r4“ > HTML Summary ADF Performance Tips 1. Smart application design - do not unnecessarily query data that is not immediately needed (unopened tree nodes, inactive tabs, invisible popups, unopened dropdown lists, etc.) 2. Query resource intensive data only on demand (BLOBS, CLOBS, etc.) 3. Detect and avoid multiple unnecessary ViewObject query executions 4. Detect and tune ViewObject slow queries 5. Set efficient ViewObject fetchsizes 6. Set efficient PageDefinition Iterator rangesizes 7. Use partialSubmit=true and immediate = true where possible on: af:commandButton, af:commandImageLink, af:commandLink, af:commandMenuItem, af:commandNavigationItem, af:commandToolbarButton. 8. If you are working with more than 500 records in tables, set the ViewObject property accessmode to ‘Range paging’ 9. Make IDs of ADF faces container components (af:pageTemplate, af:region, af:panelCollection, af:table, af:treetable, af:tree) as small as possible 10. Learn about (and try out) the most efficient Application Module pooling settings for your specific situation ADF Performance Monitor Resources • • • • Fusion Middleware Performance and tuning guide AMIS technology blog Blogs by Duncan Mills Blogs by Andrejus Baranovskis