<?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:blogger="http://schemas.google.com/blogger/2008" 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>Mon, 20 May 2013 05:04:17 +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>Active Data Guard</category><category>Instrumentation</category><category>Dead Connect Detection</category><category>Append</category><category>Recyclebin</category><category>Standby Database</category><category>row source alias</category><category>Application Server</category><category>PeopleTools 8.49</category><category>ORA-01652 unable to extend temporay segment</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>Stored Outlines</category><category>local write wait</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>DBMS_MONITOR</category><category>Flashback Query</category><category>PeopleSoft Temporary Records</category><category>Unix Process Limits Memory Application Engine</category><category>CURRENT_SCHEMA</category><category>Grid Control</category><category>Cache</category><category>Component Processor</category><category>Statistics</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>Conference</category><category>Dirty Reads</category><category>Temporary Tablespace</category><category>clone database</category><category>DBMS_STATS</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>DBMS_APPLICATION_INFO</category><category>%UpdateStats</category><category>sparse index</category><category>Scheduled Queries</category><category>Redo</category><category>DDL Model</category><category>MetaSQL</category><category>PeopleTools 8.52</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>95</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-202837616720771605</guid><pubDate>Fri, 23 Nov 2012 16:46:00 +0000</pubDate><atom:updated>2012-11-24T12:47:42.076Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">DBMS_MONITOR</category><category domain="http://www.blogger.com/atom/ns#">PeopleTools 8.52</category><category domain="http://www.blogger.com/atom/ns#">Instrumentation</category><category domain="http://www.blogger.com/atom/ns#">Application Engine</category><category domain="http://www.blogger.com/atom/ns#">DBMS_APPLICATION_INFO</category><title>PeopleTools 8.52 Application Engine sets MODULE and ACTION</title><description>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
I have written and spoken often about the &lt;a href="http://www.go-faster.co.uk/psftpres.htm#Instrumentation" target="_blank"&gt;huge importance of instrumentation&lt;/a&gt; in an application.  PeopleSoft introduced internal instrumentation to PeopleTools 8.44 for its own &lt;a href="http://blog.psftdba.com/search/label/Performance%20Monitor" target="_blank"&gt;Performance Monitor&lt;/a&gt; that works on any platform.  Since PeopleTools 7.53, when running on an Oracle database, PeopleTools also calls the Oracle supplied package &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 the module and action for the session. The values set were not particularly useful, so I wrote a PL/SQL package (&lt;a href="http://www.go-faster.co.uk/scripts.htm#psftapi.sql" target="_blank"&gt;psftapi)&lt;/a&gt; and trigger to set the module and action attributes when a process started.&lt;br /&gt;
&lt;br /&gt;
In PeopleTools 8.50 this instrumentation was enhanced to &lt;a href="http://blog.psftdba.com/2010/11/peopletools-850-uses.html"&gt;set module and action to the component and page name in the PIA&lt;/a&gt;. These &lt;a href="http://blog.psftdba.com/2009/03/using-oracle-enterprise-manager-grid.html" target="_blank"&gt;values appear in Oracle Enterprise Manager&lt;/a&gt; and &lt;a href="http://www.go-faster.co.uk/ukougpres.htm#Practical_ASH.ppt" target="_blank"&gt;Active Session History (ASH)&lt;/a&gt;. They can be used to profile components.&lt;br /&gt;
&lt;br /&gt;
Now, in PeopleTools 8.52 Application Engine has been enhanced to set the action to the full Application Engine step name.&lt;br /&gt;
&lt;br /&gt;
To illustrate what happens, I have produced an &lt;a href="http://blog.psftdba.com/2008/05/enabling-oracle-database-trace-on.html" target="_blank"&gt;Oracle Extended SQL Trace&lt;/a&gt; for the process, but I have shown only the lines in a SQL Trace file with 3 asterisks.  So you can see MODULE and ACTION being set (and some other timestamp information).  The lines in &lt;i&gt;italic&lt;/i&gt; were emitted when module and action were set by my &lt;a href="http://www.go-faster.co.uk/scripts.htm#psftapi.sql" target="_blank"&gt;psftapi&lt;/a&gt; package, in which I simply set the MODULE to the Application Engine program name.&lt;br /&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;*** 2012-11-22 21:47:38.282
*** SESSION ID:(7.2237) 2012-11-22 21:47:38.282
*** CLIENT ID:(PS) 2012-11-22 21:47:38.282
*** SERVICE NAME:(HCM91) 2012-11-22 21:47:38.282
&lt;i&gt;*** MODULE NAME:(PSPMCSOSUM) 2012-11-22 21:47:38.282
*** ACTION NAME:(PI=867:Processing) 2012-11-22 21:47:38.282
&lt;/i&gt;*** MODULE NAME:(PSAE.PSPMCSOSUM.1448) 2012-11-22 21:47:38.419
*** ACTION NAME:(PSPMCSOSUM) 2012-11-22 21:47:38.419
*** ACTION NAME:(PSPMCSOSUM.MAIN.STATS.S) 2012-11-22 21:47:38.420
*** 2012-11-22 21:47:38.588
*** ACTION NAME:(PSPMCSOSUM.MAIN.CTL.P) 2012-11-22 21:47:38.919
*** ACTION NAME:(PSPMCSOSUM.MAIN.SELECT.C) 2012-11-22 21:47:38.937
*** ACTION NAME:(PSPMCSOSUM.GETCNT.CNT.P) 2012-11-22 21:47:38.963
…
*** ACTION NAME:(PSPMCSOSUM.MAIN.SELECT.C) 2012-11-22 21:48:15.168
*** ACTION NAME:(PSPMCSOSUM.GETCNT.CNT.P) 2012-11-22 21:48:15.168
*** ACTION NAME:(PSPMCSOSUM.MAIN.Close.P) 2012-11-22 21:48:15.197
&lt;i&gt;*** ACTION NAME:(PI=867:Success) 2012-11-22 21:48:15.201&lt;/i&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Application Engine also sets the MODULE attribute.&amp;nbsp; In the above example it was set to &lt;code&gt;PSAE.PSPMCSOSUM.1448&lt;/code&gt;.&amp;nbsp; The string is composed of three parts.&lt;br /&gt;
&lt;ul style="text-align: left;"&gt;
&lt;li&gt;PSAE indicates that it was running Application Engine. It sets the same value for both stand-alone and Tuxedo server Application Engine processes.&lt;/li&gt;
&lt;li&gt;PSPMCSOSUM was the name of the Application Engine Program&lt;/li&gt;
&lt;li&gt;1448 was the operating system process ID of the Application Engine process.&amp;nbsp; This value is recorded in the column SESSIONIDNUM on the table PSPRCSQUE.&lt;/li&gt;
&lt;/ul&gt;
The inclusion of the SESSIONIDNUM is useful because it is possible to determine the process instance.&amp;nbsp; If you have multiple instances of the same Application Engine program running concurrently, you can work out which ASH samples correspond to which process instance by matching the session ID.&lt;br /&gt;
&lt;br /&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;SELECT  r.prcsinstance, h.module, h.action, sum(1) ash_secs
FROM v$active_Session_history h
, psprcsque q
, psprcsrqst r
WHERE r.prcsinstance = q.prcsinstance
AND h.module = 'PSAE.'||q.prcsname||'.'||q.sessionidnum
AND h.sample_time BETWEEN r.begindttm AND NVL(r.enddttm,SYSDATE)
and r.prcsinstance = 867
GROUP BY r.prcsinstance, h.module, h.action
ORDER BY ash_secs DESC
/

PRCSINSTANCE MODULE               ACTION                    ASH_SECS
------------ -------------------- ------------------------- --------
         867 PSAE.PSPMCSOSUM.1448 PSPMCSOSUM.GETCNT.CNT.P          7
         867 PSAE.PSPMCSOSUM.1448 PSPMCSOSUM.MAIN.STATS.S          1
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Unfortunately, this change to MODULE also means that it is no longer possible to enable trace by setting a watchpoint (&lt;a href="http://blog.psftdba.com/2012/09/oracle-sql-tracing-by-module-action.html" target="_blank"&gt;as I blogged previously&lt;/a&gt;) now does not work with Application Engine because it is not possible to predict the value of the SESSIONIDNUM in MODULE!&lt;br /&gt;
&lt;br /&gt;
I am testing with 8.52.12 and have found a significant problem.  Application Engine doesn't set the action on DO SELECT statements.  The first statement in the trace extract below does come from &lt;code&gt;PSPMCSOSUM.MAIN.CTL&lt;/code&gt; PeopleCode, however, the second SQL statement actually comes from &lt;code&gt;PSPMCSOSUM.MAIN.SELECT.D&lt;/code&gt;, but the absence of another action line would lead you to believe the statement came from the preceding PeopleCode step.

&lt;br /&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;&lt;b&gt;*** ACTION NAME:(PSPMCSOSUM.MAIN.CTL.P) 2012-11-22 21:47:38.919&lt;/b&gt;
WAIT #0: nam='SQL*Net message to client' ela= 0 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220814597
WAIT #0: nam='SQL*Net message from client' ela= 740 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220815371
CLOSE #348647416:c=0,e=33,dep=0,type=1,tim=257220815447
=====================
PARSING IN CURSOR #348528704 len=98 dep=0 uid=45 oct=3 lid=45 tim=257220815496 hv=1424819941 ad='7ff2559e550' sqlid='98af7ppafu1r5'
&lt;b&gt;&lt;/b&gt;END OF STMT
PARSE #348528704:c=0,e=35,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1148792852,tim=257220815496
EXEC #348528704:c=0,e=30,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1148792852,tim=257220815572
WAIT #348528704: nam='SQL*Net message to client' ela= 1 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220815612
FETCH #348528704:c=0,e=2824,p=0,cr=16,cu=0,mis=0,r=1,dep=0,og=1,plh=1148792852,tim=257220818462
STAT #348528704 id=1 cnt=1 pid=0 pos=1 obj=0 op='SORT AGGREGATE (cr=16 pr=0 pw=0 time=2828 us)'
STAT #348528704 id=2 cnt=3075 pid=1 pos=1 obj=228740 op='INDEX FULL SCAN PS_PSPMCSO_CHART (cr=16 pr=0 pw=0 time=333 us cost=0 size=13 card=1)'
WAIT #348528704: nam='SQL*Net message from client' ela= 123 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220818671
FETCH #348528704:c=0,e=1,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,plh=1148792852,tim=257220818696
WAIT #348528704: nam='SQL*Net message to client' ela= 0 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220818714
WAIT #348528704: nam='SQL*Net message from client' ela= 702 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220819434
CLOSE #349389936:c=0,e=16,dep=0,type=1,tim=257220819496
=====================
PARSING IN CURSOR #349702624 len=555 dep=0 uid=45 oct=3 lid=45 tim=257220819548 hv=1888777338 ad='7ff2559ce90' sqlid='5phgqq9s98x3u'
&lt;b&gt;SELECT DISTINCT OPRID , TO_CHAR(CAST((LOGINDTTM) AS TIMESTAMP),'YYYY-MM-DD-HH24.MI.SS.FF') , TO_CHAR(CAST((LOGOUTDTTM) AS TIMESTAMP),'YYYY-MM-DD-HH24.MI.SS.FF') , PM_SIGNON_TYPE , PM_SIGNOFF_TYPE FROM PSPMCSOLOG_VW WHERE '1' &amp;lt;&amp;gt; '2' AND TO_DATE(TO_CHAR(LOGINDTTM,'YYYY-MM-DD'),'YYYY-MM-DD') &amp;gt;= TO_DATE('2005-07-06','YYYY-MM-DD') AND TO_DATE(TO_CHAR(LOGOUTDTTM,'YYYY-MM-DD'),'YYYY-MM-DD') &amp;gt;= TO_DATE('2005-07-06','YYYY-MM-DD') AND OPRID NOT IN ( SELECT OPRID FROM PSPMCSOCHRTXOP) AND LOGIPADDRESS NOT IN ( SELECT LOGIPADDRESS FROM PSPMCSOCHRTXIP) ORDER BY 2&lt;/b&gt;
END OF STMT
PARSE #349702624:c=0,e=35,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=4226533831,tim=257220819547
EXEC #349702624:c=0,e=47,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=4226533831,tim=257220819669
WAIT #349702624: nam='SQL*Net message to client' ela= 1 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220819723
WAIT #349702624: nam='SQL*Net more data to client' ela= 76 driver id=1413697536 #bytes=8145 p3=0 obj#=-40016373 tim=257220831271
FETCH #349702624:c=15600,e=11621,p=0,cr=214,cu=0,mis=0,r=201,dep=0,og=1,plh=4226533831,tim=257220831375
WAIT #349702624: nam='SQL*Net message from client' ela= 988 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220832412
WAIT #0: nam='SQL*Net message to client' ela= 2 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220832481
WAIT #0: nam='SQL*Net message from client' ela= 40 driver id=1413697536 #bytes=1 p3=0 obj#=-40016373 tim=257220832541
&lt;b&gt;*** ACTION NAME:(PSPMCSOSUM.MAIN.SELECT.C) 2012-11-22 21:47:38.937&lt;/b&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
However, MAIN.SELECT.D duly appears in the AE batch timings report.
&lt;br /&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;

                               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    
------------------------------ ------- -------- ------- -------- ------- -------- --------
…
AE Program: PSPMCSOSUM

MAIN.CTL.D                           1      0.0       1      0.0       2      0.0      0.0
MAIN.SELECT.D                        1      0.0       1      0.0     556      0.0      0.0
MAIN.STATS.S                         1      0.0       1      0.5       0      0.0      0.5
…
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
This is a serious problem&amp;nbsp; If you profile the top SQL statements in an Application Engine (using either ASH as I have done above, or by profiling a trace file and looking for the top SQL statement by searching through the raw trace file) it will lead you to the wrong conclusion!&amp;nbsp; Time spent in &lt;code&gt;PSPMCSOSUM.MAIN.SELECT.D&lt;/code&gt; will be accounted as having been spent in &lt;code&gt;PSPMCSOSUM.GETCNT.CNT.P&lt;/code&gt;.&amp;nbsp; It is not until you look for the specific SQL statement in Application Designer that you realise that the source code doesn't match the instrumentation.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&amp;nbsp;&lt;a href="http://www.go-faster.co.uk/images/PSPMCSOSUM.MAIN.SELECT.D.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img alt="" border="0" src="http://www.go-faster.co.uk/images/PSPMCSOSUM.MAIN.SELECT.D.png" title="PSPMCSOSUM.MAIN.SELECT.D" width="100%" /&gt;&lt;/a&gt;&lt;/div&gt;
Nevertheless, this instrumentation is a very welcome and significant improvement for Application Engine. It will help to localise performance problems. However, until the ommisions are fixed, you will need to be aware of their implications and avoid falling into the traps.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;My thanks to &lt;a href="http://www.centrexcc.com/" target="_blank"&gt;Wolfgang Breitling&lt;/a&gt; for telling me about the feature. &lt;/i&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;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/MSPzP1Kmg2E" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/MSPzP1Kmg2E/peopletools-852-application-engine-sets.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2012/11/peopletools-852-application-engine-sets.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-7189814626467689768</guid><pubDate>Fri, 16 Nov 2012 11:50:00 +0000</pubDate><atom:updated>2012-11-16T13:30:58.988Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">ORA-01652 unable to extend temporay segment</category><category domain="http://www.blogger.com/atom/ns#">CURRENT_SCHEMA</category><category domain="http://www.blogger.com/atom/ns#">Active Data Guard</category><category domain="http://www.blogger.com/atom/ns#">Standby Database</category><category domain="http://www.blogger.com/atom/ns#">Temporary Tablespace</category><title>Using Two Temporary Tablespaces in PeopleSoft</title><description>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
&lt;a href="http://www.go-faster.co.uk/docs.htm#" target="_blank"&gt;&lt;i&gt;A longer version of this posting, with all necessary code and a demonstration test, is available as a technical note on the Go-Faster website.&lt;/i&gt;&lt;/a&gt;&amp;nbsp; I am working with two different PeopleSoft customers who have had challenges with the size of the temporary tablespaces.&amp;nbsp; Critical batch processes have failed because they have exhausted space in the temporary tablespace. &lt;br /&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;ORA-01652 unable to extend temporary segment...
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;In one case, the one and only temporary tablespace in a Payroll system has over time been extended to in excess of 360 GB.&amp;nbsp; This has happen in response to PeopleSoft processes that failed because they cannot allocate temporary tablespace because somebody else has consumed it.&amp;nbsp; This treated the symptom rather than the cause.&amp;nbsp; This system has a number of other Oracle database users who are have read-only access to the PeopleSoft data to perform adhoc queries.&amp;nbsp; These users all share the one temporary tablespace.&amp;nbsp; Occasionally, a query will be submitted that runs for many hours, writing many gigabytes of data to the temporary tablespace, when it would have been better to terminate the process&lt;/li&gt;
&lt;li&gt;Another system has 64 GB in the PSTEMP temporary tablespace used by SYSADM.&amp;nbsp; All other users already use another temporary tablespace, but PeopleSoft processes sometimes still fail because most of the temporary tablespace has been consumed by an adhoc PS/Query process, and there is nothing left for other processes. This system also has other Oracle database users with read-only access, but here they use the default TEMP temporary tablespace.&lt;/li&gt;
&lt;/ul&gt;
If a PeopleSoft system has database users executing adhoc queries, then allocating those users to separate temporary tablespace is a sensible first step.&lt;br /&gt;
&lt;br /&gt;
However, in this document I suggest going further.&amp;nbsp; I propose switching some PeopleSoft processes to use a different temporary tablespace.&amp;nbsp; Regular PeopleSoft processing will continue to use the first temporary tablespace, but adhoc queries will use the second temporary tablespace.&amp;nbsp; Thus, the first temporary tablespace can be sized to cater for normal processes safe in the knowledge that it won’t be consumed by adhoc queries and you won’t get failures due to space errors.&amp;nbsp; Meanwhile, the second tablespace can be limited to a reasonable size&amp;nbsp; and queries that make unreasonable demands on the temporary tablespace will error. &lt;br /&gt;
&lt;br /&gt;
I am going to exploit two features:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;When a session sets CURRENT_SCHEMA to another schema it uses the temporary tablespace assigned to that schema rather than its own.&lt;/li&gt;
&lt;li&gt;From PeopleTools 8.51 you can direct read-only activity via a second connection to a standby database. If you are using Oracle Active Data Guard you must configure something that looks like a second PeopleSoft database in another schema, but which actually is composed of database synonyms that point to the first schema. &lt;/li&gt;
&lt;/ul&gt;
To make PeopleSoft process use an alternative temporary tablespace: &lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Create a second schema (I'll call it SYSADMRP) in the same database, and specify a different temporary tablespace for the user.&lt;/li&gt;
&lt;li&gt;Create synonyms in the second schema for every table and view in the PeopleSoft (SYSADM) schema.&lt;/li&gt;
&lt;/ul&gt;
If you are not using Active DataGuard or are below PeopleTools 8.51 then you can set CURRENT_SCHEMA with two triggers.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Create an AFTER LOGON trigger that will set CURRENT_SCHEMA to SYSADM for PSQRYSRV query server processes only.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;
CREATE OR REPLACE TRIGGER sysadm.psqrysrv
AFTER LOGON
ON sysadm.schema
DECLARE
  l_module VARCHAR2(64);
BEGIN
  SELECT sys_context('USERENV', 'MODULE') 
  INTO   l_module
  FROM   dual;

  IF UPPER(l_module) LIKE 'PSQRYSRV%' THEN --then this is a PSQRYSRV session
    EXECUTE IMMEDIATE 'ALTER SESSION SET CURRENT_SCHEMA=SYSADMRP';
  END IF;
EXCEPTION WHEN NO_DATA_FOUND THEN NULL;
END;
/&amp;nbsp;&lt;/code&gt;&lt;/pre&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Create another trigger to set current schema for the application engine process that runs scheduled queries (you might want to add others to this list). &lt;/li&gt;
&lt;/ul&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;
CREATE OR REPLACE TRIGGER sysadm.set_current_schema
BEFORE UPDATE OF runstatus ON sysadm.psprcsrqst
FOR EACH ROW
WHEN (  (new.runstatus = '7' OR old.runstatus != '7') --if starting or terminating
     AND new.prcsname IN('PSQUERY')) --restrict to certain programs
DECLARE
BEGIN
  IF :new.runstatus = '7' THEN --if starting set alternative schema
    EXECUTE IMMEDIATE 'ALTER SESSION SET CURRENT_SCHEMA=SYSADMRP';
  ELSE --when process terminates reset to standard schema in case this is a PSAESRV process
    EXECUTE IMMEDIATE 'ALTER SESSION SET CURRENT_SCHEMA=SYSADM';
  END IF;
END;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
If you are using at least PeopleTools 8.51, you don't need these triggers.&amp;nbsp; Instead you can configure the standby connection but to the same database&lt;br /&gt;
&lt;ul style="text-align: left;"&gt;
&lt;li&gt;PS/Queries will always use the standby connection and hence the alternative tablespace.&amp;nbsp;&amp;nbsp; &lt;/li&gt;
&lt;li&gt;Batch processes can be marked as read only to make them connect to the standby connection. Remember that you must use &lt;i&gt;PSAESRV&lt;/i&gt; processes.&lt;/li&gt;
&lt;li&gt;If you want to make specific PeopleSoft components use the alternative temporary tablespace then you can only do this by perverting the PeopleTools Active Data Guard support and marking components as read only&lt;span style="font-size: large;"&gt;&lt;b&gt;.&lt;/b&gt;&lt;/span&gt; &lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
Conlusion&lt;/h4&gt;
Now, you can choose an appropriate size for&lt;b&gt; &lt;/b&gt;the one temporary tablespaces that will be sufficient for the regular operation of the application.&amp;nbsp; Adhoc queries will use alternative temporary tablespace.&amp;nbsp; You might choose to set a temporary tablespace size that may still cause queries to fail with a temproray tablespace error, but at least that won’t cause business-as-usual processes to crash.&lt;br /&gt;
&lt;br /&gt;
You could even choose to run with three temporary tablespaces, one for PeopleSoft processes, one for PeopleSoft queries, and one for adhoc users accessing the database directly.&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;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/rcyn0TBsRBM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/rcyn0TBsRBM/using-two-temporary-tablespaces-in.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>2</thr:total><feedburner:origLink>http://blog.psftdba.com/2012/11/using-two-temporary-tablespaces-in.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-2307818139874128213</guid><pubDate>Sun, 30 Sep 2012 18:30:00 +0000</pubDate><atom:updated>2012-10-02T02:28:30.149+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DBMS_STATS</category><category domain="http://www.blogger.com/atom/ns#">Statistics</category><title>Maintaining Optimizer Statistics on PeopleSoft on Oracle 11g</title><description>I have been considering how to collect optimizer statistics for a PeopleSoft system running on an Oracle 11g database.&amp;nbsp; Despite 11g being several years old, most of my current customers are still using 10g, though some are looking at the upgrade to 11g.&amp;nbsp; I believe a slightly different approach is required. &lt;br /&gt;
&lt;br /&gt;
In 2009, I wrote a &lt;a href="http://blog.psftdba.com/search/label/DBMS_STATS"&gt;series of blog postings on the subject of collecting statistics&lt;/a&gt;.&amp;nbsp; However these were all based on Oracle 10g.&amp;nbsp; I proposed a PL/SQL package that would use meta-data in a database table to determine how to collect statistics on a table, or deliberate supress collection of statistics. &lt;br /&gt;
I also recommended that statistics on tables created for use as temporary records in Application Engine programs should have their statistics deleted and locked&amp;nbsp; to prevent system-wide jobs refreshing their statistics. I proposed a package that collected statistics according to meta-data defined in a table.&lt;br /&gt;
&lt;br /&gt;
IN 2011, Oracle published document 1322888.1 “pscbo_stats - Improving Statistics in Oracle RDBMS for PeopleSoft Enterprise”.&amp;nbsp; It takes a similar approach to my 10g package.&amp;nbsp; A PL/SQL package is used to collect statistics.&amp;nbsp; A number of tables control whether, when and how statistics are collected on each record.&amp;nbsp; Oracle's package is also intended to be used to collect schema-wide statistics.&lt;br /&gt;
&lt;br /&gt;
The &lt;i&gt;psbo_stats&lt;/i&gt; package is a attempt to solve a genuine problem, and it has continued to evolve since its initial release.&amp;nbsp; However, I have a number of objections to it.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;You are expected to replace the default automatic jobs that collect statistics with &lt;i&gt;pscbo_stats&lt;/i&gt;, so it is a move away from standard default maintenance procedures.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;pscbo_stats&lt;/i&gt; is still fundamentally a 10g solution. It does not use 11g table preferences.&lt;/li&gt;
&lt;li&gt;It does use the Oracle automatic sample size in 11g if histograms are not to be collected.&amp;nbsp; Otherwise, it defaults to the previous behaviour of either using 100% sample size for when called by &lt;i&gt;%UpdateStats&lt;/i&gt; with the ‘high’ sample size, or a variety of fixed sample sizes based on internal rules and the number of rows in the table. &lt;/li&gt;
&lt;li&gt;The package contains a procedure that collects statistics on all objects in the schema that also refreshes statistics that are not stale but which have not been refreshed for a period of time determined by the size of the table.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;When it collects histograms it always sets the maximum bucket size of 254. This may not always be desirable for height balanced histograms.&lt;/li&gt;
&lt;li&gt;There is no support for collecting aggregated or incremental statistics on partitioned objects.&lt;/li&gt;
&lt;/ul&gt;
I think that the 11g table preferences offer better control over collection of statistics.&lt;br /&gt;
&lt;br /&gt;
Oracle considerably enhanced the delivered DBMS_STATS package in 11g.&amp;nbsp; It became possible to specify default values for parameters in the &lt;i&gt;dbms_stats.gather_table_stats&lt;/i&gt; program for for each table.&lt;br /&gt;
&lt;br /&gt;
The guiding principle in 11g, and one that is not specific to PeopleSoft, is that instead of calling &lt;i&gt;dbms_stats.gather_table_stats&lt;/i&gt; with the desired parameters, we should set table preferences with the desired parameters and then just call dbms_stats without table specific parameters. We can then just leave the default database and schema-wide procedures get on with the job of collecting statistics. &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
A document that describes the updated approach and the various scripts are available on the &lt;a href="http://www.go-faster.co.uk/docs.htm#Managing.Statistics.11g" target="_blank"&gt;Go-Faste website&lt;/a&gt; at &lt;a href="http://www.go-faster.co.uk/docs.htm#Managing.Statistics.11g" target="_blank"&gt;http://www.go-faster.co.uk/docs.htm#Managing.Statistics.11g&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;CAVEAT: &lt;/b&gt;&lt;i&gt;This document represents some experimental work that is in progress.&amp;nbsp; It has not been tested against a customer system, let alone been used in production. I would welcome any feedback, and the opportunity to work with someone on a PeopleSoft system on Oracle 11g.&lt;/i&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;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/ZviEDhdZyIM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/ZviEDhdZyIM/maintaining-optimizer-statistics-on.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>1</thr:total><feedburner:origLink>http://blog.psftdba.com/2012/09/maintaining-optimizer-statistics-on.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-7682718476613133751</guid><pubDate>Thu, 20 Sep 2012 17:40:00 +0000</pubDate><atom:updated>2012-11-24T12:48:26.560Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">DBMS_MONITOR</category><category domain="http://www.blogger.com/atom/ns#">Oracle SQL Trace Trigger</category><title>Enabling Oracle Extended SQL Trace by Module and Action</title><description>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
I have written previously about the value of assigning meaningful values to the module and action attributes on a database session (see &lt;a href="http://blog.psftdba.com/2009/03/using-oracle-enterprise-manager-grid.html" target="_blank"&gt;Using Oracle Enterprise Manager (Grid Control) with PeopleSoft&lt;/a&gt;). Oracle added instrumentation to PeopleTools 8.50 (see &lt;a href="http://blog.psftdba.com/2010/11/peopletools-850-uses.html" target="_blank"&gt;PeopleTools 8.50 uses DBMS_APPLICATION_INFO to Identify Database Sessions&lt;/a&gt;) that sets module and action for on-line and batch sessions.&amp;nbsp; However, I still use my own &lt;a href="http://www.go-faster.co.uk/scripts.htm#psftapi.sql" target="_blank"&gt;trigger&lt;/a&gt; to set these attributes for processes initiated by the Process Scheduler.&lt;br /&gt;
&lt;br /&gt;
I originally became interested in module and action because it made it possible to analyse performance problems in specific processes with Active Session History (see &lt;a href="http://www.go-faster.co.uk/ukougpres.htm#Practical_ASH.ppt" target="_blank"&gt;Practical Use of ASH&lt;/a&gt;). However, since Oracle 10g there is also a very easy way to enable Oracle's extended session trace in the sessions relating to&amp;nbsp; specific processes.&lt;br /&gt;
&lt;br /&gt;
In the Oracle supplied PL/SQL package &lt;a href="http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_monitor.htm#i1003993" target="_blank"&gt;DBMS_MONITOR&lt;/a&gt;, there are two programs to specify combinations of module and action for which SQL trace will be enabled.&amp;nbsp; It is rather like setting a watchpoint in a debugger. &lt;a href="http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_monitor.htm#CFAGCHJC" target="_blank"&gt;SERV_MOD_ACT_TRACE_ENABLE&lt;/a&gt; is used to create a watchpoint, and &lt;a href="http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_monitor.htm#i1003676" target="_blank"&gt;SERV_MOD_ACT_TRACE_DISABLE&lt;/a&gt; removes it. Trace is then enabled in a session when the module and action is set to a value that matches a watchpoint, and disabled when the module and/or action is changed to a value for which there is no watchpoint.&lt;br /&gt;
&lt;br /&gt;
This screenshot from OEM shows that a query is being run within a PeopleSoft component RECV_PO, page PO_PICK_ORDERS.&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/pt850oem.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://www.go-faster.co.uk/images/pt850oem.jpg" width="100%" /&gt;&lt;/a&gt;&lt;/div&gt;
It would be a simple matter to have Oracle trace the session for that component by setting a watchpoint. &lt;br /&gt;
&lt;br /&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;BEGIN
 sys.dbms_monitor.serv_mod_act_trace_enable
 (service_name=&amp;gt;'P1PPP'
 ,module_name =&amp;gt;'RECV_PO'
&amp;nbsp;,waits=&amp;gt;TRUE
 ,binds=&amp;gt;TRUE);
END;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Note that the service name must be specified as part of the watchpoint.&amp;nbsp; This will usually be the same as the PeopleSoft database name.&amp;nbsp; Thus, watchpoints copied to another database by cloning won't be effective because the service name will be different.&lt;br /&gt;
&lt;br /&gt;
I could also have specified an action in the example above, but I want to trace all pages in the component and the search dialogue. &lt;br /&gt;
&lt;br /&gt;
As with the other programs in &lt;i&gt;dbms_monitor&lt;/i&gt;, information on wait events are included in traces by default.&amp;nbsp; In the above example, I have also requested information on bind variables to be included in the trace.&lt;br /&gt;
&lt;br /&gt;
When setting a SQL trace for an on-line component it is possible that many different users could trigger tracing.&amp;nbsp; Trace will be enabled and disabled for each user. The result is that you will get a trace file for each application server process, and each trace file might contain the activity for more than one user if the application server process handled more than one service request.&lt;br /&gt;
&lt;br /&gt;
Watchpoints can be removed in a similar way.&lt;br /&gt;
&lt;ol&gt;
&lt;/ol&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;BEGIN
 sys.dbms_monitor.serv_mod_act_trace_disable
 (service_name=&amp;gt;'P1PPP'
 ,module_name =&amp;gt;'RECV_PO'
 ,action_name =&amp;gt;'PO_PICK_ORDERS');
END;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Watchpoints are held the table SYS.WRI$_TRACING_ENABLED.&lt;br /&gt;
&lt;br /&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;SELECT * FROM sys.wri$_tracing_enabled
/

TRACE_TYPE PRIMARY_ID QUALIFIER_ID1 QUALIFIER_ID2   INSTANCE_NAME FLAGS
---------- ---------- ------------- --------------- ------------- -----
         5 P1PPP      RECV_PO       PO_PICK_ORDERS                   12
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
This technique is also effective for processes initiated by the Process Scheduler. The default behaviour from PeopleTools 8.50 is to set Action to the process name, my &lt;a href="http://www.go-faster.co.uk/scripts.htm#psftapi.sql" target="_blank"&gt;trigger&lt;/a&gt; sets Module to the process name.

&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://go-faster.co.uk/images/oemgridact.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://go-faster.co.uk/images/oemgridact.png" width="100%" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;
If I specify only the Module in SERV_MOD_ACT_TRACE_ENABLE, then it trace will be enabled for that module and for any action.&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;
&lt;/div&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;BEGIN
 sys.dbms_monitor.serv_mod_act_trace_disable
 (service_name=&amp;gt;'AAAAAAA'
 ,module_name =&amp;gt;'GPPDPRUN');
END;
/
&lt;/code&gt;&lt;/pre&gt;
I choose to set the action to the Process Instance number because I find it useful to relate ASH data to a specific batch process. The above screenshot from OEM shows a streamed Global Payroll 
calculation running with many concurrent GPPDPRUN processes. The 
watchpoint would cause each and every processes to trace. &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: left;"&gt;
&lt;/div&gt;
&lt;br /&gt;
However, it has been suggested that if the Action was set to the 
process run control ID then the watchpoint could be set to the specific
 run control value used by a user or scheduled job. It would be a simple matter of changing the &lt;a href="http://www.go-faster.co.uk/scripts.htm#psftapi.sql" target="_blank"&gt;trigger&lt;/a&gt; (I'll leave that as an excercise).&amp;nbsp; Then, a user could be told
 to use a specific value for run control ID that would then invoke trace.&lt;br /&gt;
&lt;pre style="background-color: #eeeeee; border: 0px solid #000000; font-family: courier new; font-size: 85%; overflow: auto; padding-left: 4px; padding-right: 4px; width: 95%;"&gt;&lt;code&gt;BEGIN
 sys.dbms_monitor.serv_mod_act_trace_disable
 (service_name=&amp;gt;'AAAAAAA'
 ,action_name =&amp;gt;'TRACEME');
END;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;i&gt;&lt;span style="font-size: xx-small;"&gt;Update 24.11.2012&lt;/span&gt; &lt;/i&gt;This technique will not work for &lt;a href="http://blog.psftdba.com/2012/11/peopletools-852-application-engine-sets.html"&gt;Application Engine from PeopleTools 8.52&lt;/a&gt; because it now sets MODULE to a value that includes a session number and so cannot be predicted.&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;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/o3T2gPp29cM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/o3T2gPp29cM/oracle-sql-tracing-by-module-action.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2012/09/oracle-sql-tracing-by-module-action.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-25740336.post-6280568969760370161</guid><pubDate>Thu, 19 Jul 2012 18:53:00 +0000</pubDate><atom:updated>2012-07-19T19:53:14.526+01:00</atom:updated><title>Gathering Aggregated Cost-Based Optimiser Statistics on Partitioned Objects</title><description>Recently, I have been looking into how to gather cost-based optimizer
 statistics on composite partitioned objects.&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
Database partitioning is not used by default in PeopleSoft because it is database specific.&amp;nbsp; Not all databases support partitioning, and where they do it is done in a platform specific manner.&amp;nbsp; Application Designer has no capability to create partitioned objects (although from PeopleTools 8.51 it will preserve partitioning in tables and index that are already partitioned in an Oracle database). It is therefore no surprise that it is not used widely by PeopleSoft customers.&amp;nbsp; However, partitioning is essential in Global Payroll systems that use 'streamed'&amp;nbsp; processing (many concurrent payroll processes).&amp;nbsp; By definition, these are the payroll systems that have large volumes of data where collecting statistics on the results tables can quickly become a challenge.&amp;nbsp; Partitioning can also have application in General Ledger reporting in Financials, particularly where nVision is in use.&lt;br /&gt;
&lt;br /&gt;
It is important that statistics on partitioned tables are both up to date and accurate.&amp;nbsp; However, collecting global 
statistics on a large partitioned object can be a time-consuming and 
resource intensive business as Oracle samples all the physical partitions
 or sub-partitions. Briefly, if you do not collect global statistics on a
 partitioned table, Oracle will aggegrate the statistics on the physical
 partitons or sub-partitions to calculate statistics on the logical 
table and partition segments.&lt;br /&gt;
&lt;br /&gt;
Oracle 10g makes a number of mistakes in its calculation of these 
aggregated statistics.&amp;nbsp; In particular the number of distinct values on 
columns by which the table is partitioned have impossibly low values.&amp;nbsp; 
This is can affect cardinality calculations and so lead the optimizer to choose the wrong execution plan.&lt;br /&gt;
&lt;br /&gt;
I have now published the second of two documents on my &lt;a href="http://www.go-faster.co.uk/docs.htm#Partition.Statistics.10g" target="_blank"&gt;website&lt;/a&gt;
 that examine aspects of statistics on partitioned, and in particular 
composite partitioned tables. The first document examines the problems 
in 10g, and proposes a procedure to 'correct' the aggregated statistics 
to at least minimum possible values.&lt;br /&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.go-faster.co.uk/Partition.Statistics.10g.v1.00.pdf" target="_blank"&gt;Gathering Aggregated Cost-Based Optimiser Statistics on Partitioned Objects in Oracle 10g&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
The second document looks at the same issue in 11g and shows that while most of the issues are fixed, one problem remains. &lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.go-faster.co.uk/Partition.Statistics.11gR2.pdf" target="_blank"&gt;Gathering Aggregated Cost-Based Optimiser Statistics on Partitioned Objects in Oracle 11gR2&lt;/a&gt;
&lt;/li&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;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ThePeoplesoftDbaBlog/~4/0Pl2y0yHzmA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ThePeoplesoftDbaBlog/~3/0Pl2y0yHzmA/gathering-aggregated-cost-based.html</link><author>noreply@blogger.com (David Kurtz)</author><thr:total>0</thr:total><feedburner:origLink>http://blog.psftdba.com/2012/07/gathering-aggregated-cost-based.html</feedburner:origLink></item><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;/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>2012-11-24T12:49:16.039Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">Process Scheduler</category><title>More Process Priority Levels for the Process Scheduler</title><description>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
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;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 border="0" src="http://www.go-faster.co.uk/images/prcspriorty_xlats.jpg" width="100%" /&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 border="0" src="http://www.go-faster.co.uk/images/prcsdefn_priority.jpg" width="100%" /&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 alt="" border="0" src="http://www.go-faster.co.uk/images/category_admin.jpg" title="" width="100%" /&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 border="0" src="http://www.go-faster.co.uk/images/serverdefn_priority.jpg" width="100%" /&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 120
COLUMN 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 a18
COLUMN prcsname FORMAT a12
COLUMN 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'&amp;nbsp;
&amp;nbsp;
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                            Offset
Control  PRCSNAME     RUNDTTM  Amount   DURATION
-------- ------------ -------- ------ ----------
1        AE_SLEEP1                  5        175
2        AE_SLEEP2                 10        175
3        AE_SLEEP3                 15        175
4        AE_SLEEP4                 20        175
5        AE_SLEEP5                 25        175
6        AE_SLEEP6                 30        175
7        AE_SLEEP7                 35        175
8        AE_SLEEP8                 40        175
9        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.*&amp;nbsp;
FROM (
   SELECT SYSDATE, q.prcsinstance, q.prcsname, q.prcsprty, q.rundttm
   , r.begindttm, r.enddttm, q.serverassign, q.runstatus
   --, r.prcscategory
   FROM &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; q
   WHERE r.prcsinstance = q.prcsinstanceAND r.prcsname like 'AE_SLEEP_'
   AND r.prcsinstance &amp;gt; (
      SELECT MAX(prcsinstance)
      FROM psprcsrqst
     &amp;nbsp;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;
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&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;/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;/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;/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;/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>2012-11-24T12:46:47.178Z</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="100%" /&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;/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;/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;/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;/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;/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;/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;/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;/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;/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>2012-11-24T12:46:09.200Z</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>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
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;Languaged, &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" src="http://www.go-faster.co.uk/images/psxprptdefn.png" width="100%" /&gt;&lt;/a&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" src="http://www.go-faster.co.uk/images/psxpdatsrc.png" width="100%" /&gt;&lt;/a&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 href="http://www.go-faster.co.uk/peopletools/psprcsrqst.htm" target="_blank"&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 href="http://www.go-faster.co.uk/peopletools/cdm_file_list.htm" target="_blank"&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 href="http://www.go-faster.co.uk/peopletools/psxprptdefn.htm" target="_blank"&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 href="http://www.go-faster.co.uk/peopletools/psxpdatsrc.htm" target="_blank"&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 &amp;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&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;/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>2</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;/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;/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>2012-11-24T12:51:06.703Z</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" src="http://www.go-faster.co.uk/images/gppdpruncntl.png" width="100%" /&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;/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;/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;/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></channel></rss>
