540 likes | 701 Views
Improve Your ADF Fusion Application's Response Time by as Much as 70 Percent. Frank Houweling (AMIS, The Netherlands) ODTUG Kscope13 . Agenda. Why is performance so important ? Overview typical ADF bottlenecks What happens too slowly What happens too often What happens too soon Too little
E N D
Improve Your ADF Fusion Application's Response Time by as Much as 70 Percent Frank Houweling (AMIS, The Netherlands) ODTUG Kscope13
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
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 1 record < > select * from employers where id = < 324> 100s records select * from participants where employer_id = < 324> 10s records select * from benefits where participant_id = <#>
Reporting on many employers 100s records select * from employers 1 query 10k records select * from participants where employer_id = <#> 100s queries 100k records select * from benefits where participant_id = <#> 10k queries
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
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 soonImmediate 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% 35% 15% ? 15% 5%
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