<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-25740336</atom:id><lastBuildDate>Wed, 16 May 2012 15:23:43 +0000</lastBuildDate><category>varchar</category><category>Fine-Grained Auditing</category><category>enq: RO - fast object reuse</category><category>re-posting</category><category>PeopleTools 8.48</category><category>scalar queries</category><category>clob</category><category>Global Unique Identifier</category><category>row cache lock</category><category>Instrumentation</category><category>Dead Connect Detection</category><category>Append</category><category>Recyclebin</category><category>row source alias</category><category>Application Server</category><category>PeopleTools 8.49</category><category>PSPMSESSIONS_VW</category><category>unicode</category><category>performance</category><category>posting</category><category>Truncate</category><category>Stored Statements</category><category>Application Engine</category><category>Batch Timings</category><category>scalability</category><category>Hints</category><category>Report Reposiory</category><category>Restartability</category><category>local write wait</category><category>Stored Outlines</category><category>SQL Server 2005</category><category>global temporary tables</category><category>SQR</category><category>ASSM</category><category>Database Links</category><category>Temporary Records</category><category>DDL trigger</category><category>Materialized View</category><category>Privileges</category><category>Network Latency</category><category>Oracle Enterprise Manager</category><category>8.48</category><category>COBOL</category><category>v$session_longops</category><category>Freelist</category><category>view</category><category>%CurrentDateIn</category><category>column default values</category><category>Optimizer Dynamic Sampling</category><category>redo logging</category><category>nvision</category><category>Flashback Query</category><category>PeopleSoft Temporary Records</category><category>Grid Control</category><category>Unix Process Limits Memory Application Engine</category><category>Cache</category><category>Component Processor</category><category>Oracle 10g</category><category>DataGuard</category><category>SQL*Net</category><category>Multi-versioning</category><category>Partitions</category><category>trace "external table" directory</category><category>Parse</category><category>ASH</category><category>ReUse Statement</category><category>Plan Stability</category><category>Global Payroll</category><category>Statistics Retention</category><category>PeopeTools Tables</category><category>PeopleTools 8.50</category><category>Descending Indexs</category><category>Dirty Reads</category><category>Conference</category><category>DBMS_STATS</category><category>clone database</category><category>Record Locator Dialogue</category><category>Isolation Level</category><category>Tuxedo</category><category>Terminated Connection Timeout</category><category>Oracle SQL Trace Trigger</category><category>long</category><category>PeopleTools 8.1x</category><category>Dynamic Code</category><category>GetNextNumberWithGapsCommit</category><category>Performance Metrics</category><category>PS/Query</category><category>Performance Monitor</category><category>Load Balancing</category><category>serialisation</category><category>SQL Tuning</category><category>Null</category><category>sequences</category><category>Analyze</category><category>DDL</category><category>Lookup Exclusion</category><category>Locking</category><category>Read Consistency</category><category>SQL*Plus login prompt</category><category>Process Scheduler</category><category>Search Record</category><category>%UpdateStats</category><category>DBMS_APPLICATION_INFO</category><category>sparse index</category><category>Scheduled Queries</category><category>Redo</category><category>DDL Model</category><category>MetaSQL</category><category>Run Control</category><category>XML Reporting</category><category>Deferred Segment Creation</category><category>Descending Key</category><title>The PeopleSoft DBA Blog</title><description>This blog contains things about PeopleSoft that DBAs might find interesting.&lt;br&gt;Or then again they might not!&lt;br&gt;Non-PeopleSoft Oracle stuff is at &lt;a href="http://blog.go-faster.co.uk"&gt;blog.go-faster.co.uk&lt;/a&gt;.</description><link>http://blog.psftdba.com/</link><managingEditor>noreply@blogger.com (David Kurtz)</managingEditor><generator>Blogger</generator><openSearch:totalResults>90</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ThePeoplesoftDbaBlog" /><feedburner:info uri="thepeoplesoftdbablog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>ThePeoplesoftDbaBlog</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-37439287665260723</guid><pubDate>Sun, 26 Jun 2011 19:46:00 +0000</pubDate><atom:updated>2011-07-29T21:07:26.305+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Deferred Segment Creation</category><title>Deferred Segment Creation in PeopleSoft</title><description>This note has been in my to do folder for a while since I found these two excellent blog entries about &lt;a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e17120/tables002.htm#ADMIN13319" target="blank"&gt;Deferred Segment Creation&lt;/a&gt; by &lt;a href="http://antognini.ch/" target="_blank"&gt;Chistian Antognini&lt;/a&gt;.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://antognini.ch/2009/09/deferred-segment-creation/" target="_blank"&gt;http://antognini.ch/2009/09/deferred-segment-creation/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://antognini.ch/2010/10/deferred-segment-creation-as-of-11-2-0-2/" target="_blank"&gt;http://antognini.ch/2010/10/deferred-segment-creation-as-of-11-2-0-2/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;They made me think about the use of this feature in PeopleSoft.  In most PeopleSoft systems there are lots of empty tables, sometimes because not all the modules delivered in the database are in use, but also because not all the temporary table instances have ever been used.  This blog entry from Tom Kyte feels very close to home&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://tkyte.blogspot.com/2011/02/deferred-segment-creation.html" target="_blank"&gt;http://tkyte.blogspot.com/2011/02/deferred-segment-creation.html&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Deferred segment creation is available from Oracle 11gR1.  From  Oracle 11.2.0.2 this becomes the default, so there is no need to reconfigure anything in PeopleSoft in order use this Oracle feature.  You may want to drop empty segments created under previous versions of the database.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-37439287665260723?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/bzTQw2Vd5-4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/bzTQw2Vd5-4/deferred-segment-creation-in-peoplesoft.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2011/06/deferred-segment-creation-in-peoplesoft.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-8855328064521241524</guid><pubDate>Thu, 03 Mar 2011 23:30:00 +0000</pubDate><atom:updated>2011-10-11T23:32:36.920+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Process Scheduler</category><title>More Process Priority Levels for the Process Scheduler</title><description>This note started out as an idea for how to add more priority levels to the PeopleSoft Process Scheduler to improve control over prioritisation of processes in a complex batch. While testing I found some interesting behaviour that I had not expected.  I will also explain how I did some of my tests, because they are easily reproducible.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:large;"&gt;&lt;b&gt;More Priority Levels&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;By default, PeopleSoft process can be given priority 1 (low), 5 (medium) and 9 (high). If the Process Scheduler is too busy to start all the processes scheduled to be started, it starts the higher priority ones in preference to the lower priority.&lt;br /&gt;&lt;br /&gt;That’s fine, but sometimes in a very complex batch environment 3 process levels are not enough, and it would be useful to have more priority levels so that it is possible to define the hierarchy of processing to a finer level.&lt;br /&gt;&lt;br /&gt;There does not appear to be anything special about the priorities delivered by PeopleSoft. They do not appear to be hard coded anywhere within the SQL submitted Process Scheduler. As you might expect, some of the queries are sorted in descending order of priority.&lt;br /&gt;&lt;br /&gt;The priority of a process in the Process Scheduler is defined by the single character field &lt;a href="http://www.go-faster.co.uk/peopletools/prcsdefn.htm#PRCSPRIORITY" target="_blank"&gt;PRCSPRIORITY&lt;/a&gt; on the record &lt;a href="http://www.go-faster.co.uk/peopletools/prcsdefn.htm" target="_blank"&gt;PRCSDEFN&lt;/a&gt;. All you have to do is define addition translate values&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.go-faster.co.uk/images/prcspriorty_xlats.jpg"&gt;&lt;img src="http://www.go-faster.co.uk/images/prcspriorty_xlats.jpg" border="0" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;The new priorities can be assigned via the delivered components.&lt;br /&gt;&lt;br /&gt;I started with the intention of applying the new priority level to processes through the delivered component. However, I encountered some quirky behaviour from the Process Scheduler and Master Process Scheduler. There is something happening inside the programs rather than the SQL where they do not always take the process with the highest priority if that priority is not one of the delivered values. Therefore, I do not recommend using the new priorities on process definitions.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.go-faster.co.uk/images/prcsdefn_priority.jpg"&gt;&lt;img src="http://www.go-faster.co.uk/images/prcsdefn_priority.jpg" border="0" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;However, Server Categories also have priorities. Testing has shown that the priority of the Process Category take precedence over the priority on the Process Definition.&lt;br /&gt;&lt;br /&gt;The new server categories should be defined in the Process Category Administration page of the Process Scheduler System Settings component&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.go-faster.co.uk/images/category_admin.jpg"&gt;&lt;img src="http://www.go-faster.co.uk/images/category_admin.jpg" border="0" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Then priorities and maximum concurrencies can be assigned to the categories in each Process Scheduler Server definition.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.go-faster.co.uk/images/serverdefn_priority.jpg"&gt;&lt;img src="http://www.go-faster.co.uk/images/serverdefn_priority.jpg" border="0" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:large;"&gt;&lt;b&gt;Testing the Additional Priorities&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;I have (with some assistance) constructed a simple test harness. I have an Application Engine program, AE_SLEEP, that does nothing, but sleeps for a period of time by calling the Java Sleep() method in a PeopleCode step.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;Local JavaObject &amp;amp;Obj;Local ProcessRequest &amp;amp;RQST;MessageBox(0, "", 0, 0, "Sleep Begin for " | GFC_SLEEP_AET.DURATION.Value | " seconds ");&amp;amp;Obj = CreateJavaObject("java.lang.Thread");&amp;amp;Obj.start();&amp;amp;Obj.sleep(GFC_SLEEP_AET.DURATION.Value * 1000);MessageBox(0, "", 0, 0, "Sleep END");…&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;I have a number of other Application engine programs that have different priorities, or that are in categories with different priorities, that call AE_SLEEP.&lt;br /&gt;&lt;br /&gt;Finally there is a process AE_SLEEP_RUN which submits requests for the other processes based on a control table. In this test I will be setting up some test data to demonstrate how the Process Scheduler behaves, in the next article, I will be modelling the behaviour of a real batch load from a real system&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;TRUNCATE TABLE ps_gfc_sleep_rc/INSERT INTO ps_gfc_sleep_rc(run_cntl_id, prcstype, prcsname, offset_amount, duration, rundttm, last_run_cntl_id, next_run_cntl_id)SELECT level, 'Application Engine', 'AE_SLEEP'||level, 5*level, 175, NULL, ' ', ' 'FROM dual CONNECT BY level &amp;lt;= 9/COMMIT/ALTER SESSION SET NLS_DATE_FORMAT='hh24:mi:ss';set lines 120COLUMN run_cntl_id FORMAT a8 heading 'Run|Control'COLUMN last_run_cntl_id FORMAT a8 heading 'Last|Run|Control'COLUMN next_run_cntl_id FORMAT a8 heading 'Next|Run|Control'COLUMN prcsinstance FORMAT 99999 HEADING 'PRCS|INSTANCE'COLUMN offset_amount FORMAT 9999 HEADING 'Offset|Amount'COLUMN rownum FORMAT 99 HEADING 'Row|#'COLUMN prcstype FORMAT a18COLUMN prcsname FORMAT a12COLUMN prcsprty FORMAT 9 HEADING 'PRCS|PRTY'COLUMN prcscategory FORMAT a11 HEADING 'PRCS|CATEGORY'COLUMN serverassign FORMAT a6 HEADING 'SERVER|ASSIGN'COLUMN runstatus FORMAT a6 HEADING 'RUN|STATUS'SELECT run_cntl_id, prcsname, offset_amount, duration, rundttm FROM ps_gfc_sleep_rc/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;So the AE_SLEEP_RUN will schedule one process every 5 seconds. The  lowest priority processes are scheduled first.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;Run                            OffsetControl  PRCSNAME     RUNDTTM  Amount   DURATION-------- ------------ -------- ------ ----------1        AE_SLEEP1                  5        1752        AE_SLEEP2                 10        1753        AE_SLEEP3                 15        1754        AE_SLEEP4                 20        1755        AE_SLEEP5                 25        1756        AE_SLEEP6                 30        1757        AE_SLEEP7                 35        1758        AE_SLEEP8                 40        1759        AE_SLEEP9                 45        175&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;I can query what actually happened with the following SQL.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;SELECT rownum, a.* FROM (SELECT SYSDATE, q.prcsinstance, q.prcsname, q.prcsprty, q.rundttm, r.begindttm, r.enddttm, q.serverassign, q.runstatus--, r.prcscategoryFROM &lt;a href="http://www.go-faster.co.uk/peopletools/psprcsrqst.htm" target="_blank"&gt;psprcsrqst&lt;/a&gt; r, &lt;a href="http://www.go-faster.co.uk/peopletools/psprcsque.htm" target="_blank"&gt;psprcsque&lt;/a&gt; qWHERE r.prcsinstance = q.prcsinstanceAND r.prcsname like 'AE_SLEEP_'AND r.prcsinstance &amp;gt; (     SELECT MAX(prcsinstance) FROM psprcsrqst      WHERE prcsname = 'AE_SLEEP_RUN' AND runstatus = '9')ORDER BY r.begindttm, q.prcsprty DESC, q.rundttm, r.prcsinstance) a/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;In all of the examples below, the first three low priority process run first because they are scheduled to run and therefore start before the higher priority processes are scheduled. The Process Scheduler is then blocked until all the other processes are due to have run, and so priority determines while processes the scheduler starts next.&lt;br /&gt;&lt;br /&gt;However, due to a quirk in Process Scheduler, the processes are not assigned to the Process Scheduler in time order. In the following example AE_SLEEP5 was executed before AE_SLEEP6.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;Row           PRCS  # SYSDATE   INSTANCE PRCSNAME     RUNDTTM  BEGINDTTM ENDDTTM--- --------- -------- ------------ -------- --------- --------  1 13:14:17      4583 AE_SLEEP1    13:03:27 13:03:55  13:07:08  2 13:14:17      4584 AE_SLEEP2    13:03:32 13:03:55  13:07:08  3 13:14:17      4585 AE_SLEEP3    13:03:37 13:03:55  13:07:08  4 13:14:17      4591 AE_SLEEP9    13:04:07 13:07:12  13:10:27  5 13:14:17      4587 AE_SLEEP5    13:03:47 13:07:13  13:10:27  6 13:14:17      4588 AE_SLEEP6    13:03:52 13:07:13  13:10:27  7 13:14:17      4586 AE_SLEEP4    13:03:42 13:10:31  13:13:46  8 13:14:17      4589 AE_SLEEP7    13:03:57 13:10:47  13:14:01  9 13:14:17      4590 AE_SLEEP8    13:04:02 13:10:47  13:14:01&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;But if the non-standard priority jobs are put in a different category, they are executed in the right order. It would appear that there is some logic in the Master Process Scheduler (and not in the SQL queries it submits) that works category by category.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;Row              PRCS              PRCS                            SERVER RU  # SYSDATE  INSTANCE PRCSNAME     PRTY RUNDTTM  BEGINDTT ENDDTTM  ASSIGN ST PRCSCATEGORY--- -------- -------- ------------ ---- -------- -------- -------- ------ -- ------------  1 17:44:21     4681 AE_SLEEP5       5 17:31:10 17:31:24 17:34:39 PSNT   9  Default  2 17:44:21     4682 AE_SLEEP5       5 17:31:13 17:31:24 17:34:39 PSNT   9  Default  3 17:44:21     4683 AE_SLEEP5       5 17:31:16 17:31:40 17:34:56 PSNT   9  Default  4 17:44:21     4690 AE_SLEEP7       7 17:32:43 17:34:43 17:37:57 PSNT   9  Priority 9  5 17:44:21     4689 AE_SLEEP7       7 17:32:44 17:34:43 17:37:57 PSNT   9  Priority 9  6 17:44:21     4688 AE_SLEEP7       7 17:32:45 17:34:59 17:38:14 PSNT   9  Priority 9  7 17:44:21     4687 AE_SLEEP7       7 17:32:46 17:38:01 17:41:16 PSNT   9  Priority 9  8 17:44:21     4684 AE_SLEEP5       5 17:31:19 17:38:01 17:41:16 PSNT   9  Default  9 17:44:21     4685 AE_SLEEP5       5 17:31:22 17:38:17 17:41:33 PSNT   9  Default 10 17:44:21     4686 AE_SLEEP5       5 17:31:25 17:41:20 17:44:20 PSNT   9  Default&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;If there are two new priorities in the new category, then they do not run in the right order.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;Row              PRCS              PRCS                            SERVER RUN  # SYSDATE  INSTANCE PRCSNAME     PRTY RUNDTTM  BEGINDTT ENDDTTM  ASSIGN STATUS PRCSCATEGORY--- -------- -------- ------------ ---- -------- -------- -------- ------ ------ ------------  1 19:01:58     4716 AE_SLEEP5       5 18:06:58 18:07:26 18:10:40 PSNT   9      Default  2 19:01:58     4718 AE_SLEEP5       5 18:07:04 18:07:26 18:10:40 PSNT   9      Default  3 19:01:58     4719 AE_SLEEP5       5 18:07:07 18:07:26 18:10:40 PSNT   9      Default  4 19:01:58     4723 AE_SLEEP6       6 18:08:25 18:10:44 18:13:43 PSNT   9      Priority 9  5 19:01:58     4722 AE_SLEEP6       6 18:08:30 18:10:44 18:13:43 PSNT   9      Priority 9  6 19:01:58     4717 AE_SLEEP5       5 18:07:01 18:10:44 18:13:43 PSNT   9      Default  7 19:01:58     4724 AE_SLEEP7       7 18:08:40 18:14:02 18:17:00 PSNT   9      Priority 9  8 19:01:58     4725 AE_SLEEP7       7 18:08:45 18:14:02 18:17:00 PSNT   9      Priority 9  9 19:01:58     4720 AE_SLEEP5       5 18:07:10 18:14:02 18:17:00 PSNT   9      Default 10 19:01:58     4721 AE_SLEEP5       5 18:07:13 18:17:18 18:20:17 PSNT   9      Default&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;However, if I create a separate category for each priority, and assign the priority to that category in the server definition, then the processes run in the desired order.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;Row              PRCS              PRCS                            SERVER RUN  # SYSDATE  INSTANCE PRCSNAME     PRTY RUNDTTM  BEGINDTT ENDDTTM  ASSIGN STATUS PRCSCATEGORY--- -------- -------- ------------ ---- -------- -------- -------- ------ ------ ------------  1 19:38:00     4740 AE_SLEEP5       5 19:25:10 19:25:23 19:28:23 PSNT   9      Default  2 19:38:00     4741 AE_SLEEP5       5 19:25:13 19:25:39 19:28:38 PSNT   9      Default  3 19:38:00     4742 AE_SLEEP5       5 19:25:16 19:25:39 19:28:38 PSNT   9      Default  4 19:38:00     4748 AE_SLEEP7       7 19:26:52 19:28:41 19:31:41 PSNT   9      Priority 7  5 19:38:00     4749 AE_SLEEP7       7 19:26:57 19:28:58 19:31:56 PSNT   9      Priority 7  6 19:38:00     4747 AE_SLEEP6       6 19:26:37 19:28:58 19:31:56 PSNT   9      Priority 6  7 19:38:00     4746 AE_SLEEP6       6 19:26:42 19:32:00 19:34:59 PSNT   9      Priority 6  8 19:38:00     4743 AE_SLEEP5       5 19:25:19 19:32:16 19:35:15 PSNT   9      Default  9 19:38:00     4744 AE_SLEEP5       5 19:25:22 19:32:16 19:35:16 PSNT   9      Default 10 19:38:00     4745 AE_SLEEP5       5 19:25:25 19:35:02 19:37:59 PSNT   9      Default&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size:large;"&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;The Process Scheduler does not reliably take process priorities into account when scheduling processes and so I do not recommend using the additional priorities on processes, just the three delivered priorities (1, 5 and 9).&lt;br /&gt;&lt;br /&gt;However, all the priorities do work properly on process categories. The priority of the server class does not appear to have any effect (I have not produced results for those tests here).&lt;br /&gt;&lt;br /&gt;The priority of the category to which a process belongs takes precedence over the priority of the process. Thus a low priority process in a high priority category will be executed by the Process Scheduler in preference to a high priority process in a low priority category. Naturally if two processes are in the same category, or categories of equal priority, the higher priority process will be scheduled first.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:large;"&gt;&lt;b&gt;Acknowledgements&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;My thanks to Andy Mason of &lt;a href="http://www.business-integrations.com/" target="_blank"&gt;Business Integrations&lt;/a&gt; for contributing to the initial concept and the Java code to make Application Engine sleep and assistance with the test harness.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:large;"&gt;&lt;b&gt;Further Reading&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;I have &lt;a href="http://www.go-faster.co.uk/docs.htm#BatchModel.pdf" target="_blank"&gt;published a paper on my website&lt;/a&gt; that describes how to extend this technique to model a real production batch, and predict the impact of Process Scheduler configuration changes.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-8855328064521241524?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/Pr1QBfh4sng" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/Pr1QBfh4sng/more-process-priority-levels-for.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2011/03/more-process-priority-levels-for.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-7346491097619756716</guid><pubDate>Thu, 17 Feb 2011 19:26:00 +0000</pubDate><atom:updated>2011-03-07T17:50:00.582Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">Application Engine</category><category domain="http://www.blogger.com/atom/ns#">PeopleSoft Temporary Records</category><title>Unlocking Temporary Table Instances from Deleted Process Requests</title><description>In a &lt;a href="http://blog.psftdba.com/2009/02/do-you-need-more-temporary-table.html" target="_blank"&gt;previous blog entry&lt;/a&gt;, I discussed how to detect whether an Application Engine process had run out of non-shared Temporary Table instances by querying the message log table for the entries written when this happens.&lt;br /&gt;
&lt;br /&gt;
This can happen because processes that have locked all available non-shared table instances are currently processing.  However, if a restartable Application Engine program terminates with an error, the tables remain locked to that process instance so that it can be restarted.  If that process instance is neither cancelled nor restarted so that it subsequently runs to success then the tables will remain locked, and in time the Process Scheduler archive process may purge the process request record.&lt;br /&gt;
&lt;br /&gt;
However, that archive process does not unlock the temporary table instances (by deleting the registration of the table with the instance from the record &lt;a href="http://www.go-faster.co.uk/peopletools/aetemptblmgr.htm" target="_blank"&gt;AETEMPTBLMGR&lt;/a&gt;), and once the process request has been deleted it cannot be cancelled in the Process Monitor.  Therefore, there is no way for an operator to unlock the tables via the web interface.&amp;nbsp; Over time this can build up and new processes are forced to use the shared instance.&amp;nbsp; In my experience this is more common on development and test environments than production.&lt;br /&gt;
&lt;br /&gt;
The problem is that is a silent and insidious problem that can gradually degrade batch performance.&amp;nbsp; Forcing processes onto the shared instance forces them to delete rather than truncate working storage tables.&amp;nbsp; So there is additional redo, and high water marks are not reset.&amp;nbsp; Concurrent processes can contend as they share the same physical table.&amp;nbsp; There may be more work to preserve read consistency, and more activity on the undo segment. &lt;br /&gt;
&lt;br /&gt;
The way to prevent this problem is, of course, to make sure you have  sufficient instance of temporary table, but before you create new  instances check that there an no existing instances that should not  still be locked.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: xx-small;"&gt;&lt;i&gt;Update 7.3.2011:&lt;/i&gt;&lt;/span&gt; &lt;a href="http://blog.psftdba.com/2011/02/unlocking-temporary-table-instance-from.html#c7423112942731960395"&gt;Phil's  comment below&lt;/a&gt; correctly reminds me that PeopleSoft provided a way  to remove locks without issuing SQL directly at PeopleTools -&amp;gt;  Application Engine -&amp;gt; Review Temp Table Usage.&amp;nbsp; This component  clears records from &lt;a href="http://www.go-faster.co.uk/peopletools/aetemptblmgr.htm" target="_blank"&gt;PS_AETEMPTBLMGR&lt;/a&gt;, &lt;a href="http://www.go-faster.co.uk/peopletools/aeruncontrol.htm" target="_blank"&gt;PS_AERUNCONTROL&lt;/a&gt; and  &lt;a href="http://www.go-faster.co.uk/peopletools/aeruncontrolpc.htm" target="_blank"&gt;PS_AERUNCONTROLPC&lt;/a&gt;.&amp;nbsp; This feature is also described at &lt;a href="http://evmultimedia.ro/peoplenet/?p=147" target="_blank"&gt;PeopleSoft Tips &amp;amp; Notes: PS_AERUNCONTROL&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
However, you can only remove  locks for one process instance at a time.&amp;nbsp; If the problem has got out of hand, you might still chose to delete the rows from these three table where there is no longer a process request record.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;DELETE FROM ps_aetemptblmgr t
WHERE NOT EXISTS(
 SELECT 'x'
 FROM psprcsrqst r
 WHERE r.prcsinstance = t.process_instance)
/&lt;/pre&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;DELETE FROM ps_aeruncontrol t
WHERE NOT EXISTS(
 SELECT 'x'
 FROM psprcsrqst r
 WHERE r.prcsinstance = t.process_instance)
/&lt;/pre&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;DELETE FROM ps_aeruncontrolpc t
WHERE NOT EXISTS(
 SELECT 'x'
 FROM psprcsrqst r
 WHERE r.prcsinstance = t.process_instance)
/ &lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-7346491097619756716?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/_FQs0AVQUW0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/_FQs0AVQUW0/unlocking-temporary-table-instance-from.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>5</thr:total><feedburner:origLink>http://blog.psftdba.com/2011/02/unlocking-temporary-table-instance-from.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-3566058810930632781</guid><pubDate>Wed, 19 Jan 2011 12:13:00 +0000</pubDate><atom:updated>2011-01-22T10:05:18.934Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">PS/Query</category><title>Finding Unnecessary Effective Date Processing in PS/Query</title><description>In &lt;a target="_blank" href="http://www.psftdba.com"&gt;PeopleSoft for the Oracle DBA&lt;/a&gt; (Ch11, p309) I commented on how PS/Query will automatically add effective date criteria to and EFFDT column, even if it is not a key column.&amp;nbsp; &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.psftdba.com/images/ps4dba_11_11.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://www.psftdba.com/images/ps4dba_11_11.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
PS/Query does warn you that it has added the criteria, but it won't warn that the column is not a key column&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.psftdba.com/images/ps4dba_11_10.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://www.psftdba.com/images/ps4dba_11_10.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
If EFFDT is not part of the key, then it is unlikely that you will need this processing.&amp;nbsp; It is unlikely to change the result of the query, and it incurs additional work.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
Below is an example of the code added to a query by this one criteria.  The effective date sub-query will be correlated by the key columns prior to the EFFDT column.  If EFFDT is not part of the key, then all the key columns will be used for the correlation conditions.  There is only one row for each EMPLID and EMPL_RCD, and the effective date sub-queries will find and return that row, and the result set will remain the same.&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;...
AND ( a.effdt = 
        (SELECT MAX(a_ed.EFFDT) FROM ps_employees a_ed
        WHERE a.emplid = a_ed.emplid 
          AND a.empl_rcd = a_ed.empl_rcd 
          AND a_ed.EFFDT &amp;lt;= SYSDATE) 
AND a.effseq = 
        (SELECT MAX(a_es.EFFSEQ) FROM ps_employees a_es
        WHERE a.emplid = a_es.emplid 
          AND a.empl_rcd = a_es.empl_rcd 
          AND a.effdt = a_es.EFFDT) )
...&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
But how prevalent is this problem?&amp;nbsp; Like most things in PeopleSoft, Queries are stored in the PeopleTools tables.&amp;nbsp; And it is possible to construct a query to identify queries with effective date criteria on &lt;i&gt;EFFDT&lt;/i&gt; columns that are not part of the key on their record.  &lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;SELECT  c.oprid, c.qryname, r.recname, r.corrname, f.fieldname 
--,     useedit, bitand(d.useedit,1) 
FROM    &lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/psqrycriteria.htm"&gt;psqrycriteria&lt;/a&gt; c /*query crieria*/
,       &lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/psqryrecord.htm"&gt;psqryrecord&lt;/a&gt; r   /*records in queries*/
,       &lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/psqryfield.htm"&gt;psqryfield&lt;/a&gt; f    /*fields in a queries*/
,       &lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/psrecfielddb.htm"&gt;psrecfielddb&lt;/a&gt; d  /*fields on records, with sub-records fully expanded*/
WHERE   c.condtype BETWEEN 20 AND 25 /*effdt criteria, so no need to specify column name*/
AND     c.lcrtfldnum = f.fldnum 
-- 
AND     r.oprid = c.oprid 
AND     r.qryname = c.qryname 
AND     r.selnum = c.selnum 
-- 
AND     f.oprid = c.oprid 
AND     f.qryname = c.qryname 
AND     f.selnum = c.selnum 
-- 
AND     f.oprid = r.oprid 
AND     f.qryname = r.qryname 
AND     f.selnum = r.selnum 
AND     f.recname = r.recname 
-- 
AND     d.recname = f.recname 
AND     d.fieldname = f.fieldname 
AND     BITAND(d.useedit,1) = 0 /*a non-key field*/ &lt;/pre&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;ORDER BY 1,2 
/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
On my HR demo database the following delivered queries have effective date criteria on EFFDT columns that are no key columns.&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;OPRID    QRYNAME                        RECNAME         CORRN FIELDNAME
-------- ------------------------------ --------------- ----- ---------
         JPM_INACTIVE_CONTENT_ITEMS     JPM_JP_CRITM_VW A     EFFDT
         JPM_PERS_PROFILE               JPM_JP_CRITM_VW F     EFFDT
         JPM_PROFILE                    JPM_JP_CRITM_VW F     EFFDT
         NOT001                         FPAEEFLAT_TBL   A     EFFDT&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now, it is possible to review these queries and manually remove the effective date processing if that is appropriate.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-3566058810930632781?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/kAMkog9qTZM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/kAMkog9qTZM/finding-unnecessary-effective-date.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2011/01/finding-unnecessary-effective-date.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-5785071511472509139</guid><pubDate>Tue, 18 Jan 2011 08:02:00 +0000</pubDate><atom:updated>2011-01-22T10:06:41.042Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">Component Processor</category><category domain="http://www.blogger.com/atom/ns#">Locking</category><title>Row Level Locking in the PeopleTools Component Processor</title><description>My apologies in advance, but this entry is a bit of a rant.&amp;nbsp; It is one of those things in PeopleSoft about which you do nothing, but it still is interesting to know because it reveals something of how it works under the covers.&lt;br /&gt;
&lt;br /&gt;
In the &lt;a href="http://www.psftdba.com/" target="_blank"&gt;PeopleSoft for the DBA&lt;/a&gt;, I commented on how the columns updated in the UPDATE statements issued by the Component Processor at save time contain only the fields that have been updated in the component.&amp;nbsp; You get different update statements depending upon what you update in the component, but the alternative is to update all the columns, possibly to the same value, and that would generate additional unnecessary redo.&lt;br /&gt;
&lt;br /&gt;
PeopleSoft uses 'optimistic locking'.&amp;nbsp; That is to say that it hopes that the data in the database underlying the component hasn't changed between the operator querying data into the component and saving any changes.&amp;nbsp; At save time, the data is requeried so it can be compared with the results returned by the first query, but this time the row of data is locked by adding the FOR UPDATE clause.&amp;nbsp; The lock is released by the commit at the end of the save time processing.&amp;nbsp; You have the overhead of requerying the data, but it is likely to be in  the buffer cache after the first query.&amp;nbsp; However, this avoids the risks  of holding a database lock while the user is in the component.&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
This is a common approach in many applications.&amp;nbsp; It is not just reasonable, but essential for scalability.&amp;nbsp; The Component Processor never holds a lock while waiting for the user to respond.&amp;nbsp; This is also essential for the Tuxedo application server.&amp;nbsp; Each server process is stateless and each service request is atomic.&amp;nbsp; Each server process maintains a persistent database connection.&amp;nbsp; A database transaction is never left uncommitted at the end of a service request.&amp;nbsp; There is no guarantee that the next request from the same user will be handled by the same application server process, and it might have to handle requests from other users in the intervening period.&lt;br /&gt;
&lt;br /&gt;
However, PeopleSoft also dynamically generates the column list in the FOR UPDATE clause.&amp;nbsp; Here are two examples from the timesheet component in Time &amp;amp; Labor.&amp;nbsp; I have removed most of the select clause for readability.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;SQL_ID: 3vdfam8g3f7ca
SELECT EMPLID, EMPL_RCD, ...
FROM PS_TL_RPTD_TIME
WHERE EMPLID=:1 AND EMPL_RCD=:2 AND DUR=TO_DATE(:3,'YYYY-MM-DD') AND SEQ_NBR=:4
FOR UPDATE OF OPRID&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;SQL ID: d8b5sy4bcgyuh 
SELECT EMPLID, EMPL_RCD, ...
FROM PS_TL_RPTD_TIME
WHERE EMPLID=:1 AND EMPL_RCD=:2 AND DUR=TO_DATE(:3,'YYYY-MM-DD') AND SEQ_NBR=:4
FOR UPDATE OF PUNCH_END, OPRID, RT_SOURCE, OPRID_LAST_UPDT, DTTM_MODIFIED, TL_QUANTITY&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This is a totally unnecessary complexity.&amp;nbsp; Oracle (and other databases) employ row level locking.&amp;nbsp; They do not lock individual pieces of data. The row would be just as locked with one column in the FOR UPDATE clause as with many or any other.&amp;nbsp; PeopleSoft could simply have put the first column from the select clause into the FOR UPDATE clause.&amp;nbsp; Instead, we have different SQL statements with different SQL_IDs, and everything that goes with that.&lt;br /&gt;
&lt;br /&gt;
However, there is nothing you can do about this, just know about it. It shows the dynamic nature of the SQL generated by the Component Processor, and reveals how it must be tracking which fields have been updated by the user and the PeopleCode in a component.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-5785071511472509139?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/RnBBBxRwxeE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/RnBBBxRwxeE/row-level-locking-in-peopletools.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2011/01/row-level-locking-in-peopletools.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-4144737852447411151</guid><pubDate>Thu, 04 Nov 2010 20:56:00 +0000</pubDate><atom:updated>2011-01-04T23:22:35.716Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">PeopleTools 8.50</category><category domain="http://www.blogger.com/atom/ns#">DBMS_APPLICATION_INFO</category><category domain="http://www.blogger.com/atom/ns#">ASH</category><title>PeopleTools 8.50 uses DBMS_APPLICATION_INFO to Identify Database Sessions</title><description>I recently worked on a PeopleTools 8.50 system in production for the first time and was able to make use of the new Oracle specific instrumentation in PeopleTools.&lt;br /&gt;
&lt;br /&gt;
PeopleTools now uses the &lt;a href="http://download.oracle.com/docs/cd/E11882_01/appdev.112/e16760/d_appinf.htm#CHECEIEB" target="_blank"&gt;DBMS_APPLICATION_INFO&lt;/a&gt; package to set &lt;i&gt;module&lt;/i&gt; and &lt;i&gt;action&lt;/i&gt; session attributes.&amp;nbsp; This data is then copied into the Active Session History (ASH).&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Within the PIA, the application server sets module and action to the name of the current component and page within the current components&lt;/li&gt;
&lt;li&gt;For Integration Broker messages they are set to service and queue name.&lt;/li&gt;
&lt;li&gt;For Application Engine processes the module is set to PSAE and the action is set to the program name. &lt;/li&gt;
&lt;/ul&gt;The first two of these three changes are very welcome, but I think the instrumentation of Application Engine is inadequate.  I have a &lt;a href="http://blog.psftdba.com/2009/03/using-oracle-enterprise-manager-grid.html"&gt;better suggestion&lt;/a&gt; which can be implemented with a &lt;a href="http://www.go-faster.co.uk/scripts.htm#psftapi.sql" target="_blank"&gt;database trigger&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: large;"&gt;&lt;b&gt;Enterprise Manager&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
In Oracle Enterprise Manager, I can see the top SQL statements and group them by Module and Action, so I now can see which components are consuming the most time.&amp;nbsp; I can also produce an AWR for a specific component or page within a  component.&lt;br /&gt;
&lt;br /&gt;
&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://www.go-faster.co.uk/images/pt850oem.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://www.go-faster.co.uk/images/pt850oem.jpg" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;OEM Screenshot of a PeopleTools 8.50 system.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
I can query the ASH data to profile which pages consume the most time on the database.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;MODULE                 ACTION                   ASH_SECS 
---------------------- ---------------------- ---------- 
RECV_PO                PO_PICK_ORDERS                240 
XXX_REQ_INQUIRY        xyzzy                         170 
XXX_REQ_WRKLST         XXX_REQ_WORKLIST              170 
VCHR_EXPRESS           VCHR_LINE_RECV_WRK            170 
XXX_FIN_WORKLIST       XXX_FIN_WORKLIST              160 
VCHR_EXPRESS           VCHR_EXPRESS1                 160 
PURCHASE_ORDER_EXP     PO_EXPRESS                    140 
XXX_HOME_PAGE          XXX_HOME_PAGE                 140 
RECV_PO                RECV_WPO                      130 
VCHR_EXPRESS           xyzzy                         120 
XXX_PUR_WORKLIST       XXX_PUR_WRKLST                120 
CDM_RPT                CDM_RPT_INDEX                 100 
…
----------
sum                                                  2820&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;span style="font-size: xx-small;"&gt;(Updated 19.11.2010)&lt;/span&gt; For some components the action is set to ‘xyzzy’. This seems to be a default value set when the component is opened, but before any of the pages are processed.&amp;nbsp; Therefore, it refers to activity in the search dialogue, including processing of :&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;look ups to obtain values for search criteria&lt;/li&gt;
&lt;li&gt;SQL issued during SearchSave PeopleCode to validate the search criteria.&lt;/li&gt;
&lt;li&gt;the query on the Component Search record&lt;/li&gt;
&lt;/ul&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;Batch Processes&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
Now, I want to look at what happens in batch processes.  In previous versions of PeopleTools, the &lt;i&gt;module&lt;/i&gt; was set to the same value as the program name, and &lt;i&gt;action&lt;/i&gt; was left blank.  This is not particularly helpful.  In 8.50 &lt;i&gt;module&lt;/i&gt; is set to PSAE for Application Engine processes, and &lt;i&gt;action&lt;/i&gt; is set to the name of the program.  &lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;MODULE                       ACTION                      ASH_SECS
---------------------------- ------------------------- ----------
PSNVS.EXE                                                   10220
PSAE                         AP_PSTPYMNT                     3100
PSAE                         AP_MATCH                        2690
sqrw.exe                                                     1770
PSAE                         PO_RECVACCR                     1390
PSQRYSRV.exe                                                  880
PSAE                         FS_STREAMLN                      870
PSPUBDSP.exe                                                  850
PSBRKDSP.exe                                                  740
PSPRCSRV.exe                                                  690
PSSUBDSP.exe                                                  620&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This is certainly better than in previous versions. However, its weakness is that if I have multiple concurrent instances of the same process, although I could tell that the ASH data had come from different sessions, I would not be determine which came from which session.  This situation could occur, for example, in Global Payroll if ‘streaming’ was configured, where the payroll calculation can be broken into many processes that run concurrently.&lt;br /&gt;
&lt;br /&gt;
In another blog posting, &lt;a href="http://blog.psftdba.com/2009/03/using-oracle-enterprise-manager-grid.html"&gt;Using Oracle Enterprise Manager (Grid Control) with PeopleSoft&lt;/a&gt;, I proposed a trigger that writes name of the PeopleSoft process name to &lt;i&gt;module&lt;/i&gt;, and the Process Instance number into &lt;i&gt;action&lt;/i&gt;.  Furthermore, &lt;a href="http://www.go-faster.co.uk/scripts.htm#psftapi.sql" target="_blank"&gt;this trigger&lt;/a&gt; works for all processes scheduled by the Process Scheduler.&amp;nbsp; I think that that trigger is still useful in PeopleTools 8.50.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-4144737852447411151?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/0nNn924QtH8" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/0nNn924QtH8/peopletools-850-uses.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>3</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/11/peopletools-850-uses.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-3192311454484377800</guid><pubDate>Wed, 15 Sep 2010 07:13:00 +0000</pubDate><atom:updated>2010-09-15T08:13:37.933+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQR</category><category domain="http://www.blogger.com/atom/ns#">scalar queries</category><title>Performance Overhead of Multiple SQL calls in SQR</title><description>I was asked to look at a fairly simple SQR program that reported on payroll data.  It pivoted data for each employee on which it reported.  It makes 21 calls to two procedures that each obtain a value by summing data across different sets of data in one of two payroll result tables.&lt;br /&gt;
&lt;br /&gt;
The ASH data shows that most of the database time, 184 out of 192 seconds is spent in the two statements that aggregate that data.  These statements are in the procedures that are called within the driving query.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;SQL_ID        SQL_PLAN_HASH_VALUE  EXEC_SECS   ASH_SECS
------------- ------------------- ---------- ----------
515d3buvaf8us          1162018321        615        133
55a20fnkwv0ht          3972836246        615         51
...
                                             ----------
sum                                                 192&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
However, more significantly, only 192 seconds out of a total elapsed run time of 615 seconds is spent in the database.  That is just 31%.  So most of the time is spent executing code within the SQR program.&lt;br /&gt;
&lt;br /&gt;
We need to look at the code to see exactly what is happening.  &lt;br /&gt;
&lt;br /&gt;
This is the driving query.  It returns each employee who is paid in a specified payroll, and then for each row the procedure &lt;i&gt;get_gp_acum&lt;/i&gt; is used to fetch the sum of certain accumulators for that payroll and employee&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;Begin-Select
a.emplid
a.cal_run_id
a.cal_id
a.gp_paygroup
a.orig_cal_run_id
a.rslt_seg_num
a.tax_code_uk
a.gpgb_ni_categorye.
n.name

  Let $pin_name = 'GBR AC GRTX SEG'  !Taxable Gross 
  do get_gp_acum
  Let #Taxable_gross = &amp;amp;b.calc_rslt_val
  
  Let $pin_name = 'GBR AC NIBL SEG'  !Nlable Gross
  do get_gp_acum
  Let #Niable_gross1 = &amp;amp;b.calc_rslt_val
  
…
from ps_gpgb_ee_rslt a, ps_person_name n
where a.emplid = n.emplid
and a.cal_run_id = $PNL_CAL_RUN_ID1
and a.empl_rcd = 0
and a.gp_paygroup = $PNL_PYGRP1
and a.cal_id = $PNL_CAL_ID1
and a.orig_cal_run_id = a.cal_run_id
order by a.emplid,a.gp_paygroup
End-Select
End-Procedure&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This is one of the two procedures that is called to obtain each value.&amp;nbsp; It simply sums the data for that employee.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;Begin-Procedure get_gp_acum
begin-select
sum(b.calc_rslt_val) &amp;amp;b.calc_rslt_val
from ps_xx_gpacum_rpt1 b, ps_gp_pin c
where b.emplid    = &amp;amp;a.emplid
and b.empl_rcd    = 0
and b.cal_run_id  = &amp;amp;a.cal_run_id
and b.gp_paygroup = &amp;amp;a.gp_paygroup
and b.cal_id      = &amp;amp;a.cal_id
and b.orig_cal_run_id = &amp;amp;a.orig_cal_run_id
and b.rslt_seg_num    = &amp;amp;a.rslt_seg_num  
and b.orig_cal_run_id = b.cal_run_id
and b.pin_num = c.pin_num
and c.pin_nm = $pin_name
end-select
End-Procedure&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This code is very clear, well structured, and easy to maintain.  The only trouble is that it is slow.  Each SQL calls makes SQR do a lot of work, and that takes time.&lt;br /&gt;
&lt;br /&gt;
In this case there was not much procedural code in the SQR and so I was able to coalesce the SQL from the called procedures into the driving query.  &lt;br /&gt;
&lt;br /&gt;
If the called procedures had been simple single row look-ups I could have used an outer-join.  However, as they are using a group function (sum), I put the query into a scalar query (a query within in the select clause that returns only one row and one column).  Each call to a procedure was replaced with a separate scalar query.  I ended up with 21 scalar queries.&lt;br /&gt;
&lt;br /&gt;
During this rewrite I encounted an SQR quirk; if the scalar query was placed in the main select clause, SQR produces errors because it is expecting an expression, and it complains that the SELECT keyword is not a variable.  I then had to wrap the query in an in-line view.  Each scalar query must be given a column alias, and the column alias can be referenced in the SQR select clause.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;Begin-Procedure MAIN-REPORT
Begin-Select
a.emplid
a.cal_run_id
a.cal_id
a.gp_paygroup
a.orig_cal_run_id
a.rslt_seg_num
a.tax_code_uk
a.gpgb_ni_category
n.name

a.gbr_ac_grtx_seg
 Let #Taxable_gross = &amp;amp;a.gbr_ac_grtx_seg

a.gbr_ac_nibl_seg
 Let #Niable_gross1 = &amp;amp;a.gbr_ac_nibl_seg

…  
from (
select a.emplid, a.cal_run_id, a.cal_id, a.gp_paygroup, a.orig_cal_run_id, a.rslt_seg_num, a.tax_code_uk, a.gpgb_ni_category
,NVL((SELECT sum(b.calc_rslt_val) from ps_xx_gpacum_rpt1 b, ps_gp_pin c 
      where b.emplid = a.emplid and b.empl_rcd = 0 and b.cal_run_id = a.cal_run_id 
      and b.gp_paygroup = a.gp_paygroup and b.cal_id = a.cal_id 
      and b.orig_cal_run_id = a.orig_cal_run_id and b.rslt_seg_num = a.rslt_seg_num 
      and b.orig_cal_run_id = a.cal_run_id and b.pin_num = c.pin_num 
      and c.pin_nm = 'GBR AC GRTX SEG'),0) gbr_ac_grtx_seg
,NVL((SELECT sum(b.calc_rslt_val) from ps_xx_gpacum_rpt1 b, ps_gp_pin c 
      where b.emplid = a.emplid and b.empl_rcd = 0 and b.cal_run_id = a.cal_run_id 
      and b.gp_paygroup = a.gp_paygroup and b.cal_id = a.cal_id 
      and b.orig_cal_run_id = a.orig_cal_run_id and b.rslt_seg_num = a.rslt_seg_num 
      and b.orig_cal_run_id = a.cal_run_id and b.pin_num = c.pin_num 
      and c.pin_nm = 'GBR AC NIBL SEG'),0) gbr_ac_nibl_seg
…
from ps_gpgb_ee_rslt a
where a.empl_rcd = 0
and a.orig_cal_run_id = a.cal_run_id
) a
,ps_person_name n
where a.cal_run_id = $PNL_CAL_RUN_ID1
and a.gp_paygroup = $PNL_PYGRP1
and a.cal_id = $PNL_CAL_ID1
and a.emplid = n.emplid
order by a.emplid,a.gp_paygroup
End-Select&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The SQL looks much more complicated, as does the execution plan.&amp;nbsp; However, the effect on performance was dramatic.  &lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;SQL_ID        SQL_PLAN_HASH_VALUE  EXEC_SECS   ASH_SECS
------------- ------------------- ---------- ----------
f9d03ffbftv81          1694704409        154        114
5d2x9mqvvyrjk           989254841        154          2
3v550ghn6z8jv          1521271881        154          1
                                             ----------
sum                                                 117&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The response of just the combined SQL at 117 seconds is better than the separate SQLs at 154 seconds. Much more significantly, the amount of time spent in SQR (rather than the database) has fallen from 432 seconds to just 37.  Therefore, 90% of the SQR response time was spent on submitting the SQL calls in the called procedures.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Conclusions&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
SQL calls in SQR are expensive.  The cost of making lots of calls inside a loop or another driving query can add up to a significant amount of time.  SQRs that consume time in this way will also be consuming CPU and memory on the server where the Process Scheduler is located.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
In this case, combining SQL statements also improved SQL performance, but that will not always be the case.  &lt;br /&gt;
&lt;br /&gt;
There are times when better performance can be achieved at the cost of more convoluted code.  In each case there is a judgement to be made as to whether improvement in performance is worth the increase in complexity.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-3192311454484377800?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/YZExyDQsCnQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/YZExyDQsCnQ/performance-overhead-of-multiple-sql.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/09/performance-overhead-of-multiple-sql.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-8912562627197398463</guid><pubDate>Fri, 10 Sep 2010 11:45:00 +0000</pubDate><atom:updated>2010-09-10T12:55:01.131+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Run Control</category><title>PeopleSoft Run Control Purge Utility</title><description>Run Control records are used to pass parameters into processes scheduled processes.  These tables tend to grow, and are rarely purged.  Generally, once created a run control is not deleted.&amp;nbsp; When operator accounts are deleted, the Run Controls remain, but are no longer accessible to anyone else.&lt;br /&gt;
&lt;br /&gt;
I have worked on systems where new Run Controls, whose IDs contain either a date or  sequence number, are generated for each process.  The result is that the Run Control tables, especially child tables, grow quickly and if not regularly managed will become very large.  On one system, I found 18 million rows on one table!&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;RECNAME         FIELDNAME            NUM_ROWS     BLOCKS
--------------- ------------------ ---------- ----------
TL_RUN_CTRL_GRP RUN_CNTL_ID          18424536     126377
AEREQUESTPARM   RUN_CNTL_ID           1742676      19280
AEREQUESTTBL    RUN_CNTL_ID            333579       3271
XPQRYRUNPARM    RUN_CNTL_ID            121337       1630
TL_TA_RUNCTL    RUN_CNTL_ID            112920        622
…&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
I have written a simple Application Engine process, GFC_RC_ARCH, that purges old Run Controls from these tables.&amp;nbsp; It can be &lt;a href="http://www.go-faster.co.uk/scripts.htm#gfc_rc_arch" target="_blank"&gt;downloaded from my website&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Run Control records are easily identified.  They are characterised by: &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;the first column of these tables is always OPRID, and the second is either RUNCNTLID or RUN_CNTL_ID,&lt;/li&gt;
&lt;li&gt;these two columns are also the first two columns of the unique key,&lt;/li&gt;
&lt;li&gt;the Run Control tables appear on pages of components that are declared as the process security component for that process.&lt;/li&gt;
&lt;/ul&gt;I have decided that if the combination of OPRID and RUN_CNTL_ID does not appear in the process scheduler request table, PSPRCSRQST, then the Run Control record should be deleted.  Thus, as the delivered Process Scheduler Purge process, PRCSPURGE, deletes rows from the Process Scheduler tables, so my purge process will delete rows from the Run Control tables.   &lt;br /&gt;
&lt;br /&gt;
I have chosen to make these two Application Engine processes mutually exclusive, so the Process Scheduler will not run both at the same time, but that configuration cannot be delivered in an Application Designer project.&lt;br /&gt;
&lt;add picture=""&gt;&lt;/add&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-8912562627197398463?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/s1iFrpK1jNk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/s1iFrpK1jNk/peoplesoft-run-control-purge-utility.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>1</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/09/peoplesoft-run-control-purge-utility.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-5294365220435538172</guid><pubDate>Mon, 12 Jul 2010 14:14:00 +0000</pubDate><atom:updated>2010-07-12T17:00:50.172+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">PeopeTools Tables</category><title>Announcing the Co-Operative PeopleTools Table Reference</title><description>I have created a reference to the &lt;a href="http://www.go-faster.co.uk/peopletools/index.htm" target="_blank"&gt;PeopleTools tables and views&lt;/a&gt; on my website.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
In the course of my work on PeopleSoft, I spend a lot of time looking at the PeopleTools tables. They contain meta-data about the PeopleSoft application.  Much of the application is stored in various tables that are maintained by Application Designer.  Some tables provide information about the Data Model. Others contain configuration data that is maintained via PeopleTools components in the PIA.&lt;br /&gt;
&lt;br /&gt;
Many of my utility scripts query information from PeopleTools tables, and some also update them. Of course, that is strictly not supported, but if you understand how the tables fit together it can be done relatively safely.&amp;nbsp; So, it is very helpful to be able to understand what is in these tables.&lt;br /&gt;
&lt;br /&gt;
In &lt;a href="http://www.psftdba.com/" target="_blank"&gt;PeopleSoft for the Oracle DBA&lt;/a&gt;, I discussed some of the PeopleTools tables that are of regular interest.  I included the tables that correspond to the database catalogue, and I discussed what happens during the PeopleSoft login procedure, submission of process requests to the Process Scheduler and PS/Query.  The tables that are maintained by the process scheduler are valuable because they contain information about who ran what process when, and how long they ran.&lt;br /&gt;
&lt;br /&gt;
I am not the only person to have started to document the PeopleTools tables on their website or blog, most people have picked a few tables that are of particular interest.  However, I want to tackle the problem in a slightly different way.  There are over 3000 PeopleTools tables and views (as defined by the PeopleTools object security group in &lt;a href="http://www.go-faster.co.uk/peopletools/psobjgroup.htm" target="_blank"&gt;PSOBJGROUP&lt;/a&gt;).  Tackling all of them manually would be a monumental task. &lt;br /&gt;
&lt;br /&gt;
Nevertheless, I do want a complete reference.  So, I have written code to dynamically generate a page for each PeopleTools table and view, and I have put as much information about these records as I can find in the PeopleTools tables themselves.  Reference to related objects, including objects referenced in the text of views, appear as links to those pages.&lt;br /&gt;
&lt;br /&gt;
I have started to manually add my own annotation to the generated pages.&amp;nbsp; So far I have only added descriptions to a few tables (marked with an asterisk).  However, I would like to make this a collaborative project, and I have already had updates to some pages.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;There is a page for each PeopleTools table and view.  If you save that page, add descriptions, and return it to &lt;a href="mailto:info@go-faster.co.uk?subject=Submission%20to%20Co-Operative%20PeopleTools%20Tables%20Reference"&gt;me&lt;/a&gt;, I will upload it to the site.&lt;/li&gt;
&lt;li&gt;You can also add linked to related websites and blog pages to your entries.&lt;/li&gt;
&lt;li&gt;Please put your name and, if you wish, a link to your website to the bottom of the pages you author.&lt;/li&gt;
&lt;li&gt;Let me know if you think you have found a mistake.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;I hope you find it useful. &lt;br /&gt;
&lt;ul&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-5294365220435538172?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/wvIB2b_5sJE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/wvIB2b_5sJE/announcing-co-operative-peopletools.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>5</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/07/announcing-co-operative-peopletools.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-2880425044737331420</guid><pubDate>Thu, 17 Jun 2010 11:47:00 +0000</pubDate><atom:updated>2010-07-12T15:04:26.805+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Tuxedo</category><category domain="http://www.blogger.com/atom/ns#">Application Server</category><category domain="http://www.blogger.com/atom/ns#">Load Balancing</category><title>Configuring Large PeopleSoft Application Servers</title><description>Occasionally, I see very large PeopleSoft systems running on large proprietary Unix servers with many CPUs.&amp;nbsp; In an extreme case, I needed to configure application server domains with up to 14 PSAPPSRV processes per domain (each domain was on a virtual server with 8 CPU cores, co-resident with the Process Scheduler).&lt;br /&gt;
&lt;br /&gt;
The first and most important point to make is don't have too many server processes.&amp;nbsp; If you run out of CPU or if you fully utilise all the physical memory and start to page memory from disk, then you have too many server processes.&amp;nbsp; It is better to queue on a Tuxedo queue rather than the CPU run queue, or disk queue during paging.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Multiple APPQ/PSAPPSRV Queues&lt;/b&gt; &lt;br /&gt;
&lt;br /&gt;
A piece of advice that I originally got from BEA (prior to their acquisition by Oracle) was that you should not have more than 10 server processes on a single queue in Tuxedo.&amp;nbsp; Otherwise, you are likely to suffer from contention on the IPC queue structure because processes must acquire exclusive access to the queue in order to enqueue a service request to the queue or dequeue a request from it.&amp;nbsp; Instead multiple queues should be configured that are both serviced by the same server processes and so advertise the same services.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
If you look at the 'large' template delivered by PeopleSoft, you will see that it produces a domain that runs between 9 and 15 PSAPPSRV processes.&amp;nbsp; This does not conform to the advice I received from BEA.&amp;nbsp; I repeated this advice in PeopleSoft for the Oracle DBA.&amp;nbsp; Though I cannot now find the source for it, I stand by it.&amp;nbsp; I have recently been able to conduct some analysis to confirm it on a real production system.&amp;nbsp; Domains with two queues of 8 PSAPPPSRV server process each out performed domains with only a single queue.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Load Balancing Across Queues &lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
If the same service is advertised on multiple queues, then Tuxedo recommended that you should specify realistic service loads and use Tuxedo load balancing to determine where to enqueue requests.&amp;nbsp; I want to emphasise that I am talking about load balancing across queues within a Tuxedo domain, and &lt;u&gt;&lt;b&gt;not&lt;/b&gt;&lt;/u&gt; about load balancing across Tuxedo domains in the web server.&lt;br /&gt;
&lt;br /&gt;
This is what the Tuxedo documentation says about load balancing: &lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;"Load balancing is a technique used by the BEA Tuxedo system for distributing service requests evenly among servers that offer the same service. Load balancing avoids overburdening some servers while leaving others idle or infrequently used. Before sending a request to a service routine, the BEA Tuxedo system identifies all servers capable of handling the request and selects the one most appropriate for maintaining a balanced load across all the servers in the configuration.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt; &lt;/i&gt;&lt;br /&gt;
&lt;i&gt;You can control whether a load-balancing algorithm is used on the system as a whole. Such as algorithm should be used only when necessary, that is, only when a service is offered by servers that use more than one queue. Services offered by only one server, or by multiple servers in a Multiple Server, Single Queue (MSSQ) do not need load balancing. The LDBAL parameter for these services should be set to N. In other cases, you may want to set LDBAL to Y."&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
It doesn't state that load balancing is mandatory for multi-queue domains, and only hints that it might improve performance.&amp;nbsp; If load balancing is not used, the listener process puts the messages on the first empty queue (one where no requests are queued).&amp;nbsp; If all queues have requests the listener round-robins between the queues.&lt;br /&gt;
&lt;br /&gt;
You could consider giving &lt;i&gt;ICScript,&lt;/i&gt; &lt;i&gt;GetCertificate&lt;/i&gt; and other services with small service times a higher Tuxedo Service priority.&amp;nbsp; This means they jump the queue 9 times out of 10.&amp;nbsp; &lt;i&gt;ICScript&lt;/i&gt; is generally used during navigation, &lt;i&gt;GetCertificate&lt;/i&gt; is used at log on.&amp;nbsp; Giving these services higher priority will mean they perform well even when the system is busy.&amp;nbsp; Users often need to do several mouse clicks to navigate around the system, but these services are usually quick.&amp;nbsp; This will improve the user experience without changing the overall performance of the system.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Data&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
I have recently been able to test the performance of a domains with up to 14 PSAPPSRVs on a single IPC queue, versus domains with two queues with up to 7 PSAPPSRVs each, both with and without Tuxedo queue balancing.&amp;nbsp;&amp;nbsp; These results come from a real production system where the multiple queue configuration was implemented on 2 of the 4 application servers.&amp;nbsp; The system has a short-lived weekly peak period of on-line processing.&amp;nbsp; During that time Tuxedo spawns additional PSAPPSRV processes, and so I get different sets of times for different numbers of process.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
The timings are produced from transactions sampled by PeopleSoft Performance Monitor.&amp;nbsp; I capture the number of spawned processes using the &lt;a href="http://www.go-faster.co.uk/scripts.htm#tuxmon" target="_blank"&gt;Tuxmon&lt;/a&gt; scripts on my website that use &lt;i&gt;tmadmin&lt;/i&gt; to collect Tuxedo metrics.&lt;br /&gt;
&lt;br /&gt;
&lt;table border="0" cellpadding="0" cellspacing="0" id="table1" style="border-collapse: collapse;" x:str=""&gt;&lt;colgroup&gt;   &lt;col style="width: 51pt;"&gt;&lt;/col&gt;   &lt;col span="2" style="width: 48pt;"&gt;&lt;/col&gt;   &lt;col style="width: 51pt;"&gt;&lt;/col&gt;   &lt;col span="2" style="width: 48pt;"&gt;&lt;/col&gt;  &lt;/colgroup&gt;  &lt;tbody&gt;
&lt;tr height="17" style="height: 12.75pt;"&gt;   &lt;td colspan="3" style="border-color: windowtext black windowtext windowtext; border-style: solid; border-width: 1px 1px 0.5pt; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-align: center; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;1 Queue&lt;/td&gt;&lt;td colspan="3" style="border-bottom: 0.5pt solid windowtext; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: 0.5pt solid windowtext; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-align: center; text-decoration: none; vertical-align: bottom; white-space: nowrap;" width="196"&gt;2 Queue&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="69" style="height: 51.75pt;"&gt;   &lt;th style="border-color: -moz-use-text-color windowtext; border-style: none solid; border-width: medium 0.5pt medium 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: normal;"&gt;&lt;br /&gt;
Server Processes per Queue&lt;/th&gt;   &lt;th style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: solid solid none none; border-width: 1px 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: normal;"&gt;&lt;br /&gt;
Number of Services&lt;/th&gt;   &lt;th style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: solid solid none none; border-width: 1px 1px medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: normal;"&gt;&lt;br /&gt;
Mean ICPanel Service Time&lt;/th&gt;   &lt;th style="border-bottom: medium none; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: medium none; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: normal;"&gt;&lt;br /&gt;
Server Processes per Queue&lt;/th&gt;   &lt;th style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: none solid none none; border-width: medium 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: normal;"&gt;&lt;br /&gt;
Number of Services&lt;/th&gt;   &lt;th style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: none solid none none; border-width: medium 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: normal;"&gt;&lt;br /&gt;
Mean ICPanel Service Time&lt;/th&gt;  &lt;/tr&gt;
&lt;tr height="19" style="height: 14.25pt;"&gt;   &lt;td align="right" style="border-color: windowtext; border-style: double solid solid; border-width: 2pt 0.5pt 0.5pt 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
6&lt;/td&gt;   &lt;td align="right" style="border-color: windowtext windowtext windowtext -moz-use-text-color; border-style: double solid solid none; border-width: 2pt 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="2616"&gt;&lt;br /&gt;
2,616&lt;/td&gt;   &lt;td align="right" style="border-color: windowtext windowtext windowtext -moz-use-text-color; border-style: double solid solid none; border-width: 2pt 1px 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.33&lt;/td&gt;   &lt;td align="right" style="border-bottom: medium none; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: 2pt double windowtext; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
3&lt;/td&gt;   &lt;td align="right" style="border-color: windowtext windowtext -moz-use-text-color -moz-use-text-color; border-style: double solid none none; border-width: 2pt 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
6945&lt;/td&gt;   &lt;td align="right" style="border-color: windowtext windowtext -moz-use-text-color -moz-use-text-color; border-style: double solid none none; border-width: 2pt 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.05&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="19" style="height: 14.25pt;"&gt;   &lt;td align="right" height="19" style="border-color: -moz-use-text-color windowtext windowtext; border-style: none solid solid; border-width: medium 0.5pt 0.5pt 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; height: 14.25pt; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
7&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="1949"&gt;&lt;br /&gt;
1,949&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 1px 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
0.97&lt;/td&gt;   &lt;td style="border-bottom: 0.5pt solid windowtext; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: medium none; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;   &lt;td style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;   &lt;td style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="19" style="height: 14.25pt;"&gt;   &lt;td align="right" height="19" style="border-color: -moz-use-text-color windowtext windowtext; border-style: none solid solid; border-width: medium 0.5pt 0.5pt 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; height: 14.25pt; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
8&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="1774"&gt;&lt;br /&gt;
1,774&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 1px 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.06&lt;/td&gt;   &lt;td align="right" style="border-bottom: medium none; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: medium none; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
4&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: none solid none none; border-width: medium 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
7595&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: none solid none none; border-width: medium 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.16&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="19" style="height: 14.25pt;"&gt;   &lt;td align="right" height="19" style="border-color: -moz-use-text-color windowtext windowtext; border-style: none solid solid; border-width: medium 0.5pt 0.5pt 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; height: 14.25pt; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
9&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="1713"&gt;&lt;br /&gt;
1,713&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 1px 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.02&lt;/td&gt;   &lt;td style="border-bottom: medium none; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: medium none; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;   &lt;td style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: none solid none none; border-width: medium 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;   &lt;td style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: none solid none none; border-width: medium 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="19" style="height: 14.25pt;"&gt;   &lt;td align="right" height="19" style="border-color: -moz-use-text-color windowtext windowtext; border-style: none solid solid; border-width: medium 0.5pt 0.5pt 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; height: 14.25pt; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
10&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="1553"&gt;&lt;br /&gt;
1,553&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 1px 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.25&lt;/td&gt;   &lt;td align="right" style="border-bottom: medium none; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: 0.5pt solid windowtext; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
5&lt;/td&gt;   &lt;td align="right" style="border-color: windowtext windowtext -moz-use-text-color -moz-use-text-color; border-style: solid solid none none; border-width: 0.5pt 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
4629&lt;/td&gt;   &lt;td align="right" style="border-color: windowtext windowtext -moz-use-text-color -moz-use-text-color; border-style: solid solid none none; border-width: 0.5pt 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.17&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="19" style="height: 14.25pt;"&gt;   &lt;td align="right" height="19" style="border-color: -moz-use-text-color windowtext windowtext; border-style: none solid solid; border-width: medium 0.5pt 0.5pt 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; height: 14.25pt; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
11&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="1250"&gt;&lt;br /&gt;
1,250&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 1px 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.30&lt;/td&gt;   &lt;td style="border-bottom: 0.5pt solid windowtext; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: medium none; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;   &lt;td style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;   &lt;td style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="19" style="height: 14.25pt;"&gt;   &lt;td align="right" height="19" style="border-color: -moz-use-text-color windowtext windowtext; border-style: none solid solid; border-width: medium 0.5pt 0.5pt 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; height: 14.25pt; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
12&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
969&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 1px 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.32&lt;/td&gt;   &lt;td align="right" style="border-bottom: medium none; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: medium none; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
6&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: none solid none none; border-width: medium 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
3397&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext -moz-use-text-color -moz-use-text-color; border-style: none solid none none; border-width: medium 0.5pt medium medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.16&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="19" style="height: 14.25pt;"&gt;   &lt;td align="right" height="19" style="border-color: -moz-use-text-color windowtext windowtext; border-style: none solid solid; border-width: medium 0.5pt 0.5pt 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; height: 14.25pt; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
13&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
427&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 1px 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.21&lt;/td&gt;   &lt;td style="border-bottom: 0.5pt solid windowtext; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: medium none; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;   &lt;td style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;   &lt;td style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="19" style="height: 14.25pt;"&gt;   &lt;td align="right" height="19" style="border-color: -moz-use-text-color windowtext windowtext; border-style: none solid double; border-width: medium 0.5pt 2pt 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; height: 14.25pt; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
14&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid double none; border-width: medium 0.5pt 2pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="1057"&gt;&lt;br /&gt;
1,057&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid double none; border-width: medium 1px 2pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.10&lt;/td&gt;   &lt;td align="right" style="border-bottom: 2pt double windowtext; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: medium none; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
7&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid double none; border-width: medium 0.5pt 2pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
3445&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid double none; border-width: medium 0.5pt 2pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
1.13&lt;/td&gt;  &lt;/tr&gt;
&lt;tr height="18" style="height: 13.5pt;"&gt;   &lt;td style="border-color: -moz-use-text-color windowtext windowtext; border-style: none solid solid; border-width: medium 0.5pt 1px 1px; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;div style="text-align: right;"&gt;&amp;nbsp;Total&lt;/div&gt;&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 1px medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="13308"&gt;&lt;br /&gt;
13,308&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 1px 1px medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="1.1663818755635709"&gt;&lt;br /&gt;
1.17&lt;/td&gt;   &lt;td style="border-bottom: 0.5pt solid windowtext; border-left-color: inherit; border-right: 0.5pt solid windowtext; border-top: medium none; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;"&gt;&lt;br /&gt;
&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num=""&gt;&lt;br /&gt;
26011&lt;/td&gt;   &lt;td align="right" style="border-color: -moz-use-text-color windowtext windowtext -moz-use-text-color; border-style: none solid solid none; border-width: medium 0.5pt 0.5pt medium; color: windowtext; font-family: Arial; font-size: 10pt; font-style: normal; font-weight: 400; padding-left: 1px; padding-right: 1px; padding-top: 1px; text-decoration: none; vertical-align: bottom; white-space: nowrap;" x:num="1.13"&gt;&lt;br /&gt;
1.13&lt;/td&gt;  &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
The first thing to acknowledge is that this data is quite noisy because it comes from a real production system, and the effects we are looking for are quite small.&lt;br /&gt;
&lt;br /&gt;
I am satisfied that the domains with two PSAPPSRV queues generally perform better under high load, than those under 1.&amp;nbsp; Not only does the queue time increase on the single queue domain, the service time also increases.&lt;br /&gt;
&lt;br /&gt;
However, I cannot demonstrate that Tuxedo Load Balancing makes a significant difference in either direction.&lt;br /&gt;
&lt;br /&gt;
My results suggest that domains with multiple queues for requests  handled by PSAPPSRV process perform slightly better without load  balancing if there is no queue of requests, but perform slightly  better if there is a queue of pending requests.&amp;nbsp; However, the difference  is small.&amp;nbsp; It is not large enough to be statistically significant in my  test data.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
If you have a busy system with lots of on-line users, and sufficient hardware to resource it, then you might reach a point when you need more than 10 PSAPPSRVs.&amp;nbsp; In which case, I recommend that you configure multiple Tuxedo queues.&lt;br /&gt;
&lt;br /&gt;
On the whole, I would recommend that Tuxedo Load Balancing should be configured.&amp;nbsp; I would not expect it to improve performance, but it will not degrade it either.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-2880425044737331420?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/NBlSLUjDWig" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/NBlSLUjDWig/configuring-large-peoplesoft.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>10</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/06/configuring-large-peoplesoft.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-7411517549672039374</guid><pubDate>Fri, 11 Jun 2010 18:15:00 +0000</pubDate><atom:updated>2011-01-26T00:40:23.509Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">Flashback Query</category><category domain="http://www.blogger.com/atom/ns#">Process Scheduler</category><title>Life Cycle of a Process Request</title><description>Oracle's Flashback Query facility lets you query a past version of a row by using the information in the undo segment.&amp;nbsp; The VERSIONS option lets you seen all the versions that are available.  Thus, it is possible to write a simple query to retrieve the all values that changed on a process request record through its life cycle. &lt;br /&gt;
&lt;br /&gt;
The Oracle parameter &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/initparams222.htm#I1010225" target="_blank"&gt;&lt;i&gt;undo_retention&lt;/i&gt;&lt;/a&gt; determines how long that data remains in the undo segment.  In my example, it is set to 900 seconds, so I can only query versions in the last 15 minutes.  If I attempt to go back further than this I will get an error.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new;"&gt;column prcsinstance heading 'P.I.' format 9999999
column rownum heading '#' format 9 
column versions_starttime format a22
column versions_endtime format a22

SELECT rownum, prcsinstance
, begindttm, enddttm
, runstatus, diststatus
, versions_operation, versions_xid
, versions_starttime, versions_endtime
FROM sysadm.&lt;a href="http://www.go-faster.co.uk/peopletools/psprcsrqst.htm" target="_blank"&gt;psprcsrqst&lt;/a&gt;
VERSIONS BETWEEN timestamp
systimestamp - INTERVAL '15' MINUTE AND
systimestamp 
WHERE prcsinstance = 2920185
/

&lt;span style="font-size: xx-small;"&gt;#     P.I. BEGINDTTM           ENDDTTM             RU DI V VERSIONS_XID     VERSIONS_STARTTIME    VERSIONS_ENDTIME
-- -------- ------------------- ------------------- -- -- - ---------------- --------------------- ----------------------
 1  2920185 10:51:13 11/06/2010 10:52:11 11/06/2010 9  5  U 000F00070017BD63 11-JUN-10 10.52.10 AM
 2  2920185 10:51:13 11/06/2010 10:52:11 11/06/2010 9  5  U 001A002C001CB1FF 11-JUN-10 10.52.10 AM 11-JUN-10 10.52.10 AM
 3  2920185 10:51:13 11/06/2010 10:52:11 11/06/2010 9  7  U 002C001F000F87C0 11-JUN-10 10.52.10 AM 11-JUN-10 10.52.10 AM
 4  2920185 10:51:13 11/06/2010 &lt;u&gt;&lt;b&gt;10:52:11 11/06/2010&lt;/b&gt;&lt;/u&gt; 9  1  U 000E000A001771CE 11-JUN-10 10.52.10 AM 11-JUN-10 10.52.10 AM
 5  2920185 10:51:13 11/06/2010 &lt;u&gt;&lt;b&gt;10:52:02 11/06/2010&lt;/b&gt;&lt;/u&gt; 9  1  U 002A000F00125D89 11-JUN-10 10.52.01 AM 11-JUN-10 10.52.10 AM
 6  2920185 10:51:13 11/06/2010                     7  1  U 0021000B00132582 11-JUN-10 10.51.10 AM 11-JUN-10 10.52.01 AM
 7  2920185                                         6  1  U 0004002000142955 11-JUN-10 10.51.10 AM 11-JUN-10 10.51.10 AM
 8  2920185                                         5  1  I 0022002E0013F260 11-JUN-10 10.51.04 AM 11-JUN-10 10.51.10 AM&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now, I can see each of the committed versions of the record.  Note that  each version is the result of a different transaction ID.&lt;br /&gt;
Reading up from the last and earliest row in the report, you can see the history of this process request record.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;At line 8 it was inserted (the value of psuedocolumn VERSION_OPERATION is 'I') at RUNSTATUS 5 (queued) by the component the operator used to submit the record.&lt;/li&gt;
&lt;li&gt;At line 7, RUNSTATUS was updated to status 6 (Initiated) by the process scheduler.&lt;/li&gt;
&lt;li&gt;At line 6 the process begins and updates the BEGINDTTM with the current database time, and sets RUNSTATUS to 7 (processing).&lt;/li&gt;
&lt;li&gt;At line 5 the process completes, updates ENDDTTM to the current database time, and sets RUNSTATUS to 9 (success).&lt;/li&gt;
&lt;li&gt;At line 4 the ENDDTTM is updated again.  This update is performed by the Distribution Server process in the Process Scheduler domain as report output is posted to the report repository.&amp;nbsp; Note that the value is 1 second later than the VERSIONS_ENDTIME, therefore this time stamp is based on the operating system time for the host running the process scheduler.  This server's clock is slightly out of sync with that of the database server.  &lt;/li&gt;
&lt;li&gt;At lines 3 to 1 there are 3 further updates as the distribution status is updated twice more.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
For me, &lt;b&gt;the most interesting point is that ENDDTTM is updated twice&lt;/b&gt;; first with the database time at which the process ended, and then again with the time at which any report output was successfully completed.&lt;br /&gt;
&lt;br /&gt;
I frequently want measure the performance of a processes.  I often write script that calculate the duration of the process as being the difference between ENDDTTM and BEGINDTTM, but now it is clear that this includes the time taken to post the report and log files to the report repository.&lt;br /&gt;
&lt;br /&gt;
For Application Engine processes, you can still recover the time when the process ended.  If batch timings are enabled and written to the database, the BEGINDTTM and ENDDTTM are logged in &lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/bat_timings_log.htm"&gt;PS_BAT_TIMINGS_LOG&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new;"&gt;select * from ps_bat_timings_log where process_instance = 2920185

PROCESS_INSTANCE PROCESS_NAME OPRID                          RUN_CNTL_ID
---------------- ------------ ------------------------------ ------------------------------
BEGINDTTM           ENDDTTM             TIME_ELAPSED TIME_IN_PC TIME_IN_SQL TRACE_LEVEL
------------------- ------------------- ------------ ---------- ----------- -----------
TRACE_LEVEL_SAM
---------------
         2920185 XXX_XXX_XXXX 52630500                       16023
10:51:12 11/06/2010 &lt;b&gt;10:52:02 11/06/2010&lt;/b&gt;        49850      35610       13730        1159
            128&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
You can see above that ENDDTTM is the time when the process ended.  &lt;br /&gt;
&lt;br /&gt;
That gives me some opportunities. For Application Engine programs, I can measure the amount of time taken to posting report content, separately from the process execution time.&amp;nbsp; This query shows me that this particular process took 49 seconds, but the report output took a further 9 seconds to post.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new;"&gt;SELECT r.begindttm begindttm
, NVL(l.enddttm, r.enddttm) enddttm
, (NVL(l.enddttm, r.enddttm)-r.begindttm)*86400 exec_secs
, r.enddttm posttime
, (r.enddttm-NVL(l.enddttm, r.enddttm))*86400 post_secs
FROM sysadm.psprcsrqst r
    LEFT OUTER JOIN sysadm.ps_bat_timings_log l
    ON l.process_instance = r.prcsinstance
WHERE r.prcsinstance = 2920185
&lt;span style="font-size: x-small;"&gt;
&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;BEGINDTTM           ENDDTTM              EXEC_SECS POSTTIME             POST_SECS
------------------- ------------------- ---------- ------------------- ----------
10:51:13 11/06/2010 10:52:02 11/06/2010         49 10:52:11 11/06/2010          9  &lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
For more detail on the Flashback Query syntax see the Oracle &lt;a href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_10002.htm#i2112818" target="_blank"&gt;SQL Reference&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-7411517549672039374?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/pNCDnlLfV88" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/pNCDnlLfV88/life-cycle-of-process-request.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/06/life-cycle-of-process-request.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-8683221040168788275</guid><pubDate>Wed, 31 Mar 2010 19:25:00 +0000</pubDate><atom:updated>2011-05-17T14:55:09.667+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Plan Stability</category><category domain="http://www.blogger.com/atom/ns#">Global Payroll</category><category domain="http://www.blogger.com/atom/ns#">Stored Outlines</category><title>Oracle Plan Stability (Stored Outlines) in PeopleSoft Global Payroll</title><description>&lt;i&gt;A longer version of this posting, with experimental results, is &lt;a href="http://www.go-faster.co.uk/gpdoc.htm#gp.stored_outlines" target="_blank"&gt;available on my website&lt;/a&gt;.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
In &lt;a href="http://www.psftdba.com/" target="_blank"&gt;PeopleSoft for the Oracle DBA&lt;/a&gt;, I wrote a page (p. 291) explaining why stored outlines were not suitable for use in PeopleSoft.&amp;nbsp; Five years later, my view has not significantly changed.&amp;nbsp; Essentially, stored outlines work best with shared SQL, and there isn't much shared SQL in PeopleSoft, because a lot of it is dynamically generated.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Code generated by the component processor is dynamically generated.&amp;nbsp; At save time, only fields that have changed are updated.&lt;/li&gt;
&lt;li&gt;PeopleCode can written in such a way that where clauses are dynamically assembled&lt;/li&gt;
&lt;li&gt;nVision reports have variable numbers of criteria on literal tree node IDs in the queries.&lt;/li&gt;
&lt;li&gt;By default in Application Engine, bind variables are converted to literals before the SQL is submitted to the database.&amp;nbsp; Even if this is overridden by enabling ReUseStatement in Application Engine or by using Cursor Sharing in the database, the code still wouldn’t be sharable.&amp;nbsp; Different instances of Application Engine executing the same program can use different instances on non-shared temporary records, so the tables in otherwise identical SQL statements are not the same.&amp;nbsp; You would get one version of the statement per temporary table instance.&lt;/li&gt;
&lt;/ul&gt;However, there are very limited exceptions to this rule (otherwise I wouldn't have a story to tell).&amp;nbsp; The SQL in COBOL and SQR programs are more likely to be shareable.&amp;nbsp;&amp;nbsp; Although some programs are coded to generate SQL dynamically, bind variables are passed through to SQL statements, and they use regular tables for working storage and not PeopleSoft temporary records.&lt;br /&gt;
A Global Payroll customer came to me with a problem where the payroll calculation (GPPDPRUN) would usually run well, but sometimes, the execution plan of a statement would change and the calculation would take additional several hours.&amp;nbsp; It is significant that the Global Payroll engine is written in COBOL.&amp;nbsp; My usual response to this sort of problem in Global Payroll is to add a hint to the stored statement.&amp;nbsp; Usually, I find that only a few statements that are affected.&amp;nbsp; However, after this happened a couple of times in production, it was clear that we couldn't continue to react to these problems. We needed to proactively stop this happening again.&amp;nbsp; This is exactly what stored outlines are designed to do.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Using Stored Outlines in the PeopleSoft GP Engine&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Earlier I said that we could apply stored outlines to the Global Payroll calculation engine (&lt;i&gt;GPPDPRUN&lt;/i&gt;) because it generally doesn’t use dynamic code with embedded literal values.&amp;nbsp;&amp;nbsp; &lt;br /&gt;
While outlines are being created, the following privilege needs to be granted.&amp;nbsp; It can be revoked later.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;GRANT CREATE ANY OUTLINE TO SYSADM;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
We can create a trigger to collect the stored outlines for a payroll calculation, thus:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;The trigger fires when a payroll calculation process starts or finishes.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;At the start a payroll process it starts collecting stored outlines in a category called the same as the process; &lt;i&gt;GPPDPRUN&lt;/i&gt;.&lt;/li&gt;
&lt;li&gt;When the process finishes, outline collection is disabled by setting it back to false.&lt;/li&gt;
&lt;/ul&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;CREATE OR REPLACE TRIGGER sysadm.gfc_create_stored_outlines
BEFORE UPDATE OF runstatus ON sysadm.&lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/psprcsrqst.htm"&gt;psprcsrqst&lt;/a&gt;
FOR EACH ROW
WHEN (new.prcsname = 'GPPDPRUN' AND (new.runstatus = 7 OR old.runstatus = 7))
DECLARE
 l_sql VARCHAR2(100);
BEGIN
  l_sql := 'ALTER SESSION SET create_stored_outlines = ';
  IF :new.runstatus = 7 THEN
    EXECUTE IMMEDIATE l_sql||:new.prcsname;
  ELSIF :old.runstatus = 7 THEN
    EXECUTE IMMEDIATE l_sql||'FALSE';
  END IF;
EXCEPTION WHEN OTHERS THEN NULL; --because I dont want to crash the process scheduler  
END;
/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The exact number of outlines that are collected during this process will vary depending upon configuration, and which employees are processed as different payroll rules are invoked.&lt;br /&gt;
If no more outlines are to be collected the &lt;i&gt;CREATE ANY OUTLINE&lt;/i&gt; privilege can be revoked.&amp;nbsp; This does not prevent the outlines from being used.&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;REVOKE CREATE ANY OUTLINE FROM SYSADM;&lt;/pre&gt;&lt;/div&gt;Then, the category of outlines can be used in subsequent executions by replacing the trigger above with the one below, and the execution plans cannot change so long as the SQL doesn’t change. &lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;CREATE OR REPLACE TRIGGER sysadm.gfc_use_stored_outlines
BEFORE UPDATE OF runstatus ON sysadm.psprcsrqst
FOR EACH ROW
WHEN (new.prcsname = 'GPPDPRUN' AND (new.runstatus = 7 OR old.runstatus = 7))
DECLARE
 l_sql VARCHAR2(100);
BEGIN
  l_sql := 'ALTER SESSION SET use_stored_outlines = ';
  IF :new.runstatus = 7 THEN&lt;/pre&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;EXECUTE IMMEDIATE l_sql||:new.prcsname;
  ELSIF :old.runstatus = 7 THEN
    EXECUTE IMMEDIATE l_sql||'FALSE';
  END IF;
EXCEPTION WHEN OTHERS THEN NULL; --because I dont want to crash the process scheduler 
END;
/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
After running an identify-and-calc and a cancel, we can see how many of the outlines are actually used.&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;SELECT category, count(*) outlines
, sum(decode(used,'USED',1,0)) used
FROM user_outlines
GROUP BY category
ORDER BY 1
/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
I have a large number of unused outlines because of additional recursive SQL generated because OPTIMIZER_DYNAMIC_SAMPLING was set 4. This does not occur if this parameter is set to the default of 2.&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;CATEGORY                         OUTLINES       USED
------------------------------ ---------- ----------
GPPDPRUN                              572        281&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
I can then remove the unused outlines.&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;EXECUTE dbms_outln.drop_unused;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Used flags on the outlines can be reset, so we later we can see the outlines being used again.&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;BEGIN
 FOR i IN (SELECT * FROM user_outlines WHERE category = 'GPPDPRUN') LOOP
  dbms_outln.clear_used(i.name);
 END LOOP;
END;
/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
If I want to go back running without outlines, I just disable the trigger&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;ALTER TRIGGER sysadm.stored_outlines DISABLE;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
To re-enable outlines, just re-enable the trigger.&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;ALTER TRIGGER sysadm.stored_outlines ENABLE;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Conclusions&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Stored Outlines have very limited application in a PeopleSoft system.&amp;nbsp; However, they can easily be collected and used with the PeopleSoft Global Payroll engine.&amp;nbsp; It is just a matter of granting a privilege and using the database triggers on the process request table.&lt;br /&gt;
Testing that they actually have the desired effect is quite difficult, because you are trying to prove a negative.&amp;nbsp; I don’t think it is adequate simply to say that the outline has been used.&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;First you would need an environment where payroll calculation performs well, where you could collect outlines.&lt;/li&gt;
&lt;li&gt;Then you would need a payroll calculation that performs poorly because the execution plan for at least one SQL statement is different&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;Either, on a second environment with exactly the same code.&lt;/li&gt;
&lt;li&gt;Or in the same environment on a different set of data.&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;Then, it would be possible to demonstrate that applying the outline causes the execution plan to revert and restores the performance.&amp;nbsp; This can be confirmed by comparison of the ASH data for the various scenarios.&lt;/li&gt;
&lt;/ul&gt;Even if you don’t want to use a stored outline immediately, it might be advantageous to collect them, and have them available when you do encounter a problem.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-8683221040168788275?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/pNQX2RBbSDk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/pNQX2RBbSDk/oracle-plan-stability-stored-outlines.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>1</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/03/oracle-plan-stability-stored-outlines.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-7429145786468184866</guid><pubDate>Thu, 25 Mar 2010 19:34:00 +0000</pubDate><atom:updated>2010-03-25T20:11:33.652Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">DDL trigger</category><title>Capturing DDL for Database Objects Not Managed by PeopleTools</title><description>I have written before about the challenges of managing database objects and attributes of database objects that are not managed by PeopleTools Application Designer.  I proposed a &lt;a href="http://www.go-faster.co.uk/scripts.htm#t_lock.sql" target="_blank"&gt;DDL trigger to prevent such objects being dropped or altered&lt;/a&gt;. However, sometimes it is necessary to temporarily disable this DDL trigger, such as during patch or upgrade release it is necessary to disable this trigger to apply the changes.&lt;br /&gt;
&lt;br /&gt;
Now, I have another &lt;a href="http://www.go-faster.co.uk/scripts.htm#gfc_ddlcap.sql" target="_blank"&gt;DDL trigger and a packaged procedure&lt;/a&gt; that captures the DDL to recreate objects that are recursively dropped (such as DML triggers on tables).  The DDL is stored in a database table. This trigger can remain permanently enabled, and the table it maintains can be used to see what objects are missing, as well as holding the SQL to rebuild them.&lt;br /&gt;
&lt;br /&gt;
An common example of where this is can be valuable is where a system uses database triggers to capture audit data.&amp;nbsp; This method is often preferred because it generally performs better than having the application server generate additional DML to the audit table, and also captures updates made in other processes.&amp;nbsp; PeopleSoft even deliver processes to generate the DML triggers that write to the audit tables.&amp;nbsp; However, if you alter the table in Application Designer, perhaps only because you are applying a PeopleSoft fix, and apply the changes to the database by recreating the table, then the trigger will be lost.&amp;nbsp; It is then up to the customer to make sure the audit trigger is replaced.&amp;nbsp; There is absolutely nothing to warn you that the trigger is lost, and the application will still function without the trigger, but your updates will not be audited.&lt;br /&gt;
&lt;br /&gt;
When a table is dropped, the trigger calls a procedure in the package that checks for: &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;indexes that are not managed by PeopleTools (such as function-based indexes),&lt;/li&gt;
&lt;li&gt;triggers not managed by PeopleTools (other than the PSU triggers created for mobile agents),&lt;/li&gt;
&lt;li&gt;materialised view logs.&lt;/li&gt;
&lt;li&gt;If the table is partitioned or global temporary the DDL for the object being dropped is also captured.&lt;/li&gt;
&lt;/ul&gt;&lt;recname&gt;When an index is dropped the index check is performed.  Similarly the DDL to rebuild partitioned indexes or indexes on Global Temporary tables is also captured.&lt;br /&gt;
&lt;br /&gt;
When an object for which the DDL has been captured is explicitly dropped, this is indicated on the table GFC_REL_OBJ by storing the time at which it was dropped.  When it is recreated this time-stamp is cleared.&amp;nbsp; Thus it is possible to decide whether something was deliberately or accidentally dropped.&lt;br /&gt;
&lt;/recname&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-7429145786468184866?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/KqHTYI9vPOE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/KqHTYI9vPOE/capturing-ddl-for-database-objects-not.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/03/capturing-ddl-for-database-objects-not.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-7188599010033606886</guid><pubDate>Thu, 04 Mar 2010 20:22:00 +0000</pubDate><atom:updated>2011-06-28T22:50:18.606+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Dynamic Code</category><category domain="http://www.blogger.com/atom/ns#">Application Engine</category><category domain="http://www.blogger.com/atom/ns#">Hints</category><title>Hinting Dynamically Generated SQL in Application Engine</title><description>One of the clever things you can do in Application Engine is dynamically generate parts of a SQL statement.&amp;nbsp; However, this can produce challenges should decide that you also need to add an optimiser hint to a SQL statement.&amp;nbsp; In this post I want to demonstrate how you can also use the same techniques to dynamically generate valid Optimizer hints.&lt;br /&gt;&lt;br /&gt;The following statement was generated by a step in a delivered Application Engine that had not been previously been changed.&amp;nbsp; The &lt;i&gt;PS_XXX&lt;/i&gt; tables are custom tables, but the references are in custom meta-data that is used to dynamically generate the SQL.&amp;nbsp; I have added the hints and comments.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;The specific hints and comments are not important here, the point is how I managed to generate them.&lt;br /&gt;&lt;br /&gt;Note that:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;I have added a comment that contains the name of the application engine step that generated this SQL.&amp;nbsp; This has can be added automatically with the &lt;a href="http://www.go-faster.co.uk/scripts.htm#aeid.sql" target="_blank"&gt;aeid.sql&lt;/a&gt; script on &lt;a href="http://www.go-faster.co.uk/scripts.htm#aeid.sql" target="_blank"&gt;my website&lt;/a&gt;.&amp;nbsp; It can be difficult to identify which step generated which static SQL statement.&amp;nbsp; It can be impossible to do that with dynamic SQL.&amp;nbsp; This identifying comment appears in the SQL.&lt;/li&gt;&lt;li&gt;I have added a &lt;i&gt;QB_NAME&lt;/i&gt; hint to the sub-query, and then the hint can refer to that sub-query (see related blog entry &lt;a href="http://blog.psftdba.com/2009/12/hinting-sub-queries-on-oracle.html"&gt;Hinting Sub-Queries on Oracle&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;Rather than specify the index name in the &lt;i&gt;INDEX&lt;/i&gt; hint I have use the &lt;i&gt;table_name(column_list) &lt;/i&gt;construction to specify an index on a named table that starts with the named columns.&amp;nbsp; This is good general practice, the hint remains valid in the case that an index name changes.&amp;nbsp; However, it is also useful here because there is no meta-data that I can use to construct the name of the index.&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;UPDATE /*+INDEX(@SUB1 ln@SUB1 PS_XXX_SPLT_TA2(process_instance,resource_id))          INDEX(PS_XXX_SPLT_TA1 PS_XXX_SPLT_TA1(process_instance,iu_line_type))*/    /*ID-IU_PROCESSOR.P150.P150-010.S*/     PS_XXX_SPLT_TA1 SET    iu_line_type='U'WHERE iu_line_type='2' AND process_instance=12345678 AND setid_iu='XXXXX' AND ledger_group = 'ACTUALS' AND EXISTS (    SELECT /*+QB_NAME(SUB1)*/ 'X'     FROM PS_XXX_SPLT_TA2 ln    where ln.EMPLID2 = PS_XXX_SPLT_TA1.EMPLID2     and ln.SEQ_NUM = PS_XXX_SPLT_TA1.SEQ_NUM     and ln.BUSINESS_UNIT = PS_XXX_SPLT_TA1.BUSINESS_UNIT     and ln.RESOURCE_ID = PS_XXX_SPLT_TA1.RESOURCE_ID     AND ln.setid_iu = 'XXXXX'    AND ln.ledger_group = 'ACTUALS'     AND ln.process_instance = 12345678     GROUP BY ln.BUSINESS_UNIT_GL , ln.ledger, ln.OPERATING_UNIT, ln.BUSINESS_UNIT, ln.RESOURCE_ID, ln.EMPLID2, ln.SEQ_NUM     HAVING SUM(ln.RESOURCE_AMOUNT) &amp;lt;&amp;gt; 0)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;So how did I get those hints into the SQL?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First of all remember that Application Engine doesn't know anything about SQL.&amp;nbsp; An Application Engine step is just a string of characters that will be submitted to the database.&amp;nbsp; Any PeopleCode macros are executed as the step is prepared and the resultant string is then set to the database.&lt;br /&gt;&lt;br /&gt;The name of the PeopleSoft record being updated is in a variable &lt;i&gt;line_wrk2_rec&lt;/i&gt;.&amp;nbsp; I can introduce the contents of the variable &lt;i&gt;line_wrk2_rec&lt;/i&gt; with the &lt;i&gt;%BIND()&lt;/i&gt; macro.&amp;nbsp; Normally a string bind variable is delimited by single quotes because it is used in a function or predicate, but the quotes can be suppressed with the &lt;i&gt;NOQUOTES&lt;/i&gt; option.&amp;nbsp; I can convert the PeopleSoft record name to the database table name with the &lt;i&gt;%Table()&lt;/i&gt; PeopleCode macro.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;So, in this example&lt;br /&gt;&lt;table border="2" id="table1"&gt;&lt;tbody&gt;&lt;tr&gt;   &lt;td&gt;&lt;b&gt;Code in Application Engine Step&lt;/b&gt;&lt;/td&gt;   &lt;td&gt;&lt;b&gt;Expansion&lt;/b&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;tr&gt;   &lt;td&gt;%BIND(line_wrk_rec)&lt;/td&gt;   &lt;td&gt;'XXX_SPLT_TA1'&lt;/td&gt;  &lt;/tr&gt;&lt;tr&gt;   &lt;td&gt;%BIND(line_wrk_rec,NOQUOTES))&lt;/td&gt;   &lt;td&gt;XXX_SPLT_TA1&lt;/td&gt;  &lt;/tr&gt;&lt;tr&gt;   &lt;td&gt;%Table(%BIND(line_wrk_rec,NOQUOTES))&lt;/td&gt;   &lt;td&gt;PS_XXX_SPLT_TA1&lt;/td&gt;  &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Note that delivered PeopleSoft SQL never specifies a row source alias on the table being updated because this would be invalid SQL on SQL Server.&amp;nbsp; Thus one SQL statement can be used on multiple platforms.&amp;nbsp; Although it is possible to have platform specific steps in Application Engine, PeopleSoft development avoid this wherever possible because it increases their development overheads.&amp;nbsp; So the row source alias is the table name.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;I have used the expression &lt;i&gt;%Table(%BIND(line_wrk_rec,NOQUOTES))&lt;/i&gt; twice; once for the table alias and then again when I specify the index.&amp;nbsp; I want to force the use of an index on &lt;i&gt;PS_XXX_SPLT_TA1&lt;/i&gt; that leads on columns &lt;i&gt;PROCESS_INSTANCE&lt;/i&gt; and &lt;i&gt;RESOURCE_ID&lt;/i&gt;.&amp;nbsp; This is the code in the Application Engine step that generates the SQL statement above.&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;UPDATE /*+INDEX(@SUB1 ln@SUB1 %Table(%BIND(line_wrk2_rec,NOQUOTES))(process_instance,resource_id))        INDEX(%Table(%BIND(line_wrk_rec,NOQUOTES)) %Table(%BIND(line_wrk_rec,NOQUOTES))(process_instance,iu_line_type))*/    /*ID-IU_PROCESSOR.P150.P150-010.S*/%Table(%BIND(line_wrk_rec,NOQUOTES))  SET iu_line_type='U'  WHERE iu_line_type='2'    AND process_instance=%BIND(process_instance)    AND setid_iu=%BIND(iu_proc_002_aet.setid_ledger)    AND ledger_group = %BIND(iu_proc_002_aet.ledger_group)    AND EXISTS (     SELECT /*+QB_NAME(SUB1)*/ 'X'       FROM %Table(%BIND(line_wrk2_rec,NOQUOTES)) ln         %BIND(iu_where_aet.iu_where_sql,NOQUOTES)%BIND(iu_group_by_aet.iu_group_by_sql,NOQUOTES)         %BIND(iu_proc_002_aet.where_bu,NOQUOTES)        AND ln.setid_iu = %BIND(iu_proc_002_aet.setid_ledger)        AND ln.ledger_group = %BIND(iu_proc_002_aet.ledger_group)        AND ln.process_instance = %ProcessInstance      GROUP BY ln.%BIND(iu_sys_tran_aet.fieldname_bu_gl,NOQUOTES), ln.ledger%BIND(iu_proc_001_aet.iu_group_by_sql,NOQUOTES)      HAVING SUM(ln.%BIND(iu_sys_tran_aet.fieldname_base_amt,NOQUOTES)) &amp;lt;&amp;gt; 0)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;There is a problem here (at least there is in theory).  If this statement executes for a different table the index hint will instruct the SQL to look for  an index on that different table on the same columns.&amp;nbsp; The predicates in the where clauses are also derived from dynamic code.&amp;nbsp; If I was being absolutely rigorous, I would have added some procedural code in preceding steps to build this part of the hints dynamically too, however, I am also trying to keep the customisation to a minimum in an area of code that is otherwise vanilla.&lt;br /&gt;&lt;br /&gt;I have accepted a compromise.&amp;nbsp; If the step executes for a different table, the hint will probably be invalid because there is probably no such index on these columns on that table.&amp;nbsp; There is a risk that such an index does exist and so the hint could be valid but totally inappropriate to the situation because the predicates are totally different.&amp;nbsp; The result could be very poor performance.&amp;nbsp; However, in this case, in practice, this risk is zero, and the hint results in appropriate behaviour in all scenarios.&amp;nbsp; Although this is something that you need to consider on a case by case basis&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-7188599010033606886?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/YdhF7yqyKWQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/YdhF7yqyKWQ/hinting-dynamic-generated-sql-in.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/03/hinting-dynamic-generated-sql-in.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-5188327023441275916</guid><pubDate>Wed, 20 Jan 2010 22:57:00 +0000</pubDate><atom:updated>2011-01-26T00:45:26.519Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">Batch Timings</category><category domain="http://www.blogger.com/atom/ns#">XML Reporting</category><title>Performance Metrics and XML Reporting in PeopleSoft</title><description>I am working with a PeopleSoft system that makes extensive use of XML Publisher reporting.&amp;nbsp; Generally these reports are based on SQL queries that are defined in the PeopleSoft PS/Query utility.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
These queries are run by an Application Engine program PSXPQRYRPT that runs the SQL query and produces the report in a single process.&amp;nbsp; This is a generic application engine program that runs any XML report.&amp;nbsp; Line 45 (in PeopleTools 8.49) of step &lt;i&gt;MAIN.ExecRPT&lt;/i&gt; executes the report with the &lt;a href="http://download.oracle.com/docs/cd/E13292_01/pt849pbr0/eng/psbooks/tpcr/htm/tpcr44.htm#g037ee99c9453fb39_ef90c_10c791ddc07__8bc" target="_blank"&gt;ProcessReport&lt;/a&gt; PeopleCode command.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;&amp;amp;oRptDefn.ProcessReport(&amp;amp;TemplateId, &amp;amp;LanguageCd, &amp;amp;AsOfDate, &amp;amp;oRptDefn.GetOutDestFormatString(&amp;amp;OutDestFormat));&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;span style="font-size: large;"&gt;&lt;b&gt;Batch Timings&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
Analysis of the Application Engine batch timings indicate that nearly all the time in this Application Engine program is spent in PeopleCode, and that this not SQL execution time.&amp;nbsp; This is misleading.&amp;nbsp; The ProcessReport command is PeopleCode, but behind the scenes it also issues the SQL in the report data source.&amp;nbsp; Not all the time is SQL, but the Application Engine Batch Timings does not count any of this as SQL because it is not in the PeopleCode SQL Class.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
Let’s look at an example Batch Timings report (I have edited it down, removing zero and insignificant timings).&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: xx-small;"&gt;PeopleSoft Application Engine Timings
                                 (All timings in seconds)

                               C o m p i l e    E x e c u t e    F e t c h        Total           
SQL Statement                  Count   Time     Count   Time     Count   Time     Time    
------------------------------ ------- -------- ------- -------- ------- -------- --------
PeopleCode
Record.SelectByKey PSDBFIELD                          8      0.1       8      0.0      0.1
SELECT PSPRCSRQST                                     2      0.1       2      0.0      0.1
SELECT PSXPTMPLDEFN                                   3      0.1       3      0.0      0.1
                                                                                  --------
                                                                                       0.3
AE Program: PSXPQRYRPT
MAIN.ExecRpt.H                       1      0.0       1      0.0       1      0.0      0.0
                                                                                  --------
                                                                                       0.0
------------------------------------------------------------------------------------------
                               Call    Non-SQL  SQL      Total   
PeopleCode                     Count   Time     Time     Time    
------------------------------ ------- -------- -------- --------
AE Program: PSXPQRYRPT
MAIN.ExecRpt                         1    643.2      0.3    643.5
                                       -------- -------- --------
                                          643.2      0.3    643.5
------------------------------------------------------------------------------------------
                                                        E x e c u t e   
PEOPLECODE Builtin/Method                               Count   Time    
------------------------------------------------------- ------- --------
Boolean(Type 5) BuiltIns                                     90      0.1
DateTime(Type 11) BuiltIns                                    1      0.1
SQL(Type 524290) Methods                                     19      0.1
SQL(Type 524290) BuiltIns                                     9      0.1
Record(Type 524291) Methods                                1104      0.1
Session(Type 524303) Methods                                207    633.2
JavaObject(Type 524315) BuiltIns                              6      2.2
PostReport(Type 524324) Methods                               2      0.7
------------------------------------------------------------------------------------------
Total run time                :      644.0
Total time in application SQL :        0.3   Percent time in application SQL :        0.0%
Total time in PeopleCode      :      643.2   Percent time in PeopleCode      :       99.9%
------------------------------------------------------------------------------------------&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;The total execution time of is 644 seconds.&lt;/li&gt;
&lt;li&gt;643.2s are reported as being in PeopleCode, and as coming from the MAIN.ExecRpt step. &lt;/li&gt;
&lt;li&gt;Only 0.3s of that is SQL time, and that comes from PeopleCode functions in the &lt;a href="http://www.oracle.com/pls/psft/to_URL?remark=ranked&amp;amp;urlname=http:%2F%2Fdownload.oracle.com%2Fdocs%2Fcd%2FE12341_01%2Fcrm9pbr0_run2%2Feng%2Fpsbooks%2F..%2F..%2F..%2F..%2FE13292_01%2Fpt849pbr0%2Feng%2Fpsbooks%2Ftpcr%2Fbook.htm?File=tpcr%2Fhtm%2Ftpcr39.htm%23d0e141851" target="_blank"&gt;SQL Class&lt;/a&gt;&amp;nbsp; (&lt;a href="http://download.oracle.com/docs/cd/E13292_01/pt849pbr0/eng/psbooks/tpcl/htm/tpcl02.htm#g037ee99c9453fb39_ef90c_10c791ddc07__3dfe" target="_blank"&gt;CreateRecord&lt;/a&gt; , &lt;a href="http://download.oracle.com/docs/cd/E13292_01/pt849pbr0/eng/psbooks/tpcl/htm/tpcl02.htm#g037ee99c9453fb39_ef90c_10c791ddc07__3df9" target="_blank"&gt;CreateSQL&lt;/a&gt; etc.) &lt;/li&gt;
&lt;li&gt;The ProcessReport method does not appear in the PeopleCode SQL analysis section of the report, because it is not classed as SQL.&lt;/li&gt;
&lt;/ul&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;Identifying the Report ID and Report Source&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
So, the next question is how can I find out which report this process is running.&amp;nbsp; Different instances of this report may be running different queries.&lt;br /&gt;
&lt;br /&gt;
You can get the list files generated by a process from the Content Management PeopleTools table PS_CDM_FILE_LIST.&amp;nbsp; Application Engine processes usually produce various log files (with extensions .aet, .trc and .log), the name of the other file is the same as the name of the report followed by an extension that will vary depending on format.&lt;br /&gt;
&lt;br /&gt;
You can look at the report definition on-line under Reporting Tools -&amp;gt; XML Publisher -&amp;gt; Report Definition, and that will show you the Data Source ID&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.go-faster.co.uk/images/psxprptdefn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="2" height="211" src="http://www.go-faster.co.uk/images/psxprptdefn.png" width="400" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
The Data Source is defined in a component accessible from the previous entry in the same menu.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.go-faster.co.uk/images/psxpdatsrc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="2" height="243" src="http://www.go-faster.co.uk/images/psxpdatsrc.png" width="400" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;span style="font-size: large;"&gt;&lt;b&gt;Analysing Execution Times&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
I have written this query to aggregate execution time for PSXPQRYRPT by output file name.&amp;nbsp; &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/psprcsrqst.htm"&gt;PSPRCSRQST&lt;/a&gt; is the process request table, from which I get the run time of the process.&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/cdm_file_list.htm"&gt;PS_CDM_FILE_LIST&lt;/a&gt; lists the files generated by the process which would be posted to the Report Repository. If I exclude the usual trace files, I am left with the report ID.&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/psxprptdefn.htm"&gt;PSXPRPTDEFN&lt;/a&gt; is the report definition record, from which I can get the data source ID&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/psxpdatsrc.htm"&gt;PSXPDATSRC&lt;/a&gt; specifies the data source.&amp;nbsp; A data source type (DS_TYPE) of QRY indicates a PeopleSoft PS/Query.&amp;nbsp; If it is a private query, the OPRID will have a value.&lt;/li&gt;
&lt;/ul&gt;&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: x-small;"&gt;column report_defn_id heading 'Report ID'      format a12 
column ds_type        heading 'Type'           format a4 
column ds_id          heading 'Data Source ID' format a30 
column oprid          heading 'Owner'          format a10 
column processes      heading 'Prcs'           format 990 
column reprots        heading 'Reps'           format 9,990 
column secs           heading 'Total|Seconds'  format 999,990 
column median         heading 'Median|Seconds' format 999,990 
column variance       heading 'Variance'       format 9990.0
SELECT d.report_defn_id, d.ds_type, d.ds_id, d.oprid 
,      SUM(y.processes) processes
,      SUM(y.reports) reports
,      SUM(y.secs) secs
,      median(y.secs) median
,      variance(y.secs) variance
FROM (
 SELECT x.prcsinstance
 ,      x.filename
 ,      COUNT(DISTINCT x.prcsinstance) processes
 ,      COUNT(*) reports
 ,      SUM(x.secs) secs
 FROM   (
  SELECT r.prcsinstance
  ,      f.filename
  ,      86400*(r.enddttm-r.begindttm)*ratio_to_report(1) over (partition by r.prcsinstance) secs
  FROM   sysadm.psprcsrqst r
  ,      sysadm.ps_cdm_file_list f
  WHERE  r.prcsname = 'PSXPQRYRPT'
  AND    r.prcsinstance = f.prcsinstance
  AND    NOT f.cdm_file_type IN('AET','TRC','LOG')
  AND    r.begindttm &gt;= TRUNC(SYSDATE)
  ) x
 GROUP BY x.prcsinstance, x.filename
 ) y
,      sysadm.psxprptdefn d
WHERE  d.report_defn_id = SUBSTR(y.filename,1,instr(y.filename,'.')-1)
GROUP BY d.report_defn_id, d.ds_type, d.ds_id, d.oprid 
ORDER BY secs DESC
/
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
I can now see which report process has been executed run how many times, how many copies of the report have been produced, and where the processing time is being spent, and so which one I should look at first.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: xx-small;"&gt;Total   Median
Report ID    Type Data Source ID                 Owner      Prcs   Reps  Seconds  Seconds Variance
------------ ---- ------------------------------ ---------- ---- ------ -------- -------- --------
XXX_WK_LATE  QRY  XXX_WKLY_LATENESS_RPT                       20     20    2,973      148   3702.9
XXX_HRAM_CON QRY  XXX_HRPD_CNT_AMD_RPT_QRY                    92     92    2,677       27    108.4
XXX_CKOUT_RP QRY  XXX_CHECKOUT_REPORT                         47     47    2,043       41    347.7
XXX_BNK_RPT  QRY  XXX_BNK_DTLS_SCH_QRY                         1     75      141      141      0.0
…
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;span style="font-size: large;"&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
If you have a SQL performance problem with an XML report because the source PS/Query performs poorly, the batch timings will lead you to believe that you have a PeopleCode problem and not a SQL problem.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Beware, this may not be the case.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Most of the execution time for XML Reports sourced from PS/Queries is almost certain to be SQL execution time.&amp;nbsp; The above query will tell you which queries are consuming how much time, and so inform your tuning effort.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-5188327023441275916?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/G1WAzoFBzxQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/G1WAzoFBzxQ/performance-metrics-and-xml-reporting.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/01/performance-metrics-and-xml-reporting.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-4809409387370113549</guid><pubDate>Sun, 10 Jan 2010 12:50:00 +0000</pubDate><atom:updated>2010-01-10T12:57:36.775Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">COBOL</category><category domain="http://www.blogger.com/atom/ns#">Stored Statements</category><title>Automatically Identifying Stored Statements &amp; Using DBMS_STATS in PeopleSoft Cobol</title><description>It is not possible to reference a long column in a Oracle database trigger, but it is possible to reference LOBs.  From PeopleSoft v9 Applications, the long columns have become CLOBs.  Hence, it is now possible to have a database trigger fire on insert into the Stored Statement table and so automatically make certain changes to Stored Statements as they are loaded by Data Mover.  Previously, these changes could be made by a PL/SQL script, but you had to remember to run in after any Stored Statements were reloaded.  I have published a new script (&lt;a href="http://www.go-faster.co.uk/scripts.htm#gfc_stmtid_trigger.sql" target="_blank"&gt;gfc_stmtid_trigger.sql&lt;/a&gt;) on my website that creates two such database triggers.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Trigger GFC_STMTID &lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
In &lt;a href="http://www.psftdba.com/" target="_blank"&gt;PeopleSoft for the Oracle DBA&lt;/a&gt; (listing 11-23, page 280), I proposed a PL/SQL procedure to add identifying comments to stored statements (see blog entry: Identifying Application Engine Source Code), so that the statements can be identified in traces or Oracle Enterprise Manager.&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;
Trigger GFC_STMTSTATS&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
PeopleSoft Cobol programs do not use the PeopleSoft DDL models that Application Engine users when it processes the %UPDATESTATS macro.  In &lt;a href="http://blog.psftdba.com/2009/11/controlling-how-peoplesoft-cobol.html" target="_blank"&gt;another blog posting&lt;/a&gt;, I showed how to change the stored statements to call the &lt;a href="http://www.go-faster.co.uk/scripts.htm#wrapper848meta.sql" target="_blank"&gt;wrapper package&lt;/a&gt;, so that the program uses the Oracle supplied &lt;i&gt;dbms_stats&lt;/i&gt; procedure instead of the depreciated &lt;i&gt;ANALYZE&lt;/i&gt; command.  This trigger automatically replaces the %UPDATESTATS macro in the stored statement with a call to the wrapper package.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-4809409387370113549?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/LmGpzj_-XSw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/LmGpzj_-XSw/automatically-identify-stored.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2010/01/automatically-identify-stored.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-2366255657166583921</guid><pubDate>Thu, 17 Dec 2009 20:24:00 +0000</pubDate><atom:updated>2009-12-26T17:40:39.857Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hints</category><title>Hinting Sub-Queries on Oracle</title><description>This posting is a purely Oracle RDBMS discussion about how to correctly apply hints to sub-queries.  However, it is particularly relevant to PeopleSoft, which it makes extensive use of correlated sub-queries.  The point of this story is not the particular hints that I applied, but where I placed the hints, and how I scoped them to the sub-queries.&lt;br /&gt;
&lt;br /&gt;
The following SQL extract is from the delivered Global Payroll GPGB_EDI process (although I have already made some minor changes, and PS_GP_RSLT_PIN is partitioned).  Notice that each sub-query joins two tables together.  PS_GP_RSLT_PIN is second largest of Global Payroll result tables.  PS_GP_PIN is a look-up table, and the criterion on PIN_CODE will only return a single row.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: 85%;"&gt;UPDATE Table(GPGB_EDIE_TMP) X
  SET X.GPGB_WK53_IND = ( 
 SELECT %TrimSubstr(%Sql(FUNCLIB_HR_CHAR,A.CALC_RSLT_VAL),1,2) 
  &lt;b&gt;FROM PS_GP_RSLT_PIN A 
  ,PS_GP_PIN B&lt;/b&gt; 
 WHERE A.EMPLID = %Table(GPGB_EDIE_TMP).EMPLID 
...&lt;/pre&gt;&lt;pre style="font-family: courier new; font-size: 85%;"&gt;AND A.PIN_NUM = B.PIN_NUM 
   AND B.&lt;b&gt;PIN_CODE = 'TAX VR PERIOD GBR' 
&lt;/b&gt;   AND A.SLICE_BGN_DT = ( 
 SELECT MAX(D.SLICE_BGN_DT) 
  FROM PS_GP_RSLT_PIN D 
 WHERE D.EMPLID = %Table(GPGB_EDIE_TMP).EMPLID 
...
   AND D.INSTANCE = A.INSTANCE 
   AND D.PIN_NUM = B.PIN_NUM) 
...
   )
 WHERE EXISTS ( 
...
   )
AND PROCESS_INSTANCE = %Bind(PROCESS_INSTANCE)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This is part of the initial SQL execution plan.  The problem is that the sub-query starts by scanning through the PS_GP_RSLT_PIN table, 4 times because there are three correlated sub-queries, and only at the very end does it look up the PIN_CODE by the PIN_NUM.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: 66%;"&gt;---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                     | Name              | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| 
---------------------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                              |                   |     1 |    65 |   113M (93)| 39:44:51 |       |     
|   1 |  UPDATE                                       | PS_GPGB_EDIE_TMP4 |       |       |            |          |       |     
|*  2 |   FILTER                                      |                   |       |       |            |          |       |     
|*  3 |    TABLE ACCESS FULL                          | PS_GPGB_EDIE_TMP4 |   673K|    41M|  9967   (6)| 00:00:13 |      
|   4 |    &lt;b&gt;NESTED LOOPS&lt;/b&gt;                               |                   |     1 |   108 |     4   (0)| 00:00:01 |       |     
|   5 |     PARTITION RANGE SINGLE                    |                   |     1 |    83 |     3   (0)| 00:00:01 |   KEY | 
|   6 |      PARTITION LIST SINGLE                    |                   |     1 |    83 |     3   (0)| 00:00:01 |   KEY | 
|*  7 |       INDEX RANGE SCAN                        | PS_GP_RSLT_PIN    |     1 |    83 |     3   (0)| 00:00:01 |   KEY | 
|   8 |        SORT AGGREGATE                         |                   |     1 |    72 |            |          |       |     
|   9 |         PARTITION RANGE SINGLE                |                   |     1 |    72 |     3   (0)| 00:00:01 |   KEY | 
|  10 |          PARTITION LIST SINGLE                |                   |     1 |    72 |     3   (0)| 00:00:01 |   KEY | 
|* 11 |           INDEX RANGE SCAN                    | PS_GP_RSLT_PIN    |     1 |    72 |     3   (0)| 00:00:01 |   KEY | 
|  12 |            SORT AGGREGATE                     |                   |     1 |    83 |            |          |       |     
|  13 |             PARTITION RANGE SINGLE            |                   |     1 |    83 |     3   (0)| 00:00:01 |   KEY | 
|  14 |              PARTITION LIST SINGLE            |                   |     1 |    83 |     3   (0)| 00:00:01 |   KEY | 
|  15 |               FIRST ROW                       |                   |     1 |    83 |     3   (0)| 00:00:01 |       |     
|* 16 |                INDEX RANGE SCAN (MIN/MAX)     | PS_GP_RSLT_PIN    |     1 |    83 |     3   (0)| 00:00:01 | 
|  17 |                 SORT AGGREGATE                |                   |     1 |    83 |            |          |       |     
|  18 |                  PARTITION RANGE SINGLE       |                   |     1 |    83 |   158  (99)| 00:00:01 |   KEY | 
|  19 |                   PARTITION LIST SINGLE       |                   |     1 |    83 |   158  (99)| 00:00:01 |   KEY | 
|  20 |                    FIRST ROW                  |                   |     1 |    83 |   158  (99)| 00:00:01 |       |     
|* 21 |                     INDEX RANGE SCAN (MIN/MAX)| PS_GP_RSLT_PIN    |     1 |    83 |   158  (99)| 00:00:01 |   KEY |
|* 22 |     &lt;b&gt;INDEX RANGE SCAN&lt;/b&gt;                          | &lt;b&gt;PSAGP_PIN&lt;/b&gt;         |     1 |    25 |     1   (0)| 00:00:01 |       |     
...
---------------------------------------------------------------------------------------------------------------------------&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
It would be much better if we started with the PS_GP_PIN table, looked up the PIN_NUM with the PIN_CODE (there is a suitable index on this column) and used the PIN_NUM value as a part of the lookup on PS_GP_RSLT_PIN (again PIN_CODE is in the unique index on that table).  &lt;br /&gt;
&lt;br /&gt;
It is tempting to add LEADING hints to the sub-queries, but such a hint does not work because the hint is not scoped to the sub-query and is considered to be invalid because the entire query cannot start at this point.  &lt;br /&gt;
&lt;br /&gt;
The only supported place in the query to add a LEADING hint would be the first query, in this case after the UPDATE keyword.  &lt;br /&gt;
&lt;br /&gt;
In this case, I have named the query blocks in the sub-queries with the QB_NAME hint.  It is valid to put this hint into the sub-query.  Then I added LEADING hints for each sub-query after the UPDATE keyword, but I specified their scope using the name of the sub-query specified in the QB_NAME hint.  Each sub-query must now start with the PS_GP_PIN table.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: 85%;"&gt;UPDATE &lt;b&gt;/*+LEADING(@SUB1 B@SUB1) LEADING(@SUB2 B@SUB2)*/&lt;/b&gt; %Table(GPGB_EDIE_TMP) X
  SET X.GPGB_WK53_IND = ( 
 SELECT &lt;b&gt;/*+QB_NAME(SUB1)*/&lt;/b&gt; %TrimSubstr(%Sql(FUNCLIB_HR_CHAR,A.CALC_RSLT_VAL),1,2) 
  FROM PS_GP_RSLT_PIN A 
  ,PS_GP_PIN B 
 WHERE A.EMPLID = %Table(GPGB_EDIE_TMP).EMPLID 
...
   AND A.PIN_NUM = B.PIN_NUM 
   AND B.PIN_CODE = 'TAX VR PERIOD GBR' 
   AND A.SLICE_BGN_DT = ( 
 SELECT MAX(D.SLICE_BGN_DT) 
  FROM PS_GP_RSLT_PIN D 
 WHERE D.EMPLID = %Table(GPGB_EDIE_TMP).EMPLID 
...
   AND D.INSTANCE = A.INSTANCE 
   AND D.PIN_NUM = B.PIN_NUM) 
...
   )
 WHERE EXISTS ( 
 SELECT &lt;b&gt;/*+QB_NAME(SUB2)*/&lt;/b&gt; 'X' 
  FROM PS_GP_RSLT_PIN A1 
  ,PS_GP_PIN B1 
 WHERE A1.EMPLID = %Table(GPGB_EDIE_TMP).EMPLID 
...
   AND A1.PIN_NUM = B1.PIN_NUM 
   AND B1.PIN_CODE = 'TAX VR PERIOD GBR' 
...
   )
AND PROCESS_INSTANCE = %Bind(PROCESS_INSTANCE)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This is the new execution plan.  The sub-query starts with an access of index PSAGP_PIN (which leads on PIN_CODE) on PS_GP_PIN, and then does the lookups on PS_GP_RSLT_PIN.  The cost is the same, but the execution time was considerably reduced.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: 66%;"&gt;----------------------------------------------------------------------------------------------------
| Id  | Operation                                     | Name              | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| 
----------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                              |                   |     1 |    65 |   113M (93)| 39:44:51 |       |     
|   1 |  UPDATE                                       | PS_GPGB_EDIE_TMP4 |       |       |            |          |       |     
|*  2 |   FILTER                                      |                   |       |       |            |          |       |     
|*  3 |    TABLE ACCESS FULL                          | PS_GPGB_EDIE_TMP4 |   673K|    41M|  9967   (6)| 00:00:13 |       |
|   4 |    &lt;b&gt;NESTED LOOPS&lt;/b&gt;                               |                   |     1 |   108 |     4   (0)| 00:00:01 |       |     
|*  5 |     &lt;b&gt;INDEX RANGE SCAN&lt;/b&gt;                          | &lt;b&gt;PSAGP_PIN&lt;/b&gt;         |     1 |    25 |     2   (0)| 00:00:01 |       |     
|   6 |     PARTITION RANGE SINGLE                    |                   |     1 |    83 |     2   (0)| 00:00:01 |   KEY | 
|   7 |      PARTITION LIST SINGLE                    |                   |     1 |    83 |     2   (0)| 00:00:01 |   KEY | 
|*  8 |       INDEX RANGE SCAN                        | PS_GP_RSLT_PIN    |     1 |    83 |     2   (0)| 00:00:01 |   KEY | 
|   9 |        SORT AGGREGATE                         |                   |     1 |    72 |            |          |       |     
|  10 |         PARTITION RANGE SINGLE                |                   |     1 |    72 |     3   (0)| 00:00:01 |   KEY | 
|  11 |          PARTITION LIST SINGLE                |                   |     1 |    72 |     3   (0)| 00:00:01 |   KEY | 
|* 12 |           INDEX RANGE SCAN                    | PS_GP_RSLT_PIN    |     1 |    72 |     3   (0)| 00:00:01 |   KEY | 
|  13 |            SORT AGGREGATE                     |                   |     1 |    83 |            |          |       |     
|  14 |             PARTITION RANGE SINGLE            |                   |     1 |    83 |     3   (0)| 00:00:01 |   KEY | 
|  15 |              PARTITION LIST SINGLE            |                   |     1 |    83 |     3   (0)| 00:00:01 |   KEY | 
|  16 |               FIRST ROW                       |                   |     1 |    83 |     3   (0)| 00:00:01 |       |     
|* 17 |                INDEX RANGE SCAN (MIN/MAX)     | PS_GP_RSLT_PIN    |     1 |    83 |     3   (0)| 00:00:01 |       |
|  18 |                 SORT AGGREGATE                |                   |     1 |    83 |            |          |       |     
|  19 |                  PARTITION RANGE SINGLE       |                   |     1 |    83 |   158  (99)| 00:00:01 |   KEY | 
|  20 |                   PARTITION LIST SINGLE       |                   |     1 |    83 |   158  (99)| 00:00:01 |   KEY | 
|  21 |                    FIRST ROW                  |                   |     1 |    83 |   158  (99)| 00:00:01 |       |     
|* 22 |                     INDEX RANGE SCAN (MIN/MAX)| PS_GP_RSLT_PIN    |     1 |    83 |   158  (99)| 00:00:01 |   KEY |
|  23 |   TABLE ACCESS BY LOCAL INDEX ROWID           | PS_GP_RSLT_PIN    |     1 |    86 |     3   (0)| 00:00:01 |       |
...
---------------------------------------------------------------------------------------------------------------------------&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-2366255657166583921?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/ftiFib7mYYA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/ftiFib7mYYA/hinting-sub-queries-on-oracle.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>1</thr:total><feedburner:origLink>http://blog.psftdba.com/2009/12/hinting-sub-queries-on-oracle.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-400004847550273152</guid><pubDate>Wed, 25 Nov 2009 08:23:00 +0000</pubDate><atom:updated>2011-01-26T00:46:41.137Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">COBOL</category><category domain="http://www.blogger.com/atom/ns#">Stored Statements</category><title>Controlling How PeopleSoft Cobol Collects Statistics</title><description>In previous postings, I have proposed &lt;a href="http://blog.psftdba.com/2008/06/oracle-optimizer-statistics-and.html"&gt;locking statistics&lt;/a&gt; on temporary working storage tables and &lt;a href="http://blog.psftdba.com/2009/06/controlling-how-updatestats-collects.html"&gt;changing the DDL model for %UpdateStats&lt;/a&gt; to call my own PL/SQL Package.&amp;nbsp; That works for Application Engine programs, but PeopleSoft COBOL can also update object statistics, and they use a different mechanism.&lt;br /&gt;
&lt;br /&gt;
In the case of the Global Payroll calculation engine, &lt;i&gt;GPPDPRUN&lt;/i&gt;, the run control component has a secondary page with a check box to enable statistics collection during the process.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.go-faster.co.uk/images/gppdpruncntl.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="2" height="100" src="http://www.go-faster.co.uk/images/gppdpruncntl.png" width="400" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
However, to get streamed processing in Payroll (where the population of employees is broken into ranges of employee IDs that are each processed by a different concurrent process) to work effectively I change the working storage tables to be Global Temporary Tables, and then because the different physical instances would still share statistics (&lt;a href="http://blog.go-faster.co.uk/2009/10/global-temporary-tables-shared.html"&gt;Global Temporary Tables Share Statistics Across Sessions&lt;/a&gt;) I delete and lock the statistics on these tables.&lt;br /&gt;
&lt;br /&gt;
If the payroll calculation is run with the Update Statistics option it generates the following error. &lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: 85%;"&gt;Application Program Failed
 Action Type     : SQL UPDATE
 In Pgm Section  : SQLRT: EXECUTE-STMT                                 
 With Return Code: 38029 
 Error Message   : ORA-38029: object statistics are locked
 Stored Stmt     : GPPSERVC_U_STATS  
 SQL Statement   : ANALYZE TABLE PS_GP_PYE_STAT_WRK ESTIMATE STATISTICS&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
COBOL issued an ANALYZE command, and did not use the DDL model defined in the table PSDDLMODEL.&amp;nbsp; However, the command came from a stored statement, in this case GPPSERVC_U_STATS.&amp;nbsp; The stored statement is defined as follows in the gppservc.dms.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: 85%;"&gt;STORE GPPSERVC_U_STATS
%UPDATESTATS(PS_GP_PYE_STAT_WRK)
;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
So, the expansion of %UPDATESTATS in the stored statement to the ANALYZE command is hard coded somewhere in the delivered executable code.&amp;nbsp; I would not suggest attempting to change that.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
However, it is perfectly possible to change the stored statement to call the wrapper package (&lt;a href="http://www.go-faster.co.uk/scripts/wrapper848meta.sql" target="_blank"&gt;www.go-faster.co.uk/scripts/wrapper848meta.sql&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: 85%;"&gt;STORE GPPSERVC_U_STATS
BEGIN wrapper.ps_stats(p_ownname=&amp;gt;user, p_tabname=&amp;gt;'PS_GP_PYE_STAT_WRK'); END;; &lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In all there are 5 statements in HR 9.0 that call %UPDATESTATS that all relate to GPPDPRUN.&amp;nbsp; I generated a data mover script to replace them with calls to my replacement package using the following SQL.script.&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background-color: #eeeeee; border: 0px solid rgb(0, 0, 0); overflow: auto; padding-left: 4px; padding-right: 4px; width: 100%;"&gt;&lt;pre style="font-family: courier new; font-size: 85%;"&gt;set head off feedback off long 5000
spool updatestats_after.dms
select 'STORE '||pgm_name||'_'||stmt_type||'_'||stmt_name
||CHR(10)
||'BEGIN wrapper.ps_stats(p_ownname=&amp;gt;user,p_tabname=&amp;gt;'''
||substr(stmt_text
 , INSTR(stmt_text,'(')+1
 , INSTR(stmt_text,')')-INSTR(stmt_text,'(')-1
 )
||'''); END;;'
from &lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/sqlstmt_tbl"&gt;ps_sqlstmt_tbl&lt;/a&gt;
where stmt_text like '%UPDATESTATS(%'
/
spool off&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
I have only tested this against HR9.0. Note that long columns such as PS_SQLSTMT_TBL.STMT_TEXT only become CLOBs when the application version reaches 9.0. &amp;nbsp; You cannot use the LIKE operation on the long column.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-400004847550273152?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/k_jAQLOUG6I" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/k_jAQLOUG6I/controlling-how-peoplesoft-cobol.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>2</thr:total><feedburner:origLink>http://blog.psftdba.com/2009/11/controlling-how-peoplesoft-cobol.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-2021058070571426795</guid><pubDate>Wed, 28 Oct 2009 08:19:00 +0000</pubDate><atom:updated>2009-10-28T08:19:28.430Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">Database Links</category><title>Database Links and PeopleSoft</title><description>I have seen Oracle database links used in conjuction with PeopleSoft at a number of customer sites over the years.  I think some scenarios are examples where it is reasonable to use a database link, some are not.&lt;br /&gt;
In Oracle RDBMS, a database link (see also the &lt;a href="http://download.oracle.com/docs/cd/E11882_01/server.112/e10595/ds_concepts002.htm#i1007709"&gt;Oracle Concepts Manual&lt;/a&gt;) specifies how a database server can create a session on another database in order to perform a remote operation. The remote database may be another Oracle database, or another type of database.  The remote operation may be a query, or it may modify remote data (DML operation), in which case a two-phase commit mechanism ensures transaction integrity across the databases.&lt;br /&gt;
&lt;br /&gt;
PeopleSoft does not use database links between databases because they are a database platform specific technology. Instead, the PeopleSoft methodology is to replicate data between databases with an application message. This works well with small data sets, and with larger data sets that change slowly. However, there are scenarios with very large data volumes where Application Messaging will struggle to keep up, and Oracle RDMBS specific replication technologies are an appropriate alternative. &lt;br /&gt;
&lt;br /&gt;
Data can be replicated between databases with Materialised Views (formerly known as Snapshots).  This is a SQL based technology.  Materialised Views Logs on the source database track changes in the source table.  An incremental or full refresh process is run on the target, usually as a scheduled job. &lt;br /&gt;
&lt;br /&gt;
Oracle introduced an alternative replication technology in Oracle 9i called ‘Streams’. This uses supplementary redo logging on the source database which can then be applied to the target. Additional database server processes transmit, receive and apply this redo stream.  This technology is aimed a large volume replication for data warehouses.  I know of one site where several hundred database tables are replicated from PeopleSoft HR to a data warehouse.&lt;br /&gt;
&lt;br /&gt;
I think the clearest way is to explain the good and bad use of links is by example.&lt;br /&gt;
&lt;br /&gt;
At two different sites with HR and Finance, I have seen similar examples of both good and bad use of database links (one was on PeopleTools 7.0, before Application Messaging was available).&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;GL data needs to be sent from Payroll in the HR database to GL in the Financials database.  The PeopleSoft delivered mechanism was to use generate and reload an interface file.  This customer changed replaced the interface table in HR with a view that referenced a table with the same name and structure on the Financial system via a link.  The payroll GL extract process now inserted directly into the table on Financials.  I think this is a perfectly reasonable use of a database link.  It was simple.  It performed better than the interface file, and would certainly have been faster than application messaging.&amp;nbsp; &lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;However, the same customer also used the employee expenses module in Financials. This required employee personal data. So they made the PS_PERSONAL_DATA table in Financials a view of the corresponding table in HR. The result was that every time somebody opened the expenses component in Financials, the application server process referenced the database link to HR. There is a limit on the maximum number of database links that a session can concurrently open (OPEN_LINKS) and that the whole database instance can concurrently open (OPEN_LINKS_PER_INSTANCE). They both default is 4, which gives an indication of how Oracle expect you to use this feature. This system ran out of database links.  No Oracle errors were generated, instead sessions wait to be allowed to make the connection to the remote database. There are specific database link wait events that report the amount of time lost.&amp;nbsp; Eventually you reach the point where Tuxedo services timeout and then users receive errors.  I think this is an example of the wrong way to use a database link.  Instead I think that the data should have been replicated from HR to Financials.  In a modern PeopleSoft system this should be done with an application message (on the 7.0 system a Materialised View could have been used).&amp;nbsp; &lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Not only were database links used to provide data to components, but the same views, that referenced remote objects, were used in queries and reports resulting in complex distributed queries.  When multiple remote objects are referenced in a single SQL, I have seen Oracle decide to copy the whole of one or more of these objects to the local temporary tablespace.  This does not result in good performance.  Remember also, that even query only operations via a database link create a transaction on the remains until a commit or rollback is issued. &lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;I worked on another site where Materialised Views were used to incrementally replicate data from CRM and HR to EPM databases. Processes in the EPM database then referenced the Materialised Views. In this system, there was never any question of EPM processes referencing objects via the links.  The data volumes were such that Application Messaging would never have coped.  In this case only the Materialised View refresh process references the database links, and so the DBA can manage the usage of links.  This is a system where (after upgrade to Oracle 10g) Streams could also have been used to replicate the data.&lt;/li&gt;
&lt;/ul&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
My view is that database links should not be used to directly support functionality in either on-line components or reporting. When this is done in reports and queries it presents the problem of tuning distributed queries, and having to decide whether local or remote database should drive the query. Instead data should be replicated to the local database, preferably by PeopleSoft messaging, but if necessary by Oracle replication. However, I think that it can be reasonable to use a database link in an occasional batch process that moves data between systems.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-2021058070571426795?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/jYNw-Yyh_IU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/jYNw-Yyh_IU/database-links-and-peoplesoft.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>7</thr:total><feedburner:origLink>http://blog.psftdba.com/2009/10/database-links-and-peoplesoft.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-310734979297341651</guid><pubDate>Sun, 11 Oct 2009 16:55:00 +0000</pubDate><atom:updated>2011-01-26T00:48:17.584Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">Batch Timings</category><title>Aggregating &amp; Purging Batch Timings</title><description>Application Engine can collect timing information for the programs being execution.  These 'batch timings' can be written to log file and/or tables in the database.  I always recommend that this is enabled in all environments.  The runtime overhead is very low, and this data is extremely valuable to determine the performance of a system over a period of time, and to identify the pieces of SQL or PeopleCode code that account for the most time.  The timing data collected for individual processes can be viewed directly within the Process Monitor component.&lt;br /&gt;
&lt;br /&gt;
The Process Scheduler purge process does not delete batch timings, so this data remains in the database indefinitely, although it can no longer be accessed via the Process Monitor.  Over time, on a busy system, a large volume of data can accumulate. In some ways this is a good thing.  There are good reasons to purge the Process Scheduler as aggressively as the business will permit.&amp;nbsp; The batch timings can still be analysed by direct SQL query. However, the sheer volume of data is likely to result in queries that can take quite a while to execute.  After a while, you are less likely to be interested in the performance of individual processes, but are more likely to want to aggregate the data.  So, it makes sense to hold the data at least partly aggregated.  &lt;br /&gt;
&lt;br /&gt;
I have produced a very simple Application Engine program (&lt;a href="http://www.go-faster.co.uk/scripts.htm#gfc_timings_arch"&gt;GFC_TIM_ARCH&lt;/a&gt;) to address this problem.  This program is available for &lt;a href="http://www.go-faster.co.uk/scripts.htm#gfc_timings_arch"&gt;download from the Go-Faster website&lt;/a&gt; as a PeopleTools Application Designer Project.  Please note that this process has been written using Oracle RDBMS specific SQL syntax.&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Application Engine stores batch timing data in three table &lt;i&gt;&lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/bat_timings_log.htm"&gt;PS_BAT_TIMINGS_LOG&lt;/a&gt;&lt;/i&gt;, &lt;i&gt;&lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/bat_timings_fn.htm"&gt;PS_BAT_TIMINGS_FN&lt;/a&gt;&lt;/i&gt; and &lt;i&gt;&lt;a target="_blank" href="http://www.go-faster.co.uk/peopletools/bat_timings_dtl.htm"&gt;PS_BAT_TIMINGS_DTL&lt;/a&gt;&lt;/i&gt;.&lt;/li&gt;
&lt;li&gt;I have created three new tables &lt;i&gt;PS_GFC_TIMINGS_LOG&lt;/i&gt;, &lt;i&gt;PS_GFC_TIMINGS_FN&lt;/i&gt; and &lt;i&gt;PS_GFC_TIMINGS_DTL&lt;/i&gt;.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;GFC_TIM_ARCH&lt;/i&gt; aggregates the data in each of these tables by the day on which the process begin, by the process name, and (where applicable) by the 'detail_id' column.  The aggregated data is put into the &lt;i&gt;GFC_TIMINGS%&lt;/i&gt; tables, the original data is removed from the &lt;i&gt;BAT_TIMINGS%&lt;/i&gt; tables.&lt;/li&gt;
&lt;li&gt;It finds the earliest three days for which timing data exists that is older than the longest Process Scheduler retention limit.  The idea is that the process should be run daily (the delivered 'Daily Purge' recurrence is suitable), but if it doesn't run for some reason it will catch up the next day.&lt;/li&gt;
&lt;li&gt;Only one instance of WMS_TIM_ARCH is permitted to run concurrently.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
On one system, where I tested this process, &lt;i&gt;BAT_TIMINGS_DTL&lt;/i&gt; was growing by over 1 million rows per day.  This became around 5000 rows per day in &lt;i&gt;GFC_TIMINGS_DTL&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
If you have been running with batch timings for a while, then when you first introduce this process you will probably have a large backlog of data to be aggregated and purged.  The easiest option is to run this process repeatedly until the data has been processed (possibly using the recurrence that causes a program to run every minute).  After the backlog has been cleared the &lt;i&gt;BAT_TIMINGS%&lt;/i&gt; tables should be rebuilt or shrunk in order to release the space left in the tables by the deleted rows.  This will help queries that scan the &lt;i&gt;BAT_TIMINGS_DTL&lt;/i&gt; record, otherwise these scans still need to include the empty rows because they are below the tables High Water Mark.&lt;br /&gt;
&lt;br /&gt;
Once the backlog has been cleared, the &lt;i&gt;GFC_TIM_ARCH&lt;/i&gt; process can run daily and, not withstanding variations in the load on the system, the rolling volume of data retained in the &lt;i&gt;BAT_TIMINGS%&lt;/i&gt; tables should be fairly constant, and it should not be necessary to rebuild them frequently.  Space freed by the daily delete should be used by new rows that are inserted into the table as AE processes run.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-310734979297341651?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/FTvkJIVehVE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/FTvkJIVehVE/aggregating-purging-batch-timings.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2009/10/aggregating-purging-batch-timings.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-8773421305452583790</guid><pubDate>Thu, 10 Sep 2009 18:40:00 +0000</pubDate><atom:updated>2009-09-11T14:20:00.643+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Application Engine</category><title>Identifying Application Engine Source Code</title><description>One of the recurring challenges of performance tuning PeopleSoft systems is that having identified a problem SQL statement with the aid of a database utility, such as Oracle Enterprise Manager or session trace, you often need to identify the source so that you can implement a change.&lt;br /&gt;&lt;br /&gt;Stored Outlines and SQL Profiles do not work well with PeopleSoft.  Bind variables in Application Engines often become literals before the SQL statement is submitted to the database, unless the &lt;a target="_blank" href="http://blog.psftdba.com/2009/02/performance-benefits-of-reuse-statement_27.html"&gt;ReUse Statement&lt;/a&gt; feature has been enabled, which it is not by default.&lt;br /&gt;&lt;br /&gt;I wrote about this in &lt;a target="_blank" href="http://www.psftdba.com/"&gt;PeopleSoft for the Oracle DBA&lt;/a&gt;, it contains a script called &lt;a target="_blank" href="http://www.go-faster.co.uk/scripts/stmtid.sql"&gt;stmtid.sql&lt;/a&gt; (listing 11-23, page 280) that adds an identifying comment to each of the stored statements used by PeopleSoft COBOL programs.&lt;br /&gt;&lt;br /&gt;Now, I have produced a similar script for Application Engine programs.  The script &lt;a target="_blank" href="http://www.go-faster.co.uk/scripts.htm#aeid.sql"&gt;aeid.sql&lt;/a&gt; adds identification comments containing the name of object.  It directly modifies the PeopleTools tables for Application Engine steps and PeopleSoft SQL objects.  These comments will be seen in database tools and traces.  So now when your database tools find a problem statement, it is easy to find the source.&lt;br /&gt;&lt;br /&gt;Below, is part of a screen shot from Oracle Enterprise Manager.  You can see the identifying comment in the SQL, which was added by this script&lt;br /&gt;&lt;br /&gt;&lt;a target="_blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.go-faster.co.uk/images/aeid.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 100%;" src="http://www.go-faster.co.uk/images/aeid.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The comment identifies the name of the Application Engine program, section, step and step type.   In this example, the SQL is from an Application Engine ID &lt;span style="font-style: italic;"&gt;GPGB_EDI_PRC&lt;/span&gt;, in section called &lt;span style="font-style: italic;"&gt;EDI_PRC&lt;/span&gt;, in a SQL type step called &lt;span style="font-style: italic;"&gt;Step01&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;For example, if you need adjust this SQL statement, perhaps add a hint to it, you don't have to spend time working out where it came from.  It is not uncommon to find many similar SQL statements in a program.  Also, where dynamic code is used to generate the SQL statement, it can be very tricky to find the exact source.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Using aeid.sql&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;u&gt;You will need to edit the script each time&lt;/u&gt;, to specify which Application Engine programs and SQL objects are to be commented.  The script is not perfect.  It does not handle some very long statements, so you cannot simply instrument the entire system in one go.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The script is designed to run in SQL*Plus.&lt;/li&gt;&lt;li&gt;It produces a report of statements that it has altered.&lt;/li&gt;&lt;li&gt;The script does not commit.  You should either &lt;span style="font-style: italic;"&gt;commit&lt;/span&gt; or &lt;span style="font-style: italic;"&gt;rollback&lt;/span&gt; the update depending upon whether you are satisfied with the output.&lt;/li&gt;&lt;li&gt;I suggest that the script only be run in pre-production environments.  The comments will of course be migrated along with any other changes.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-8773421305452583790?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/L9jPPEVC7PA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/L9jPPEVC7PA/identifying-source-code.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>1</thr:total><feedburner:origLink>http://blog.psftdba.com/2009/09/identifying-source-code.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-1358044497525697450</guid><pubDate>Tue, 28 Jul 2009 05:39:00 +0000</pubDate><atom:updated>2009-07-28T07:25:47.865+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Truncate</category><category domain="http://www.blogger.com/atom/ns#">Freelist</category><category domain="http://www.blogger.com/atom/ns#">ASSM</category><title>Automatic Segment Space -v- Freelist Management for PeopleSoft Temporary Records</title><description>&lt;style type="text/css"&gt;.nobr br { display: none }&lt;/style&gt;Earlier this year, I wrote about some research into the &lt;a target="_blank" href="http://blog.psftdba.com/2009/01/performance-implications-of-concurrent.html"&gt;effects of concurrent TRUNCATE TABLE operations in concurrent PeopleSoft Application Engine process&lt;/a&gt;.  Since then I have been prompted to look at the effect of Automatic Segment Space -v- Freelist (or Manual) Management.&lt;br /&gt;&lt;br /&gt;&lt;a target="_blank" href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/physical.htm#sthref523"&gt;ASSM&lt;/a&gt; was introduced in Oracle 9i for Locally Managed Tablespaces, and from Oracle 10g it is default option,  It was designed for high concurrency systems, and avoids some of the problems, such as contention on header blocks, that can occur with Freelist Management.  It uses tracking blocks to indicate utilisation of data blocks.  The ASSM structure is similar to a B-tree index (see Tanel Poder's presentation: &lt;a target="_blank" href="http://www.tanelpoder.com/files/Poder_Freelists_vs_ASSM.ppt"&gt;Freelists -v- ASSM in Oracle 9i&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Normally, using ASSM is preferable in multi-user OLTP systems.  The reduction in contention on the freelists should outweigh the additional I/O for the tracking blocks.&lt;br /&gt;&lt;br /&gt;However, non-shared instances of PeopleSoft Temporary Records are only used by a single Application Engine process at any one time.  Therefore, there would be no contention on freelists for these objects, and so DML operations on these objects will not derive much benefit from ASSM.  In fact, the contrary could easily be true.  The additional tracking blocks are also maintained during DML and are cleared out by local writes during a truncate operation.  Truncate operations are serialised in Oracle on the RO enqueue.  If you run multiple concurrent Application Engine programs you can get contention between the truncate operations.  The additional I/O on the ASSM tracking blocks causes the truncate to take longer and in extreme cases can aggravate truncate contention.&lt;br /&gt;&lt;br /&gt;The local write operations that occur during truncate operations cannot be deferred.  Significant truncate contention can be a symptom of disk contention.  However, switching back to Freelist Management saves the I/O to the tracking blocks, and so improves the performance of truncate.&lt;br /&gt;&lt;br /&gt;I ran a test where I truncated a table with 5000 rows.  I examined an Oracle trace with waits of the truncate.  I tested it in an ASSM tablespace, and Freelist Managed tablespace.  I tested truncating just the table on its own, and sometimes with an index.&lt;br /&gt;&lt;br /&gt;&lt;div class="nobr" style="width: 100%; overflow: auto; border: 0px solid #000000; padding-left: 4px; padding-right: 4px; background-color:#EEEEEE;font-family:courier new;font-size:85%;"&gt;&lt;br /&gt;&lt;table border="1" cellpadding="2" cellspacing="0"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;th&gt;Number of Wait Events&lt;/th&gt;&lt;br /&gt;&lt;th colspan="3"&gt;&lt;p align="center"&gt;Table only, without any indexes&lt;/p&gt;&lt;/th&gt;&lt;br /&gt;&lt;th colspan="3"&gt;&lt;p align="center"&gt;With a single primary key index&lt;/p&gt;&lt;/th&gt;&lt;br /&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Tablespace Type&lt;/td&gt;&lt;br /&gt;&lt;th&gt;db file sequential read&lt;/th&gt;&lt;br /&gt;&lt;th&gt;enq: RO - fast object reuse&lt;/th&gt;&lt;br /&gt;&lt;th&gt;local writes&lt;/th&gt;&lt;br /&gt;&lt;th&gt;db file sequential read&lt;/th&gt;&lt;br /&gt;&lt;th&gt;enq: RO - fast object reuse&lt;/th&gt;&lt;br /&gt;&lt;th&gt;local writes&lt;/th&gt;&lt;br /&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Automatic Segment Space Management&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;8&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;4&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;16&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;2&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;9&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Freelist Management&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;3&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;6&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;2&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;p align="center"&gt;3&lt;/p&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;You can see from this table that the truncates in the ASSM tablespace required more single block reads (db_file_sequential_read) and more local writes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Recommendation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In addition to my &lt;a target="_blank" href="http://blog.psftdba.com/2009/01/performance-implications-of-concurrent.html"&gt;previous recommendation&lt;/a&gt; to move the all (non-shared instances) of temporary working storage tables, and their indexes, to tablespaces with a larger block size, I also recommend that those tablespaces should be specified with MANUAL segment space management.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-1358044497525697450?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/JI8-mAaRdB0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/JI8-mAaRdB0/automatic-segment-space-v-freelist.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2009/07/automatic-segment-space-v-freelist.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-5691892938693685558</guid><pubDate>Thu, 23 Jul 2009 18:41:00 +0000</pubDate><atom:updated>2009-09-22T21:36:27.226+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Recyclebin</category><title>PeopleSoft and the Oracle Recycle Bin</title><description>If you are running PeopleSoft on Oracle 10g, what do you do about the &lt;a target="_blank" href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14231/tables.htm#i1011340"&gt;Recycle Bin&lt;/a&gt;?  It is a new feature in Oracle 10g, and it is enabled by default.  So you are using it, unless you have taken a decision to the contrary.&lt;br /&gt;&lt;br /&gt;It works just like the Windows recycle bin.  You can drop a table and then flash it back (they didn't call it UNDROP because Oracle marketing now calls everything Flashback).  So when you drop a table, Oracle marks it as dropped, and renames it with a system generated name beginning with BIN$.  You can look at the contents of the Recycle Bin through a catalogue view.&lt;br /&gt;&lt;br /&gt;&lt;div style="border: 0px solid rgb(0, 0, 0); overflow: auto; width: 100%; padding-left: 4px; padding-right: 4px; background-color: rgb(238, 238, 238);"&gt;&lt;pre style="font-family: courier new; font-size: 85%;"&gt;&gt;create table t (a number);&lt;br /&gt;&gt;drop table t;&lt;br /&gt;&gt;select * from user_recyclebin&lt;br /&gt;&lt;br /&gt;OBJECT_NAME                    ORIGINAL_NAME                    OPERATION TYPE&lt;br /&gt;------------------------------ -------------------------------- --------- -------------------------&lt;br /&gt;TS_NAME                        CREATETIME          DROPTIME               DROPSCN&lt;br /&gt;------------------------------ ------------------- ------------------- ----------&lt;br /&gt;PARTITION_NAME                   CAN CAN    RELATED BASE_OBJECT PURGE_OBJECT      SPACE&lt;br /&gt;-------------------------------- --- --- ---------- ----------- ------------ ----------&lt;br /&gt;BIN$ZCLja8iAA9LgRAAOf+3h5A==$0 T                                DROP      TABLE&lt;br /&gt;PSDEFAULT                      2009-03-02:13:35:12 2009-03-02:13:35:14 9.7561E+12&lt;br /&gt;                          YES YES     776642      776642       776642          4&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;If you don't want a table to go into the recycle bin when you drop it, you can code DROP TABLE ... PURGE - but PeopleSoft doesn't do this.&lt;br /&gt;&lt;br /&gt;PeopleSoft alter scripts usually drop and recreate tables.  In a production system this is often the best option, otherwise you run the risk of causing rows to migrate to other blocks when you add new columns.   In one system, I found 17000 objects in the recycle bin, occupying 1.3Gb.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;My Opinion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Personally, I would disable the recycle bin by setting the initialisation parameter &lt;a target="_blank" href="http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/initparams175.htm"&gt;RECYCLEBIN = OFF&lt;/a&gt; in all PeopleSoft environments, with the possible exception of the development environment.&lt;br /&gt;&lt;br /&gt;The RECYCLEBIN parameter can also be set dynamically at session or system level.  You could perhaps turn it on prior to upgrade/data migration procedures, and then when satisfied turn it off again and manually purge the recycle bin.&lt;br /&gt;&lt;br /&gt;I think Oracle features should be used knowingly.  It doesn't matter whether you decide to use a feature or not.  It &lt;span style="font-style: italic;"&gt;is&lt;/span&gt; important that in making that decision you have thought about how to deal with the implications, rather than be surprised later.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-5691892938693685558?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/6HqeSYbqXSU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/6HqeSYbqXSU/peoplesoft-and-oracle-recycle-bin.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>4</thr:total><feedburner:origLink>http://blog.psftdba.com/2009/07/peoplesoft-and-oracle-recycle-bin.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-3231799605698304596</guid><pubDate>Tue, 14 Jul 2009 09:18:00 +0000</pubDate><atom:updated>2011-05-07T17:52:43.016+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Terminated Connection Timeout</category><category domain="http://www.blogger.com/atom/ns#">Dead Connect Detection</category><category domain="http://www.blogger.com/atom/ns#">SQL*Net</category><title>Oracle Terminated Connection Timeout</title><description>I have recently come across situations on two different PeopleSoft sites where ad-hoc queries continue to run on the Oracle database server long after the Application Server process, which is the Oracle session client, has terminated. Often, queries perform poorly because they are poorly coded, but that is another story.   To help guard against this situation Oracle has mechanism called &lt;a target="_blank" href="http://download.oracle.com/docs/cd/B28359_01/network.111/b28316/profile.htm#sthref694"&gt;Terminated Connection Timeout&lt;/a&gt; (also known as &lt;a target="_blank" href="http://download-west.oracle.com/docs/cd/A87860_01/doc/network.817/a76933/concepts.htm#997825"&gt;Dead Connection Detection&lt;/a&gt; (DCD) when it was introduced in Net8).&lt;br /&gt;
&lt;br /&gt;
Oracle Support Note &lt;a target="_blank" href="https://support.oracle.com:443/CSP/ui/flash.html#tab=KBHome(page=KBHome&amp;id=()),(page=KBNavigator&amp;id=(bmDocDsrc=DOCUMENT&amp;viewingMode=1143&amp;bmDocID=615782.1&amp;bmDocTitle=E-AS:%20Managing%20Long%20Running%20Queries%20After%20the%20Server%20Process%20has%20Terminated%20on%20Oracle%20Database&amp;from=BOOKMARK&amp;bmDocType=PROBLEM))"&gt;615782.1&lt;/a&gt; explains the mechanism. &lt;span&gt;"DCD is initiated on the server when a connection is established. At this time SQL*Net reads the SQL*Net parameter files and sets a timer to generate an alarm.   The timer interval is set by providing a non-zero value in &lt;u&gt;minutes&lt;/u&gt; for the &lt;span style="font-style: italic;"&gt;SQLNET.EXPIRE_TIME&lt;/span&gt; parameter in the &lt;span style="font-style: italic;"&gt;sqlnet.ora&lt;/span&gt; file on the database server side. When the timer expires, SQL*Net on the server sends a 'probe' packet, essentially an empty SQL*Net packet, to the client. If the client end of the connection is still active, the probe is discarded, and the timer mechanism is reset.  If the client has terminated abnormally, the server will receive an error from the send call issued for the probe, and SQL*Net on the server will signal the operating system to release the connection's resources."&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Thus, if a PeopleSoft operator initiates an ad-hoc query that runs on the &lt;span style="font-style: italic;"&gt;PSQRYSRV&lt;/span&gt; server for longer than the &lt;span style="font-style: italic;"&gt;ICQuery &lt;/span&gt;service time-out (default 1200 seconds), then Tuxedo will terminate and restart the busy server process.  However, the query will continue run on the database server until the current fetch operation returns.  In the case of a query performing a large sort or hash operation, it might be a long time before the first row is returned.  All the while, the query is continuing to consume resources on the database.&lt;br /&gt;
&lt;br /&gt;
PeopleTools 8.44, also introduced the ability to kill a query via the Query Monitor that had reached a maximum run time.  This is one of the functions of the PSMONITORSRV server process (see Oracle Support Note &lt;a target="_blank" href="https://support.oracle.com:443/CSP/ui/flash.html#tab=KBHome(page=KBHome&amp;id=()),(page=KBNavigator&amp;id=(bmDocDsrc=DOCUMENT&amp;viewingMode=1143&amp;bmDocID=624339.1&amp;bmDocTitle=E-PerfMon:%20What%20is%20the%20PSMONITORSRV%20Process%20Used%20for?%20Can%20it%20be%20Disabled?&amp;from=BOOKMARK&amp;bmDocType=PROBLEM))"&gt;624339.1&lt;/a&gt;.   The maximum run time is specified in a permission list (see Security Administration PeopleBook &lt;a target="_blank" href="http://download.oracle.com/docs/cd/E13292_01/pt849pbr0/eng/psbooks/tsec/chapter.htm?File=tsec/htm/tsec04.htm%23d0e5379"&gt;Permission List Query Profile&lt;/a&gt;) and then the ability to kill queries that have timed out can be enabled or disabled system-wide (see PeopleSoft Query PeopleBook &lt;a target="_blank" href="http://download.oracle.com/docs/cd/E13292_01/pt849pbr0/eng/psbooks/tpsq/chapter.htm?File=tpsq/htm/tpsq09.htm%23d0e8623"&gt;Query Administration&lt;/a&gt;. It will kill the application server process that submitted the query, but for the same reasons, the query may continue to run on the database.&lt;br /&gt;
&lt;br /&gt;
Thus, setting Terminated Connection Timeout is not merely a good idea for a PeopleSoft system running on Oracle, it is effectively mandatory.  Otherwise. some PeopleSoft functionality simply won't work as intended.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;What is an appropriate value for SQLNET.EXPIRE_TIME?&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The value for this parameter is the time between successive SQL*Net probes sent by the Oracle shadow server process to the client.  Setting it is a balance between the maximum time that a query can be left to consume resources after a client process terminates, against the additional overhead of every client process sending a probe every few minutes.&lt;br /&gt;
&lt;br /&gt;
The SQL*Net documents often talk about additional network traffic generated by DCD.  This was a consideration in the past on client-server applications that ran across a wide area network.  However, it is rarely a consideration in relatively modern systems such as PeopleTools 8, the database connections are made by Application Server and Process Scheduler, which are usually physically close to the database server.&lt;br /&gt;
&lt;br /&gt;
The time-out can be set independently of any of the other time-outs for the Application Server and Web Server.   Documents on Metalink often suggest 5 or 10 minutes, and I don't think that is unreasonable.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;My thanks to Colin Kilpatrick who prompted me to look at this again.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-3231799605698304596?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/miRAcDFRyGE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/miRAcDFRyGE/oracle-terminated-connection-timeout.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>1</thr:total><feedburner:origLink>http://blog.psftdba.com/2009/07/oracle-terminated-connection-timeout.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-6355404204789194109</guid><pubDate>Mon, 06 Jul 2009 21:48:00 +0000</pubDate><atom:updated>2009-09-22T22:05:40.656+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">global temporary tables</category><category domain="http://www.blogger.com/atom/ns#">Restartability</category><category domain="http://www.blogger.com/atom/ns#">Application Engine</category><title>Dynamically Switching PeopleSoft Temporary Records between Global Temporary and Normal Tables during Application Engine Programs</title><description>I am working on a Time &amp;amp; Labour system. We run the main T&amp;amp;L process in different modes. There is a company wide overnight batch process, but individual parts of the company can run the process for their section during the day to process exceptions and to generate schedules. This is done with a custom AE program that calls the delivered TL_TIMEADMIN.&lt;br /&gt;&lt;br /&gt;Running on Oracle 10gR2, we have faced performance problems caused by contention between concurrent truncate operations in these processes (see &lt;a href="http://blog.psftdba.com/2009/01/performance-implications-of-concurrent.html"&gt;Factors Affecting Performance of Concurrent Truncate of Working Storage Tables&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;One way to evade this problem is to convert the working storage tables to be Global Temporary Tables (GTTs).  The contention problem caused by serialisation of truncate operations does not occur with GTTs.  Last year I wrote a blog entry (&lt;a href="http://blog.psftdba.com/2008/01/global-temporary-tables-and-peoplesoft.html"&gt;Global Temporary Tables and PeopleSoft Temporary Records&lt;/a&gt;) that discussed when it was possible to use GTTs in Application Engine (AE) programs. Essentially, you can only use GTTs when restart is disabled for all AE programs that reference a temporary records.&lt;br /&gt;&lt;br /&gt;So we could consider running the small but frequent daytime processes with restart disabled. Then we can make the non-shared instances of the temporary records into Global Temporary tables for the non-restartable daytime processes. However, we want to continue to run the overnight processes with restart enabled so that we have the option to restart a process that fails overnight from where it crashed, rather than going back to the beginning. The same non-shared instances may need to be Global Temporary during the day but normal recoverable tables by night.&lt;br /&gt;&lt;br /&gt;Previously, I have only converted tables to Global Temporary where they are not referenced by a restartable AE program.  I have now devised a way of switching a table to being a GTT if it is allocated to a process for which restart is disabled, and switching it back to a normal table if not. This is the best of both worlds.&lt;br /&gt;&lt;br /&gt;When a non-shared instance of a temporary record is allocated to a particular process instance, a row is inserted into PS_AETEMPTBLMGR. The value of the restart disable flag is also stored in PS_AETEMPTBLMGR. I have created a &lt;a target="blank" href="http://www.go-faster.co.uk/scripts.htm#gfc_temp_table_type.sql"&gt;trigger&lt;/a&gt; that switches a non-shared instance of a temporary record from a normal table to a GTT if restart is disabled, or switches it back to a normal table if restart is enabled. The trigger will create the GTT and any indexes on it if necessary, and will rename the tables as necessary so that the correct version has the default name expected by the process.&lt;br /&gt;&lt;br /&gt;So if, for example, I have instance number 11 of a record called WRK_SHIFT_TAO, then the table will be called PS_WRK_SHIFT_TAO11. If that is allocated to a non-restartable AE program, the trigger will check for a table called GT_WRK_SHIFT_TAO11. If it doesn't exist the trigger will create it, dynamically generating the DDL with the &lt;a target="blank" href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_metada.htm#i1019414"&gt;dbms_metadata&lt;/a&gt; package. Thus, the structure of the GTT will be identical to PS_WRK_SHIFT_TAO11, the leading PS will also be changed to GT. The same indexes will also be created. The original normal table PS_WRK_SHIFT_TAO11 will be renamed to XX_WRK_SHIFT_TAO11, and then the GTT, at that point called GT_WRK_SHIFT_TAO11 will be renamed to PS_WRK_SHIFT_TAO11. The indexes are not renamed. If later the same instance is allocated to a restartable process, the change will be reversed by renaming again.  The GTT will not be dropped so that it does not need to be recreated again the next time the non-restartable program uses it.&lt;br /&gt;&lt;br /&gt;&lt;a target="blank" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_SlmmwRbAUdk/SlJzW8vcYmI/AAAAAAAAALA/AFHINGl3Luw/s1600-h/gfc_temp_table_type.JPG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 164px; height: 200px;" src="http://4.bp.blogspot.com/_SlmmwRbAUdk/SlJzW8vcYmI/AAAAAAAAALA/AFHINGl3Luw/s200/gfc_temp_table_type.JPG" alt="" id="BLOGGER_PHOTO_ID_5355469744996377186" border="0" /&gt;&lt;/a&gt;All DDL generated and executed by the trigger is written to the Message Log for that process (click on the screen shot to enlarge).&lt;br /&gt;&lt;br /&gt;The shared instance of a record (the one without a instance number suffix) is never rebuilt as a Global Temporary table because it is possible that a restartable and non-restartable process might both use the shared instance at the same time.&lt;br /&gt;&lt;br /&gt;One complication is how to handle changes to the temporary records. Application Designer will only create normal tables. So, if the table is to be rebuilt, them it needs to be switched back to a normal table, and the corresponding GTT created by the trigger should then be dropped. The Application Designer can recreate the table in the usual way. Next time the non-restartable AE runs, it will recreate the GTT with the new structure.&lt;br /&gt;&lt;br /&gt;An updated version of &lt;a href="http://www.go-faster.co.uk/scripts.htm#t_lock.sql"&gt;T_LOCK&lt;/a&gt; is available which handles PeopleSoft temporary records and prevent DDL on PS_ tables with corresponding GT_ tables.  The commands which swap these tables back and forth are explicitly permitted by the new version of this trigger.&lt;div class="blogger-post-footer"&gt;&lt;a href="http://www.go-faster.co.uk/"&gt;©David Kurtz, Go-Faster Consultancy Ltd.&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25740336-6355404204789194109?l=blog.psftdba.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/YN8goos9sfQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/YN8goos9sfQ/dynamically-switching-peoplesoft.html</link><author>noreply@blogger.com (David Kurtz)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_SlmmwRbAUdk/SlJzW8vcYmI/AAAAAAAAALA/AFHINGl3Luw/s72-c/gfc_temp_table_type.JPG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2009/07/dynamically-switching-peoplesoft.html</feedburner:origLink></item></channel></rss>

