{"id":572,"date":"2012-02-20T12:51:13","date_gmt":"2012-02-20T10:51:13","guid":{"rendered":"http:\/\/hasselba.ch\/blog\/?p=572"},"modified":"2012-02-20T12:58:54","modified_gmt":"2012-02-20T10:58:54","slug":"performance-killer-in-der-xpage","status":"publish","type":"post","link":"https:\/\/hasselba.ch\/blog\/?p=572","title":{"rendered":"Performance-Killer in der XPage"},"content":{"rendered":"<p>Dank einer Frage von Ulrich Krause im <a title=\"ibm.com: Partial refresh is executed on IDs that are not part of the partial refreh\" href=\"http:\/\/www-10.lotus.com\/ldd\/xpagesforum.nsf\/topicThread.xsp?action=openDocument&amp;documentId=56E9B8537DA50A90852579A6002EAC64\" target=\"_blank\">XPages Developer Forum<\/a> wurde ein Thema &#8222;wiederbelebt&#8220;, das mir vor einiger Zeit in einem Projekt aufgefallen ist und sich als wahre Bremse bei XPages-Applikationen herausstellt:<\/p>\n<p>S\u00e4mtliche Datenquellen (DataContext-Variablen, Repeat Controls, usw.) werden bei <span style=\"text-decoration: underline;\">jedem<\/span> Partial Refresh neu berechnet, auch wenn sie nicht das Ziel (bzw. ein Kind-Element des Ziels) des Refreshs sind.<\/p>\n<p>Ich nutze als Beispiel hierf\u00fcr die <a title=\"ibm.com: Partial refresh is executed on IDs that are not part of the partial refreh\" href=\"http:\/\/www-10.lotus.com\/ldd\/xpagesforum.nsf\/topicThread.xsp?action=openDocument&amp;documentId=56E9B8537DA50A90852579A6002EAC64\" target=\"_blank\">von Ulrich Krause gepostete Beispiel-XPage<\/a>, um das Problem zu verdeutlichen. Den fett markierten Code ist von mir zur Verdeutlichung auf der Serverkonsole eingebaut worden, dass es sich nicht um einen fehlerhaftes print-Statement handelt, sondern der Code wirklich neu berechnet wird.<\/p>\n<pre>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;xp:view xmlns:xp=\"http:\/\/www.ibm.com\/xsp\/core\"&gt;\r\n &lt;xp:button value=\"Label\" id=\"button1\"&gt;\r\n &lt;xp:eventHandler event=\"onclick\" submit=\"true\"\r\n refreshMode=\"partial\" refreshId=\"col1\" \/&gt;\r\n &lt;\/xp:button&gt;\r\n &lt;xp:table&gt;\r\n &lt;xp:tr&gt;\r\n &lt;xp:td id=\"col1\"&gt;\r\n &lt;xp:text escape=\"true\" id=\"computedField2\"\r\n   value=\"#{javascript:@Now()}\"&gt;\r\n &lt;xp:this.converter&gt;\r\n &lt;xp:convertDateTime type=\"both\" \/&gt;\r\n &lt;\/xp:this.converter&gt;\r\n &lt;\/xp:text&gt;\r\n &lt;\/xp:td&gt;\r\n &lt;xp:td id=\"col2\"&gt;\r\n &lt;xp:text escape=\"true\" id=\"computedField1\"\r\n   value=\"#{javascript:@Now()}\"&gt;\r\n &lt;xp:this.converter&gt;\r\n &lt;xp:convertDateTime type=\"both\" \/&gt;\r\n &lt;\/xp:this.converter&gt;\r\n &lt;\/xp:text&gt;\r\n &lt;xp:dataTable id=\"dataTable1\" rows=\"30\"&gt;\r\n &lt;xp:this.value&gt;&lt;![CDATA[#{javascript:\r\n print(<strong>java.lang.System.currentTimeMillis()<\/strong> + ' You shall not refresh');\r\n}]]&gt;&lt;\/xp:this.value&gt;\r\n &lt;xp:column id=\"column1\" \/&gt;\r\n &lt;\/xp:dataTable&gt;\r\n &lt;\/xp:td&gt;\r\n &lt;\/xp:tr&gt;\r\n &lt;\/xp:table&gt;\r\n&lt;\/xp:view&gt;<\/pre>\n<p>Wird der Button geklickt, wird ein Refresh auf das Element mit der Id &#8222;col1&#8220; ausgel\u00f6st, und eigentlich d\u00fcrfte die Data Table nicht neu berechnet werden, doch genau das ist aber der Fall!<\/p>\n<p><em>[Die Ausgabe auf der Serverkonsole habe ich mit einem Debug PhaseListener kombiniert, um die Phasen im Lifecycle hervorzuheben]<\/em><\/p>\n<h2>Button<\/h2>\n<pre>&lt;xp:button value=\"Label\" id=\"button1\"&gt;\r\n   &lt;xp:eventHandler event=\"onclick\" submit=\"true\"\r\n      refreshMode=\"partial\" refreshId=\"col1\" \/&gt;\r\n&lt;\/xp:button&gt;<\/pre>\n<h2>1. \u00d6ffnen der XPage<\/h2>\n<pre>19.02.2012 18:02:17\u00a0 \u00a0HTTP JVM: Before phase: RENDER_RESPONSE 6\r\n19.02.2012 18:02:17\u00a0 \u00a0<strong>HTTP JVM: 1329670937472 You shall not refresh<\/strong>\r\n19.02.2012 18:02:17\u00a0 \u00a0HTTP JVM: After phase: RENDER_RESPONSE 6<\/pre>\n<h2>2. Partial Refresh<\/h2>\n<pre>19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: Before phase: RESTORE_VIEW 1\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: After phase: RESTORE_VIEW 1\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: Before phase: APPLY_REQUEST_VALUES 2\r\n19.02.2012 18:05:37\u00a0 \u00a0<strong>HTTP JVM: 1329671137733 You shall not refresh<\/strong>\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: After phase: APPLY_REQUEST_VALUES 2\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: Before phase: PROCESS_VALIDATIONS 3\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: After phase: PROCESS_VALIDATIONS 3\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: Before phase: UPDATE_MODEL_VALUES 4\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: After phase: UPDATE_MODEL_VALUES 4\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: Before phase: INVOKE_APPLICATION 5\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: After phase: INVOKE_APPLICATION 5\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: Before phase: RENDER_RESPONSE 6\r\n19.02.2012 18:05:37\u00a0 \u00a0<strong>HTTP JVM: 1329671137769 You shall not refresh<\/strong>\r\n19.02.2012 18:05:37\u00a0 \u00a0HTTP JVM: After phase: RENDER_RESPONSE 6<\/pre>\n<p>Die Berechung der DataTable erfolgt zwei Mal w\u00e4hren des Partial Refreshs.<\/p>\n<p>\u00c4ndert man die Ausf\u00fchrung des Buttons auf Partial Excecution, erh\u00e4lt man folgendes Ergebnis:<\/p>\n<h2>Button<\/h2>\n<pre>&lt;xp:button value=\"Label\" id=\"button1\" execMode=\"partial\"&gt;\r\n   &lt;xp:eventHandler event=\"onclick\" submit=\"true\"\r\n   refreshMode=\"partial\" refreshId=\"col1\" \/&gt;\r\n&lt;\/xp:button&gt;<\/pre>\n<h2>1. \u00d6ffnen der XPage<\/h2>\n<pre>19.02.2012 18:13:54\u00a0 \u00a0HTTP JVM: Before phase: RENDER_RESPONSE 6\r\n19.02.2012 18:13:54\u00a0 \u00a0<strong>HTTP JVM: 1329671634490 You shall not refresh<\/strong>\r\n19.02.2012 18:13:54\u00a0 \u00a0HTTP JVM: After phase: RENDER_RESPONSE 6<\/pre>\n<h2>2. Partial Refresh<\/h2>\n<pre>19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: Before phase: RESTORE_VIEW 1\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: After phase: RESTORE_VIEW 1\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: Before phase: APPLY_REQUEST_VALUES 2\r\n19.02.2012 18:14:02\u00a0 \u00a0<strong>HTTP JVM: 1329671642321 You shall not refresh<\/strong>\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: After phase: APPLY_REQUEST_VALUES 2\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: Before phase: PROCESS_VALIDATIONS 3\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: After phase: PROCESS_VALIDATIONS 3\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: Before phase: UPDATE_MODEL_VALUES 4\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: After phase: UPDATE_MODEL_VALUES 4\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: Before phase: INVOKE_APPLICATION 5\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: After phase: INVOKE_APPLICATION 5\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: Before phase: RENDER_RESPONSE 6\r\n19.02.2012 18:14:02\u00a0 \u00a0<strong>HTTP JVM: 1329671642360 You shall not refresh<\/strong>\r\n19.02.2012 18:14:02\u00a0 \u00a0HTTP JVM: After phase: RENDER_RESPONSE 6<\/pre>\n<p>Wieder erfolgt die Berechung der DataTable zwei Mal w\u00e4hren des Partial Refreshs.<\/p>\n<p>Eine \u00c4nderung des Execution-Modes des Events verringert die Berechnung zumindest auf eine Neuberechnung pro Partial Refresh:<\/p>\n<h2>Button<\/h2>\n<pre>&lt;xp:button value=\"Label\" id=\"button1\" execMode=\"partial\"&gt;\r\n\u00a0 \u00a0&lt;xp:eventHandler event=\"onclick\" submit=\"true\"\r\n   refreshMode=\"partial\" refreshId=\"col1\" execMode=\"partial\"\/&gt;\r\n&lt;\/xp:button&gt;<\/pre>\n<h2>1. \u00d6ffnen der XPage<\/h2>\n<pre>19.02.2012 18:23:17\u00a0 \u00a0HTTP JVM: Before phase: RENDER_RESPONSE 6\r\n19.02.2012 18:23:17\u00a0 \u00a0<strong>HTTP JVM: 1329672197161 You shall not refresh<\/strong>\r\n19.02.2012 18:23:17\u00a0 \u00a0HTTP JVM: After phase: RENDER_RESPONSE 6<\/pre>\n<h2>2. Partial Refresh<\/h2>\n<pre>19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: Before phase: RESTORE_VIEW 1\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: After phase: RESTORE_VIEW 1\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: Before phase: APPLY_REQUEST_VALUES 2\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: After phase: APPLY_REQUEST_VALUES 2\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: Before phase: PROCESS_VALIDATIONS 3\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: After phase: PROCESS_VALIDATIONS 3\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: Before phase: UPDATE_MODEL_VALUES 4\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: After phase: UPDATE_MODEL_VALUES 4\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: Before phase: INVOKE_APPLICATION 5\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: After phase: INVOKE_APPLICATION 5\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: Before phase: RENDER_RESPONSE 6\r\n19.02.2012 18:23:18\u00a0 \u00a0<strong>HTTP JVM: 1329672198875 You shall not refresh<\/strong>\r\n19.02.2012 18:23:18\u00a0 \u00a0HTTP JVM: After phase: RENDER_RESPONSE 6<\/pre>\n<p>Selbst das Setzen der\u00a0<em>execId<\/em>\u00a0auf\u00a0<em>col1<\/em>\u00a0\u00e4ndert nichts &#8211; der Refresh f\u00fchrt immer zu einer Neuberechnung der DataTable.<\/p>\n<p>Da diese Neuberechnung bei jedem Partial Refresh ausgel\u00f6st wird, werden Applikationen extrem ausgebremst. Daher sollte die Berechnung der Datenquelle wenn m\u00f6glich auf &#8222;<em>Computed On Page Load<\/em>&#8220; gesetzt werden.<\/p>\n<p>Wenn dies nicht m\u00f6glich ist, habe ich hier ein kleines Snippet, das die Berechnung nur dann durchf\u00fchrt, wenn die Datenquelle auch wirklich refresht wird:<\/p>\n<pre>&lt;xp:repeat id=\"repeat1\" rows=\"30\" var=\"rCol\"&gt;\r\n\u00a0\u00a0 &lt;xp:this.value&gt;&lt;![CDATA[#{javascript:\r\n\u00a0\u00a0 \u00a0 \u00a0function calculate(){\r\n\u00a0\u00a0 \u00a0 \u00a0 \u00a0 var data:java.util.Vector = new java.util.Vector();\r\n\u00a0\u00a0 \u00a0 \u00a0 \u00a0 data.add(java.lang.System.currentTimeMillis());\r\n\u00a0\u00a0 \u00a0 \u00a0 \u00a0 return data;\r\n\u00a0\u00a0 \u00a0 \u00a0}\r\n\r\n\u00a0\u00a0 \u00a0 \u00a0if( viewScope.containsKey(\"data\") == false){\r\n\u00a0\u00a0 \u00a0 \u00a0 \u00a0 viewScope.data = calculate();\r\n\u00a0\u00a0 \u00a0 \u00a0 \u00a0 return viewScope.data;\r\n\u00a0\u00a0 \u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0 \u00a0if( com.ibm.xsp.ajax.AjaxUtil.isAjaxPartialRefresh(facesContext) === true ){\r\n\u00a0\u00a0 \u00a0 \u00a0 \u00a0 var pId = com.ibm.xsp.ajax.AjaxUtil.getAjaxComponentId( facesContext \u00a0);\r\n\u00a0\u00a0 \u00a0 \u00a0 \u00a0 if( pId !== getClientId( 'repeat1' ) )\r\n\u00a0\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0return viewScope.data;\r\n\u00a0\u00a0 \u00a0 \u00a0}\r\n\r\n\u00a0\u00a0 \u00a0 \u00a0viewScope.data = calculate();\r\n\u00a0\u00a0 \u00a0 \u00a0viewScope.data}]]&gt;\r\n\u00a0\u00a0 &lt;\/xp:this.value&gt;\r\n\u00a0\u00a0 &lt;xp:label value=\"#{javascript:rCol }\" id=\"label1\"&gt;&lt;\/xp:label&gt;\r\n&lt;\/xp:repeat&gt;<\/pre>\n<p><em>[Hier ein Beispiel mit einem Repeat Control]<\/em><\/p>\n<p>Die Version ist recht simpel aufgebaut, eine verbesserte Version werde ich in den n\u00e4chsten Tagen als XSnippet ver\u00f6ffentlichen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dank einer Frage von Ulrich Krause im XPages Developer Forum wurde ein Thema &#8222;wiederbelebt&#8220;, das mir vor einiger Zeit in einem Projekt aufgefallen ist und sich als wahre Bremse bei XPages-Applikationen herausstellt: S\u00e4mtliche Datenquellen (DataContext-Variablen, Repeat Controls, usw.) werden bei &hellip; <a href=\"https:\/\/hasselba.ch\/blog\/?p=572\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29,26,27,76,74],"tags":[34,33,7,30,88,86,87,5,3],"class_list":["post-572","post","type-post","status-publish","format-standard","hentry","category-expression-language","category-jsf","category-performance","category-ssjs","category-xpages","tag-8-5-2","tag-8-5-3","tag-domino","tag-el","tag-expression-language","tag-jsf","tag-performance","tag-ssjs","tag-xpages"],"_links":{"self":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/572","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=572"}],"version-history":[{"count":4,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/572\/revisions"}],"predecessor-version":[{"id":575,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/572\/revisions\/575"}],"wp:attachment":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=572"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=572"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=572"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}