+ All Categories
Home > Documents > dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the...

dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the...

Date post: 22-Apr-2018
Category:
Upload: lamdan
View: 218 times
Download: 4 times
Share this document with a friend
63
Performance tips, guidelines and techniques Performance and Benchmark Group
Transcript
Page 1: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

Performance tips, guidelines and techniques

Performance and Benchmark Group

Page 2: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

Table of Contents1. INTRODUCTION.......................................................................................................................................4

1.1 DOCUMENT PURPOSE..........................................................................................................4

2. PERFORMANCE CONSIDERATIONS WHEN USING APPLICATION CLASSES.................................5

2.1 INHERITANCE LEVELS...........................................................................................................52.1.1.1 Suggested Alternative(s)..........................................................................5

2.2 AVOID UN-NECESSARY OBJECT INSTANTIATIONS.....................................................................52.2.1.1 Suggested Alternative(s)..........................................................................5

2.3 USE OF GLOBAL VARIABLES AND COMPONENT VARIABLES WITHIN APPLICATION CLASSES.........52.4 SINGLETONS........................................................................................................................ 6

3. PEOPLECODE PERFORMANCE TIPS...................................................................................................7

3.1 GENERAL PERFORMANCE ISSUES.........................................................................................73.2 PERFORMANCE TIPS............................................................................................................7

3.2.1 SOME EXAMPLES.................................................................................................................10

4. ONLINE CODING TECHNIQUES FOR PERFORMANCE.....................................................................15

4.1 COMPONENT/PAGE/FIELD CODING GUIDELINES...................................................................154.1.1 Place PeopleCode At The Right Level (Component, Page or Field level?)...........................154.1.2 Consolidate PeopleCode To One Field..................................................................................154.1.3 Use Rowsets To Store Commonly Shared Or Frequently Used Data....................................154.1.4 Do Not Invoke AutoSelect For Loading Rows Into A Grid Or Scroll Area...........................164.1.5 Manually Limit The Number Of Rows Selected Into A Grid Or Scroll Area – Chunking......174.1.6 Limit Use Of Related Display Fields In Grids.......................................................................184.1.7 Select Data Onto Pages Of A Component As Needed............................................................19

4.2 ONLINE PEOPLECODE CODING GUIDELINES........................................................................194.2.1 Store and Reuse Common Functions......................................................................................194.2.2 Load And Execute Functions Only When Necessary.............................................................194.2.3 Use Component Variables To Enhance Sharing Of Data......................................................204.2.4 Use Arrays To Store Frequently Accessed Data....................................................................204.2.5 Use Search Arrays To Access Data In Arrays........................................................................214.2.6 Use Search Arrays To Access Data In Rowsets.....................................................................224.2.7 Properly Declare Methods In Application Classes................................................................22

4.3 ONLINE SQL CODING GUIDELINES......................................................................................224.3.1 Store And Reuse Common SQL Statements............................................................................224.3.2 Execute SQL Statements Only When Necessary.....................................................................224.3.3 Decrease SQL Statements Executed In A Loop......................................................................234.3.4 Check For Value Of “NEXT” During ADD Transaction......................................................244.3.5 Use Views With Care..............................................................................................................244.3.6 Close SQL objects after used..................................................................................................24

4.4 GENERAL RULE OF THUMB FOR CODING ONLINE TRANSACTIONS........................................244.4.1 Minimize Server Trips............................................................................................................25

5. TOP 10 BEST PRACTICES FOR BATCH PROGRAM DESIGN...........................................................26

5.1 DO NOT ABUSE THE USAGE OF TEMPORARY TABLES.............................................................265.2 USE SET BASED PROCESSING AS MUCH AS POSSIBLE...........................................................265.3 AVOID USING PEOPLECODE IN A LOOP................................................................................275.4 MINIMIZE DURATION OF LOCK ON HOT SPOT TABLES.............................................................275.5 DESIGN TO SUPPORT PARALLEL PROCESSING......................................................................275.6 AVOID COMPLEX SQL'S (SQL’S WITH MORE THAN 4 NESTED SUB QUERIES, SQL WITH MORE THAN 5 TABLE JOINS)................................................................................................................... 28

Page 3: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

5.7 COMMIT FREQUENTLY (BUT NOT AFTER EACH STATEMENT)...................................................285.8 ADD RESTART LOGIC IF UNIT OF WORK CAN BE LARGE..........................................................285.9 SHARING CODE BY BATCH AND ONLINE................................................................................285.10 USE PARAMETER MARKERS FOR SQL'S THAT IS BEING EXECUTED IN A LOOP TO AVOID DATABASE COMPILATION TIME......................................................................................................28

6. APPENDIX A..........................................................................................................................................29

6.1 INHERITANCE LEVELS TESTS...............................................................................................296.2 OBJECT INSTANTIATION ANALYSIS.......................................................................................29

6.2.1.1 Suggested Alternative............................................................................307. APPENDIX B – PEOPLECODE FAQ’S.................................................................................................31

8. APPENDIX C - PEOPLECODE APPLICATION CLASSES – DESIGN AND PROGRAMMING BEST PRACTICES................................................................................................................................................... 34

8.1 OVERVIEW......................................................................................................................... 348.2 WHAT DO APP CLASSES BRING TO THE TABLE?....................................................................348.3 WHY DECOUPLE CODE FROM THE GUI?..............................................................................348.4 WHAT IS GUI-SPECIFIC CODE?...........................................................................................358.5 HOW DO APP CLASSES INCREASE MAINTAINABILITY?............................................................358.6 ABSTRACT BASE CLASSES..................................................................................................368.7 GENERIC BASE CLASSES....................................................................................................368.8 UTILITY CLASSES...............................................................................................................378.9 APP CLASS STRUCTURE......................................................................................................378.10 IMPORTS....................................................................................................................... 378.11 CLASS DECLARATION.....................................................................................................388.12 PRIVATE INTERFACE......................................................................................................388.13 PROPERTIES..................................................................................................................388.14 INSTANCE VARIABLES.....................................................................................................398.15 CONSTANTS AND READONLY PROPERTIES.......................................................................398.16 METHOD PARAMETERS...................................................................................................408.17 OTHER VARIABLES.........................................................................................................428.18 EXCEPTIONS..................................................................................................................428.19 DOCUMENTATION CONVENTIONS.....................................................................................438.20 METHOD AND CLASS HEADER COMMENTS........................................................................448.21 Summary..................................................................................................................... 45

Page 4: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

1. Introduction

1.1 Document PurposeThe intent of this document is outline PeopleCode application classes design and programming best practices, key performance considerations when using application classes, general PeopleCode performance tips and online coding techniques.

Page 5: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

2. Performance Considerations when using application classes

2.1 Inheritance LevelsAn application class hierarchy or inheritance tree can be as deep as needed. There are no limitations as such on the levels of inheritance.

However, depending on the inhertiance level, instantiating a subclass is resource intensive and time consuming. The cost for instantiating a subclass grows higher as the inheritance hierarchy goes deeper (See Appendix A Section 4.1).

2.1.1.1 Suggested Alternative(s) Assess the functionality in the base classes and determine if there is scope for

flattening the inheritance hierarchy. Avoid too many levels of inheritance, as class construction is expensive.

Use composition instead of inheritance. Instead of extending an existing class, give your new class a private field that references an instance of the existing class. The calls to the methods in this new class are delegated to corresponding methods in the contained instance.

Inheritance is appropriate only when you have an “is-a” relationship between two classes.

2.2 Avoid un-necessary object instantiationsPrograms that create many objects potentially have performance implications due extensive resource utilization (CPU, memory etc). 2.2.1.1 Suggested Alternative(s)

Design before code: Follow basic object oriented design principles to design application specific class hierarchy (encapsulation, what is a class versus what class behavior is). See Appendix A Section 4.2 on some observations for a transaction. Since the seeds of many performance issues are sown at design time, it pays to be aware of the potential performance impact of certain design decisions.

2.3 Use of global variables and component variables within application classes

Peoplecode allows you to declare global variables in application class code (below end-class, alongside method body declarations). From a purist standpoint this breaks encapsulation however; this can be leveraged to implement singletons across user

Page 6: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

sessions. One classic example where this technique can be beneficial is in the area where:

There is a Constants class that contains constant values that can be used across entire application. So instead of having all objects that need a reference to a Constant instance instantiate this class, one could create a BaseObject that all classes can inherit off and have the Constant class declared as a global variable in the BaseObject. Even though the globals variables traverse between the web and application server for every request the overhead is close to minimal due to the size of this Constant object compared to instantiating a Constant class every time.

Do not use globals to store large datasets. This data gets carried back and forth during server trips and adds to the serialization cost and becomes resource intensive.Also if you plan to use globals, make sure the stale data is flushed periodically

Use component variables declared in application classes to share common data between application classes which are invoked during a single component transaction. For example, if there is a string value that needs to be passed between 2 applications classes, then a string variable would be declared in both application classes:

Component string &c_share_string;

This declaration must appear at the very top of the application class.

To fill the string variable or to query the string variable, each of the application classes would contain PeopleCode such as:

If &c_share_string = null Then &c_share_string = Create share_string();Else /* do nothing*/End-If;

&local_string = &share_string;

2.4 SingletonsThe singleton pattern i.e. a single instance of a class per app. serv. process cannot be implemented using pplcd/application class constructs available. However, a user session based singletons can be accomplished via use of global member variables in an application class. This is only true for frequently used lightweight objects like Constants, common shared Utilities since these global objects traverse between the web and application server. This prevents un-necessary object instantiations.

Use funclibs to return instances over creating wrapper objects to return the instance.

Page 7: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

3. PeopleCode Performance TipsThis section serves as a resource in writing good PeopleCode programs that perform well. PeopleCode has a long history and has evolved quite a long way from a basic scripting language (see PeopleCode 1.0 Specifications). PeopleCode consists of a compiler that compiles code for the PeopleCode "virtual machine" and an evaluator that interprets this intermediate code. As an interpreted language it can never compete with languages that have sophisticated compilers that produce optimized code that runs on the native machine.

3.1 General Performance IssuesPeopleCode is intended as an auxiliary programming language and is not meant to supplant definitional approaches to application development. Computationally intensive programming in PeopleCode is guaranteed to perform poorly. Choose the right tool for the job!Algorithm design is always an important issue. Algorithms that do not scale well (sometimes these are referred to as "n squared" meaning that the algorithm's cost does not scale linearly with the size of the problem) will perform poorly in any language and will perform very poorly in PeopleCode.

3.2 Performance Tips 1. Auto-declared variables: One of the conveniences of PeopleCode is that you do

not have to declare your variables before you use them. The variable becomes a chameleon taking on the type of the value it is assigned. This convenience has a price in that you lose type checking at compile time, which can lead to problems at run time.

Example: When you validate or save your PeopleCode in the designer take notice of the auto-declared messages. Ask yourself if that is what you intended?

2. Precise is good: Declaring variable types specifically. This is a follow on of the previous point. Most of the time we know what types the variables we use should be so we should declare them of that type to begin with.

Example: If you know that a particular variable is going to be an Integer value why not declare it to be of that type in the first place. In this particular case you can get much better runtime performance. It is particularly effective for loop control variables (for &i ...) but has limited range (9-10 digits max), so must be used judiciously

Local Integer &Index;For &Index = 1 to &Limit;

3. We all need some good references: Be aware of and perhaps take advantage of reference parameters. In PeopleCode functions calls, parameters are passed by

Page 8: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

reference. This means a reference to the value is passed instead of the value itself. In the following code the function foo changes the value of &Str after the call. This can be a blessing and a curse.

Function Foo(&Par as String)

&Par = "Surprise";end-function;

Local String &Str = "Hello";Foo(&Str);/* now &Str has the value "surprise" */

4. Give me a break in the evaluate statement: The evaluate statement semantics are that the when clauses continue to be evaluated until the end of the evaluate statement or a "break" is encountered. If you have an evaluate statement with a number of when clauses and you only expect one of them to match, make sure you put a break statement there. Otherwise all the subsequent when clauses will continue to be evaluated. Note that your program is still correct. It is just that it is very inefficient at run time, particularly if you have a large number of when clauses and this statement is in a loop.

5. Size matters - when it comes to your state: One of the keystones in the PIA architecture is that the app server is stateless. What this means is that when required, the state of your session is bundled up and exchanged between the appserver and the web server. For example on a user interaction (UI) the whole state, including your PeopleCode state, has to be serialized to the web server and then once the UI has completed, that state is de-serialized in the app server so that your application can continue. What can you do about this? A couple of things. First, by being aware of this you can watch the size of PeopleCode objects you create (strings, arrays etc.) to make sure they are only as big as you need them to be. Second, if you are going to have some UI, be aware that all your PeopleCode state will need to be serialized across that interaction. You might be able to change the logic of your program to minimize the state. For example if you were building up a large string (a couple of Megabytes) and then doing some UI, you might be able to change your program logic to build the string after the UI.

6. Say it once: The PeopleCode compiler is not an optimizing compiler unlike some current compilers for languages like C++. For example it does not do common sub-expression analysis. So sometimes if you have a complicated bit of PeopleCode that is used a lot you can isolate the common expression yourself. This can make your code look cleaner and make your code faster especially it it’s in a loop. In the example below notice how the common sub expression is broken out.

Example: /*---- For this customer, setup time on B is influenced by

Page 9: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

*---- the machine flavors of A. */ &r_machine = &rs(&idB).GetRecord(Record.MACHINE_INFO); If (&typeA = "F") And (&typeB == "U") Then &r_machine.SETUP_TIME.Value = 50; Else &r_machine.SETUP_TIME.Value = 10; End-If;

7. Think about what is really going on: can you spot the implicit conversions? The most common ones are those going from a character string to a number and vice versa. Now you might be able to do nothing about this but by being aware of it you might be able to spot opportunities for performance improvement. In the code below two character strings are converted into numeric values before the difference is taken. Now if this code was in a loop, and one of the values did not change it would be a significant performance gain to do the conversion once as the second statements illustrate.

&Diff = &R1.QE_EMPLID.Value - &R2.QE_EMPID.Value;

&Original = &R1.QE_EMPLID.Value;. . .

&Diff = &Original - &R2.QE_EMPID.Value;

8. Choosing the right sequel style: In certain cases the one-shot SQLExec is what you want to do. In other cases your SQL could benefit greatly from being executed through a SQL object instead. This particularly applies if you can plan to execute the statement more than once with different bind parameters. The performance gain comes from compiling the statement once and executing it many times. SQL objects also have the ReuseCursor property, which can be used for further performance. More detail can be found in PeopleBooks.

9. What is happening inside that loop? It always is worthwhile to examine loops in your code to see what is happening inside the loop. The key thing is not to have code in the loop that doesn’t need to be in the loop. For example if you are working with File objects and your file layout does not change there is no reason to set the file layout every time you go through the loop reading lines from the file. Set the file layout once, outside the loop.

10. Are you done with that? Once you are done with an object reference, especially a global or component one, assign Null to it to get rid of the object. This allows the runtime to cleanup unused objects reducing the size of your PeopleCode state.

11. Application class specific suggestions:Simple properties (without get/set) are much more efficient than method calls. Be clear in your design what need to be simple properties, properties with get/set, and methods. Never make something a method that really should be a property.Analyze your use of properties implemented with get/set. While PeopleCode properties are in a sense "first class" properties with more flexibility in that you

Page 10: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

can run PeopleCode to actually get and set their values, make sure you actually need get and set methods. If all you have is a normal property which is more of an instance variable then avoid get/set methods. In the example below (without the strikethrough!) by having get/set for the property SomeString you have made it much more inefficient to get/set that property since every property reference has to run some PeopleCode. Often this can creep in when properties are designed to be flexible at the beginning and never subsequently analyzed for whether getters/setters were really needed after all.

class Test...property String SomeString get set;

end-class;

get SomeStringreturn &SomeString;end-get;

set SomeString&SomeString = &NewValue;end-set;

3.2.1 SOME EXAMPLES1. Beware of the Rowset fill method aka "What not to do in an Application Engine

PeopleCode step". Sometimes the algorithm is the issue. Below you'll see a real live PeopleCode program that adopts the approach: read all the data in one gulp into a rowset, process it row by row and update as necessary. This is a bad approach in general since you might run out of memory if the rowset if too large and you lose the general advantage of set-based programming.

Local Rowset &RS;Local Record &REC;Local SQL &SQL_UPDATE;

&REC_NAME1 = "Record." | SOME_AET.SOME_TMP;&RS = CreateRowset(@(&REC_NAME1));&LINE_NO = 1;

&NUM_ROWS = &RS.Fill("WHERE PROCESS_INSTANCE = :1 AND BUSINESS_UNIT = :2 AND TRANSACTION_GROUP = :3 AND ADJUST_TYPE = :4 ", SOME_AET.PROCESS_INSTANCE, SOME_AET.BUSINESS_UNIT, SOME_AET.TRANSACTION_GROUP, SOME_AET.ADJUST_TYPE);

For &I = 1 To &NUM_ROWS &REC = &RS(&I).GetRecord(@(&REC_NAME1)); &REC.SOME_FIELD.Value = &LINE_NO; &REC.Update(); &LINE_NO = &LINE_NO + 2;

Page 11: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

End-For;

Notice the problems:You might run out of memory in the fill method if the select potentially selects a large amount of data.The Fill is selecting all the columns in the table when all that's being updated is one column.

This could be changed to read in the data one row at a time using a SQL object or using a similar algorithm but chunk the rowsets into manageable size through use of an appropriate where clause.

There are some rough numbers you can use to see how big the rowset would be: the overhead for a field buffer (in tools 8x) independent of any field data is ~88 bytes; the overhead for a record buffer is ~44 bytes; the overhead for a row is ~26bytes. So for a rowset with just one record/row the general rough formula would be:

mem = nrows * (row overhead + nrecords * ( rec overhead + nfields * ( field overhead) + average cummulative fielddata for all fields))

Plug in your numbers and you'll get a sense of how big your rowset might be.

2. Some examples that illustrate how to change your code to "say it once"

BEFORE:Local Rowset &RS_Level2;

Local Boolean &TrueOrFalse = (PSU_TASK_RSRC.COMPLETED_FLAG.Value = "N");

For &I = 1 To &RS_Level2.ActiveRowCount &RS_Level2(&I).PSU_TASK_EFFORT.EFFORT_DT.Enabled = &TrueOrFalse; &RS_Level2(&I).PSU_TASK_EFFORT.EFFORT_AMT.Enabled = &TrueOrFalse; &RS_Level2(&I).PSU_TASK_EFFORT.CHARGE_BACK.Enabled = &TrueOrFalse;End-For;

AFTER:Local Boolean &TrueOrFalse = (PSU_TASK_RSRC.COMPLETED_FLAG.Value = "N");

For &I = 1 To &RS_Level2.ActiveRowCount Local Record &TaskEffort = &RS_Level2(&I).PSU_TASK_EFFORT;

&TaskEffort.EFFORT_DT.Enabled = &TrueOrFalse; &TaskEffort.EFFORT_AMT.Enabled = &TrueOrFalse; &TaskEffort.CHARGE_BACK.Enabled = &TrueOrFalse;

Page 12: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

End-For;

- saves a simple evaluation from happening three times ( &RS_Level2(&I).PSU_TASK_EFFORT)- arguably cleaner looking code.

Another one.

BEFORE:Local Row &CurrentRow;&TrueOrFalse = (GetField().Value = "N");&CurrentRow = GetRow();For &I = 1 To &CurrentRow.ChildCount For &J = 1 To &CurrentRow.GetRowset(&I).ActiveRowCount For &K = 1 To &CurrentRow.GetRowset(&I).GetRow(&J).RecordCount For &L = 1 To &CurrentRow.GetRowset(&I).GetRow(&J).GetRecord(&K).FieldCount &CurrentRow.GetRowset(&I).GetRow(&J).GetRecord(&K).GetField(&L).Enabled = &TrueOrFalse; End-For; End-For; End-For;End-For;

AFTER:Local Row &CurrentRow;Local integer &I, &J, &K, &L;

Local boolean &TrueOrFalse = (GetField().Value = "N");&CurrentRow = GetRow();For &i = 1 To &CurrentRow.ChildCount /* No specific RowSet, Record, or Field is mentioned! */ Local Rowset &ThisRs = &CurrentRow.GetRowset(&i);

For &J = 1 To &ThisRs.ActiveRowCount Local Row &ThisRow = &ThisRs(&J);

For &K = 1 To &ThisRow.RecordCount Local Record &ThisRec = &ThisRow.GetRecord(&K);

For &L = 1 To &ThisRec.FieldCount &ThisRec.GetField(&L).Enabled = &TrueOrFalse; End-For; End-For; End-For;End-For;

- shorthand used: &ThisRs(&J) is same as &ThisRs.GetRow(&J)

Page 13: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

- eliminate auto-declared messages by declaring all local variables (I think it is good practice to declare all your variables. You get better logic, better compile time checking and potentially better run-time performance) I would say that "Ideally, you should not have any auto-declared messages in the ide window when you validate your PeopleCode".- notice the Integer: if you know your variables will fit in an integer (or float) declare them that way. Runtime performance for Integer is way better than regular old Number.- same as above: arguably cleaner less cluttered code.- for a large rowset the performance of this would be way better due to the way fewer redundant expression evaluations.

3. Concatenating a large number of strings into a large string. Sometimes you need to do this. The simplest approach is to do something like:

&NewString = &NewString | &NewPiece;

In itself this is not a bad approach but you can do this much more efficiently using an application class below in Tools 843 and above. Changes in the Tools' runtime representation of strings combined with the C++ efficiency of the array join method make for a very fast concatenation.

Special note: in tools 844-112a+ the concatenation can get 8 times faster with the addition of the line in red below. In some sample string intensive work that difference between standard concatenation for a growing string and use of this class showed a 40 fold improvement.

class StringBuffer method StringBuffer(&InitialValue As string); method Append(&New As string) returns StringBuffer; // allows &X.Append("this").Append("that").Append("and this") method Reset(); property string Value get set; property integer Length readonly; property integer MaxLength;private instance array of string &Pieces;end-class;

method StringBuffer /+ &InitialValue as String, +/

&Pieces = CreateArray(&InitialValue); &MaxLength = 2147483647; // default maximum size &Length = Len(&InitialValue);end-method;

method Reset &Pieces.Len = 0; &Length = 0;

Page 14: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

end-method;

method Append /+ &New as String +/ Local integer &TempLength = &Length + Len(&New); If &Length > &MaxLength Then throw CreateException(0, 0, "Maximum size of StringBuffer exceeded(" | &MaxLength | ")"); End-If; &Length = &TempLength; &Pieces.Push(&New); return %This;end-method;

get Value /+ Returns String +/ Local string &Temp = &Pieces.Join("", "", "", &Length); /* collapse array now */ &Pieces.Len = 1; &Pieces[1] = &Temp; /* start out with this combo string */ Return &Temp;end-get;

set Value /+ &NewValue as String +/ /* Ditch our current value */ &Pieces.Len = 1; &Pieces[1] = &NewValue; /* start out with this string */ &Length = Len(&NewValue);end-set;

Use the above as follows:

Local StringBuffer &S = create StringBuffer("");

....&S.Append(&line); /* to get the value of string simply use &S.Value */

Page 15: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

4. Online coding techniques for performance

4.1 Component/Page/Field Coding Guidelines

4.1.1 Place PeopleCode At The Right Level (Component, Page or Field level?)

In general, the execution of PeopleCode functions should not be unnecessarily repeated. This can be achieved by associating the PeopleCode function with the correct level object( i.e. the field or page). For example, if the purpose of the function is to retrieve data based upon an order number that is unique for the component, then the function should only be executed for the component, not for every row in a grid, which carries the same order number. Therefore, the SQL to extract the data from the database should be invoked by PeopleCode at the component level, versus the field level, for example in record level RowInit.

4.1.2 Consolidate PeopleCode To One Field.There are multiple PeopleCode functions, which occur at the record level and are associated to a field on the record. These include RowInit, RowInsert, RowDelete, SaveEdit, SavePreChg, SavePostChg, and Workflow. Although associated with a field, these functions are logically connected to the record as a whole. In other words, they are not dependent on the field associated with the function. These functions are executed for each row activated for the record, such as when rows are selected into a grid or scroll area based upon some search keys.

Performance can be improved if, rather than spreading these functions between multiple fields, they are consolidated and associated with one field. This prevents the unnecessary execution of PeopleCode functions and the savings can be significant if the record is defined for a grid or scroll area.

4.1.3 Use Rowsets To Store Commonly Shared Or Frequently Used Data.There are occasions in an application where it is necessary to refer to the same data multiple times, but the data need not be displayed on a page. If this data is being retrieved from the database by a SELECT statement, then it would be more efficient to store the data in memory for later reference, than to keep selecting it from the database.

There are options on how and where this data can be stored. The first is to create a work record, place the record as a hidden record on the page, and then fetch the data into the record. The second option is to use component level standalone rowsets. Both options place this data in memory.

Depending on the size of the data, it is more efficient to store the data in a component level standalone rowset, rather than a work record. If the data were stored on a work record, this record would need to be included on each of the pages needing to use the

Page 16: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

data. With a component level standalone rowset, it needs only to be declared within each application that needs to refer to the data.

4.1.4 Do Not Invoke AutoSelect For Loading Rows Into A Grid Or Scroll Area.

By default, the selection of rows of data into a grid or scroll area is executed automatically upon component load. In most instances, however, the data does not have to be selected into the grid or scroll area until the user is ready to view that data. For example, if the grid of data is on a secondary page for which the user must choose a hyperlink or button from the primary page in order to view, then loading the data during the initial component load is unnecessary and degrades performance. Performance can be further degraded if there are PeopleCode functions attached to any of the fields on the grid, such as RowInit, which are automatically executed for every row selected into the grid. In addition, if the same component can be executed in either the Insert and Update mode, having a grid or scroll without the NoAutoSelect option turned on causes the automatic and unnecessary execution of the SQL to select data with invalid values for the search keys.

It is better for performance if the selection of data into the grid is handled manually by coding the select SQL. The parameter to invoke NoAutoSelect must also be turned on as a grid property. To find and turn on this grid property, do the following:

Bring up the page in App Designer and display the page layout.Place the cursor over the grid and do a right click on the mouse.The following popup screen will appear:

Page 17: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

Make sure that the No Auto Select option has been checked on.The SQL SELECT statement could be coded as part of the Page Activate function for the page containing the grid, or as FieldChange on the button on the primary page which brings the user to this page.

There are occasions when the automatic selection of data into a scroll or grid may be acceptable. For example, if the scroll or grid were on the primary page and the number of rows did not have to be controlled with “chunking”. See the item below to learn more about “chunking”

4.1.5 Manually Limit The Number Of Rows Selected Into A Grid Or Scroll Area – Chunking.

In those cases where the number of rows being Selected in a grid or scroll area could be large (100’s or even 1000’s), it may become necessary to manually control the number of rows to fetch with each Select to improve performance. We refer to this limitation of rows as “chunking”. To fetch 1000’s of rows into a grid based upon key values chosen by the user, and invoke numerous PeopleCode functions while preparing the grid, can result in excessive execution time and page time outs.

Page 18: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

In order to improve performance, the rows of data being fetched into the grid must be manually controlled into “chunks”. A preset fixed number of rows is fetched into the grid when the user chooses to view the data. There is an implied ORDER BY on the selection of data based upon the search key. The value of key of the last row in the “chunk” is then stored to be used as the starting point when retrieving the next “chunk” of data. The user is provided with the options to retrieve the NEXT or PREVIOUS “chunk” of rows via buttons on the page.

We have examples of SCM applications where this “chunking” approach has been used. If you would like more details on this, please contact the P&B Architecture Team.

4.1.6 Limit Use Of Related Display Fields In GridsWhen a Related Display Field is defined within a record, its value is derived based upon the value of a defined Control Field. Usually, the Related Display Field and the Control Field are defined on different records. The value of the Related Display field is retrieved during the population of data into a row of the record and is usually accomplished by a SQL SELECT statement using the value of the Control Field as part of the selection criteria.

When the Related Display Field is defined within a grid, there can be performance problems depending on the number of rows being fetched into the grid. This is because each row will invoke a SQL to be executed in order to retrieve the value for the Related Display Field.

If possible, the Related Display Field and Control Field should be defined as part of a view so that the retrieval of values for the Related Display Field can be accomplished by a single execution of SQL. For example, suppose your grid shows lines for a user specified order number and contains fields order_no and order_int_line_no, product_id from table ps_ord_line, and product_id is defined as the Control Field and you have the descr from ps_prod_item table defined as the Related Display Field. To fill the grid you would issue the following SQL:

SELECT ORDER_NO, ORDER_INT_LINE_NO, PRODUCT_ID

FROM PS_ORD_LINE WHERE ORDER_NO = :1

Then for every row selected you would issue the following SQL:

SELECT PRODUCT_ID, DESC FROM PS_PROD_ITEM WHERE PRODUCT_ID = :1

However, if you have all the fields in the grid defined within one view, you could then get the values of all the fields at one time during the filling of the grid:

SELECT A.ORDER_NO, A.ORDER_INT_LINE_NO, A.PRODUCT_ID, B.DESC

Page 19: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

FROM PS_ORD_LINE A, PS_PROD_ITEM BWHERE A.PRODUCT_ID = B.PRODUCT_IDAND A.ORDER_NO = :1

4.1.7 Select Data Onto Pages Of A Component As NeededIf the component is composed of multiple pages, and the user has a choice of navigation to these pages from a main page, then only select data to fill those pages based upon navigation by the user, rather than preselecting data for all the pages during component load. For example, let’s say that the component contains a main page of header type info which is the initial page presented to the user from the Search Page. From this “Main” page the user can traverse to a “Details” page, a “Notes“ page, and an “Attachments” page by clicking on page tabs or links. The initial component load would bring in data for just the “Main” page. The data for the other pages, for example the “Details” page, would be triggered by the page activate event of the user clicking on the page tab or link for the “Details” page.

However, if the user will always choose to navigate to the “Details” page from the “Main” page, then the data should be preselected for the “Details” page at component load time so that an extra trip to the database is not consistently incurred for the transaction. The other less frequently navigated pages, such as a “Notes” page, could still remain dependent on a page activation trigger.

4.2 Online PeopleCode Coding Guidelines

4.2.1 Store and Reuse Common Functions.Rather than repeatedly coding common routines and functions within multiple applications, these functions should be stored and shared via libraries created for each product called FUNCLIB. Since the code is stored once, it is easier to maintain and easier to predict performance from application to application.

In addition, calling a function from a common library when needed is better for performance than having the code tied to a field on a record since the cost of loading the program is avoided. This is especially effective if the function is conditionally executed; if code were attached to a field on a record, it would be loaded regardless of use.

4.2.2 Load And Execute Functions Only When Necessary.In some cases, the first step in a function is to check for a condition before continuing in the processing logic. If this condition check were executed prior to executing the function call, this would eliminate the unnecessary loading and execution of the function.

For example, if the first line of code in a function checks for the presence of value in a parameter being passed to that function by the calling function, the check should be

Page 20: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

moved to the calling function and executed as a condition of executing the called function. In the cases where the condition is false, the function will not be executed.

4.2.3 Use Component Variables To Enhance Sharing Of DataUser component variables declared in application classes to share common data between application classes which are invoked during a single component transaction. For example, if there is a string value that needs to be passed between 2 applications classes, then a string variable would be declared in both application classes:

Component string &c_share_string;

This declaration must appear at the very top of the application class.

To fill the string variable or to query the string variable, each of the application classes would contain PeopleCode such as:

If &c_share_string = null Then &c_share_string = Create share_string();Else &local_string = &share_string;End-If;

Note: Global variables could also be used to share data between applications and would involve similar coding. However, since Global variables are “alive” for an entire session vs the component, greater care must be taken when using them.

4.2.4 Use Arrays To Store Frequently Accessed DataArrays can be used to store and search multiple rows of data in memory with less overhead than rowsets. Like rowsets, arrays can hold multiple rows of data in memory and have been recommended to store rows of data which are used multiple times during a transaction so that they do not have to be retrieved multiple times from the database. However, when you need to search for a particular row of data based upon a value of a column, with rowsets you must do this search within a LOOP through all the rows and comparing values. With an array, the FIND method is available so that you can go directly to the row without a LOOP.

For example, during the transaction, the type description field for an associated type id field must be derived to display for the user. If the type id field can be repeated in the grouping of data that you are processing, it would me more efficient to store off the description field in memory with the first retrieval for reuse, rather than retrieve it from the database each time. If stored in a rowset, to check if the description for the type id already exists in memory so that it can be reused, a LOOP must be executed through the rows of the rowset. With an array, we can search directly for the row by using the FIND method. In this example, we would need a 2-dimensional array; the first column would contain the type id and the second column would contain the text description. Shown

Page 21: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

below is the coding that is needed to do this at the application method or application class level depending on the level of visibility that is needed for this array:

Local Array of Array of String &descr_array;Local Number &aindex;

&aindex = &descr_array.Find(&typeid_tofind);If &aindex > 0 Then &descr_found = &descr_array[&aindex][2];Else *need to retrieve from database and store in array* End-If;

NOTE: USE OF ARRAYS AND ROWSETS IS RECOMMENDED TO STORE DATA THAT IS REPEATEDLY REFERENCED WITHIN A TRANSACTION. THEY STORE DATA IN MEMORY SO WE SHOULD BE AWARE THAT WE DON’T GO OVERBOARD IN THEIR USE SO THAT MEMORY USAGE BECOMES A PERFORMANCE CONCERN, ESPECIALLY WHEN THEY MAYBE 100’S OR 1000’S OF CONCURRENT USERS FOR THE TRANSACTION. AS A “RULE OF THUMB”, WE’VE BEEN USING THE GUIDELINE THAT IF THE DATA IS ACCESSED MORE THAN 5 TIMES WITHIN A SINGLE COMPONENT TRANSACTION, THEN IT SHOULD BE STORED IN EITHER A ROWSET OR ARRAY.

4.2.5 Use Search Arrays To Access Data In Arrays If, however, you have to search for a row of data based upon the value of multiple columns, even with an array, you will need to LOOP since the FIND method only allows for the specification of one column value. To resolve this issue, a “search” array can be used. This array will have one column containing a concatenated key value. The sequence of rows in the search array is kept in synch with that of the main array.

Using the example from above, if we needed to key by type id and type name, then a search array could be used. The search array would be a one dimensional array containing a column with the concatenated value of type id and type name. In this case, the main array would be expanded to another dimension:

Local Array of Array of Array of String &descr_array; Local Array of String &descr_srch;

LOCAL NUMBER &AINDEX;

&AINDEX = &DESCR_SRCH.FIND(&TYPEID_TYPENAME_TOFIND);

If &aindex > 0 Then &descr_found = &descr_array[&aindex][3]; Else *concatenate the type id and type name, push row into &descr_srch using concatenated value, push row into &descr_array*

Page 22: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

End-If;

4.2.6 Use Search Arrays To Access Data In RowsetsA search array could also be used to search rowsets quickly for the existence of a row. The rowset and search array would also need to be kept in synch. Using the example from item #7, here is how the rowset search could be coded:

Local rowset &descr_rowset;Local Array of String &descr_srch;Local Number &aindex;

&aindex = &descr_srch.Find(&typeid_typename_tofind);If &aindex > 0 Then &descr_found = &descr_rowset.GetRow(&aindex).DESCR.ValueElse*concatenate the type id and type name, push row into &descr_srch using concatenated value, insert row into &descr_rowset*End-If;

4.2.7 Properly Declare Methods In Application ClassesIf a method is only referenced within the Application Class where it is declared, insure that it has been declared as Private. If not, when the method is called, it will be considered an external call even if the call comes from within the Application Class where it was declared. External calls carry more overhead PeopleCode than internal calls which can make a negative impact on performance depending on the number of calls to the method within a transaction.

4.3 Online SQL Coding Guidelines

4.3.1 Store And Reuse Common SQL Statements.Along the same lines as common functions, common SQL statements should be stored as SQL objects and shared via the SQL repository. Once again, it is easier to maintain and easier to predict performance since multiple applications can all use the same SQL statement.

4.3.2 Execute SQL Statements Only When Necessary.Before executing SQL statements via SQLExec, check for the presence of value in all columns which are included in the WHERE clause if those columns are defined as NOT NULL. This will eliminate the execution of the SQL with NULL values for those columns, which will never return any result rows.

For example, in Sales Order Entry, order_no and business_unit are required fields in table ps_ord_header. To conditionally execute a SQL statement against the table from within a PeopleCode function, it could be coded as:

Page 23: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

If All(&ORDER_NO) And All(&OM_BU) ThenSQLExec("select distinct 'x' from ps_ord_header ord ord.business_unit = :1 and ord.order_no = :2 and ord.eff_status = 'A'", &BU, &ORDER_NO, &ORDEXISTS);End-If;

4.3.3 Decrease SQL Statements Executed In A Loop.In the situations where the results of one SQL are used as filter values fed to another SQL in a loop, the two SQL statements could be joined using an IN clause with a subquery. In this manner the final result set is achieved with 1 SQL statement versus several as in this example from ELM:

The first SQL -

SELECT GP.LM_LRNR_GROUP_ID FROM PS_LM_GROUP_PERSON GP, PS_LM_LRNR_GROUPS LG WHERE GP.LM_PERSON_ID=:1 AND LG.LM_LRNR_GROUP_ID = GP.LM_LRNR_GROUP_ID AND LG.LM_ACTIVE='Y'

The second SQL being executed in a loop for every value of LM_LRNR_GROUP_ID that resulted from the first SQL –

SELECT DISTINCT C.LM_CATG_ID, C.LM_CS_LONG_NM, C.LM_CS_DESCR, A.LM_DISP_ORDER FROM PS_LM_CATG_REL_TBL A, PS_LM_CATG_SEC_TBL B, PS_LM_CATG_TBL C WHERE A.LM_CHLD_CATG_ID=C.LM_CATG_ID AND A.LM_CHLD_CATG_ID=B.LM_CATG_ID AND C.LM_CS_STATUS='10' AND A.LM_CATG_ID=3 AND B.LM_LRNR_GROUP_ID= :1

These two SQL could be combined into:

SELECT DISTINCT C.LM_CATG_ID, C.LM_CS_LONG_NM, C.LM_CS_DESCR, A.LM_DISP_ORDER FROM PS_LM_CATG_REL_TBL A, PS_LM_CATG_SEC_TBL B, PS_LM_CATG_TBL C WHERE A.LM_CHLD_CATG_ID=C.LM_CATG_ID AND A.LM_CHLD_CATG_ID=B.LM_CATG_ID AND C.LM_CS_STATUS='10' AND A.LM_CATG_ID=3 AND B.LM_LRNR_GROUP_ID IN (SELECT GP.LM_LRNR_GROUP_ID FROM PS_LM_GROUP_PERSON GP, PS_LM_LRNR_GROUPS LG WHERE GP.LM_PERSON_ID= :1 AND LG.LM_LRNR_GROUP_ID = GP.LM_LRNR_GROUP_ID AND LG.LM_ACTIVE='Y')

Page 24: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

4.3.4 Check For Value Of “NEXT” During ADD Transaction.Many of the online transactions incorporate ADD and UPDATE in the same component. Many times, the first set of SQL executed during processing look for existing rows on various tables using values from the search page which are specified by the user.

In the case where the user chooses to automatically generate an id for the transaction, the first set of SQL are executed using the value of “NEXT” as one of the key field values in the WHERE clause. It is very rare that the value of “NEXT” is valid for key columns on tables. Therefore in most cases, if a check for the value of “NEXT” in an ADD transaction is executed prior to executing the SQL statement, it will eliminate the unnecessary execution of the SQL.

4.3.5 Use Views With Care.Many applications create views to “shield” other applications from their base tables or to give them an easy way to access just the data from the base tables that they need. In some cases, when an application needs data from multiple tables, this can lead to views on top of views joined with views. This can then cause performance problems depending on the complexity of the views and the number of tables involved. Many times, performance has been improved by just pointing the SQL directly to the tables versus having to go through views.

Also for views, try to avoid DISTINCT, GROUP BY, MAX, MIN in the view text since on some database platforms, these grouping functions will cause the view to materialize before any filtering is applied to the view. This results in all rows for the view to be selected from the database, then the filtering is done to reduce the number of result rows.

4.3.6 Close SQL objects after used.Close SQL objects after they have been used.If within a function more SQL statements are used since cursor for first SQL Object(say cursor A) is still in use,  a new cursor (say cursor B) is created automatically by PSAPPSRV.  This new cursor B is used for the subsequent SQL statements.  When the function completes the cursor A associated with first SQL object is closed.  The new cursor B created within the function is not closed because an explicit PeopleCode statement did not open it, instead it was opened by PSAPPSRV because the other cursor was still in use.  The new cursor B is closed when the destructor of CramTrans class is called.  Since the new cursor B is the only opened cursor and it is not cursor "#1", SAM closes the connection to the database. 

4.4 General Rule Of Thumb For Coding Online Transactions

Page 25: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

4.4.1 Minimize Server Trips A general rule for performance is to minimize the number of trips between the

webserver, application server, and database server. In addition to the coding tips given above there are other ways to save a trip to the server:

Use Deferred Processing for as much of your component processing as possible. Avoid coding to format fields such as for graying/ungraying and hiding/unhiding.

Page 26: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

5. Top 10 best practices for batch program design

5.1 Do not abuse the usage of temporary tablesA. Set an objective when using temporary tables:

To avoid to go back to base table to minimize contention: If this is your objective then you should identify all your data requirements from the base table, and retrieve all the needed columns into the temp tables. The process should never go back to the base table again for data.

To massage/manipulate data in intermediate stages: Combine the logic to reduce the amount of inserts, and the amount of temp tables needed. For example, if I need to compute the total cost (derived from Unit of cost x quantity bought) of an invoice then group all the invoices by payment type, instead of breaking this into 2 sqls requiring 2 temp tables, I should combine the 2 into one using one temporary table.

To simplify sql access: For example if I have a large effective dated table that being used almost everywhere in my application (i.e. PS_CURRENCY_CD). Every Sql statement that accesses this table need to have a (max(effdt) subselect, then it will make sense for me to create a copy of this table (using a temp table) with an end_effdt at the beginning of the program so that all my subsequents Sqls can use a BETWEEN clause to access this temp table instead of a subselect on max(effdt). Again the decision should be weighted against the benefit of writing simple sqls vs the cost of building this new temp table.

B. Decision of Delete vs Truncate: If the number of rows inserted is less than 3000 rows, it will bemore efficient to Delete than Truncate the tables.

C. Decision to update statistics of temp tables: Update statistics of temp tables should be done:

when there are rows inserted into the temp table after the last insert statement into the temp table if you have

multiple inserts into the same temp tableD. To identify temp tables that can be candidate for database Global temp tables.

Data in these tables cannot persist across COMMIT points

5.2 Use set based processing as much as possibleFor example, if I have to read data from a file, validate some of the input fields before inserting into a base table, I can do it in 2 ways:

Approach 1: Read the row into a rowset, call PeopleCode function to edit each required field (on error write it to a log file), and insert them into the base table. This approach is doing row-by row processing, and field-by-field editing.

Page 27: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

Approach 2: Read the row into a temp table. Write Sqls to edit one column at the time and write error to n error table. At then end, insert the rows that have no errors in the base table. All these actions can be done in a set based. Therefore, approach 2 will be better.

Note: Some basic calculations should be made here to estimate the workload of both approaches for each task:Assuming we are dealing with a file of 100,000 rows, and each row have 20 fields to be edited.

Read file into table or rowset:. This is done row by row for both approaches. Edit each of the 20 fields:

o Approach 1: 2,000,000 (100,000 x 20) edits o Approach 2: 20 edits since edits are done at the column level

Insert into base table:o Approach 1: 2,000,000 inserts using bulk inserto Approach 2: 1 insert of 100,000 lines.

5.3 Avoid using PeopleCode in a loopFor AE program, if the logic being executed in a PeopleCode loop can be replaced by SQL then do so.

5.4 Minimize duration of lock on hot spot tablesHot spot tables are tables with few rows that are being frequently updated by Batch, Online or both. One example of that is table being used to store a pre-defined sequence as Journal_id, voucher_id etc. Try to put a commit immediately after the update of the hot spot table or structure your logic so that the update will happen right before the commit.

5.5 Design to support parallel processing Check if run control is designed to support parallel processing. Check the predicates of DML statements to ensure that the WHERE

predicates are designed to use indexes to avoid lock contention and to allow parallel processing. The WHERE predicates should be designed in a way that you can efficiently partition the data for your process. For example if your process is designed to run in parallel for Business_unit, and journal id ranges then the WHERE predicates should have at least these 2 criteria. You should also include all the criteria specified in the run control into the WHERE predicates when accessing tables commonly shared among parallel processes.

Page 28: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

5.6 Avoid complex SQL's (SQL’s with more than 4 nested sub queries, sql with more than 5 table joins)

Most of these Sqls are complex because they have multiple logic embedded in. They are design to catch 2 or 3 different processing logics into one sql. The recommendation is to break them into 2 or 3 simpler ones.

5.7 Commit frequently (but not after each statement)

5.8 Add restart logic if unit of work can be large

5.9 Sharing code by batch and onlineWhen coding this type of program, you must weight the cost of doing certain logic in a set based vs row based carefully. The overhead incurred by implementing the set based approach may cause the program to run longer when processing 2-3 lines of data. If this is true then consider maintaining a different code for online.

5.10 Use parameter markers for Sql's that is being executed in a loop to avoid database compilation time.

Page 29: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

6. Appendix A

6.1 Inheritance Levels tests

Below are test results for instantiating 100,000 and 1000,000 CRM objects. Contact inherits from Individual, Individual inherits from BC, BC inherits from BO and BO inherits from BOAbstract

Objects Instantiated

100000 objects

1000000 objects

BOAbstract 8281 ms 90999 msBO 36781 ms 396932 msBC 41031 ms 413994 msIndividual 47734 ms 474634 msContact 61015 ms 604507 ms

6.2 Object instantiation analysisReviews conducted on the execution logic for a particular transaction (save) revealed that there were utility/helper classes created for performing just validations. For example, a saveedit operation on business object BC, creates a BCAddressValidation instance and calls Process operation to perform address validation. Hence an instance needs to be created evertime a validation needs to be performed. Other example includes saveedit on business object BO, creates a ContactMethodSaveValidation instance and calls Process operation.

Page 30: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

Point to note in the above digram: Individual saveedit calls BC.saveedit which in turn BO.saveedit since Individual inherits from BC and BC inherits from BO. So these validation helper classes get constructed again.

6.2.1.1 Suggested AlternativeThe validations should be encapulated as part of a business object. A possible approach is to refactor BC and creating a method validateAddress and have the logic encapsulated in BC as opposed to BCAddressValidation.Process call.Follow basic principle - A class name should be a noun as opposed to a verb. Verbs indicate they are operations/methods.

Page 31: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

7. Appendix B – PeopleCode FAQ’s

This section answers some of the common questions that arise in regard to PeopleCode. It also is a resource for those wanting some general information about how PeopleCode works.

#1: How long has PeopleCode been in use at PeopleSoft?

PeopleCode is PeopleSoft's propietary language that has been in development as a language for quite a while. You can see the change by consulting the first specification in PeopleCode 1.0 Specifications ( April 1989)and comparing it with PeopleCode 9.0 specifications (Feb 2003). What began as a simple scripting language has evolved into a fully functional object oriented language.

#2: Why do we use a proprietary language instead of one that is in the public domain or more generally well known?

The historical reason would be that at the time of PeopleCode 1.0 there were no obvious choices for a simple language that fit the criteria for data validation and transformation. The other reasons would include the ability to control the language and to protect the current investment in PeopleCode in most PeopleSoft applications.

#3: Where is PeopleCode used in PeopleSoft?

PeopleCode is used in many different contexts. It is primarily used as part of the event model for the component processor where PeopleCode programs are associated with a particular event such as Field Change. However PeopleCode is used in many different contexts ranging from PeopleCode steps in Application Engine programs, to enabling portal navigation in PIA. It has become an integral part of the application development toolkit.

#4: Why can't I format my PeopleCode programs the way I like them?

Because there is a desire for consistency in how PeopleCode programs look when they are editted in the ide. Besides you won't necessarily be working on that PeopleCode program forever anyway and most likely the next person won't necessarily share your formatting taste! Big brother/sister has decided for you.

#5. How are PeopleCode programs stored?

PeopleCode programs are stored as a tokenized source form in the database. What you see in the ide is not stored in the database. Rather the tokens that represent the PeopleCode program you see in the ide are stored as a text string in the database.

Page 32: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

#6. How are PeopleCode programs executed?

PeopleCode programs are never executed from the source form. The program is first compiled into an intermediate code for the PeopleCode virtual machine. This internal code is then executed by the PeopleCode evaluator. Your PeopleCode program is compiled the first time it is used. The compiled version of the PeopleCode program is never stored in the database but is stored in cache. Once a PeopleCode program has been compiled it will never be compiled again during the lifetime of the cache unless the program is changed or the cache changes.

#7. What is the PeopleCode virtual machine?

It is a simple stack machine with a limited set of operators that deal with operands on the stack. A comparison with the Java language might be helpful. Just as there is a source form of a Java program (ending in a .java suffix) stored in some file system there is a source form of a PeopleCode program stored in the database. However a Java program has to be compiled into a class file in order to be executed by the Java Virtual Machine (jvm). So too must a PeopleCode program first be compiled before it can be executed by the PeopleCode evaluator.

#8. Is the PeopleCode compiler an optimizing compiler?

No. For example it does not do common sub-expression evaluation. In order to write efficient PeopleCode programs consult PeopleCode Performance tips . However the runtime code only evaluates statements/expressions that are necessary. For example in evaluating a statement with some conditions such as "if (a=1 or b=2) . .", if a does equal 1 then "b=2" is not evaluated.

#9. Various urban legends I have encountered about PeopleCode . . . truth is sometimes stranger than fiction

PeopleCode is executed by somehow parsing the PeopleCode text everytime a program is executed.In executing an if-then-else statement even though the "then" section is only executed the "else" section is quickly scanned anyway.There is a performance penalty when you include long comments in your program.PeopleCode is a magic password for those participating in the upcoming revolution - just kidding.

#9. What's with component and global variables?

Component variables are variables whose scope is limited to the component. (prior to tools 842 component variables were shared between a component and a CI (component interface) that the component invoked. Post 842 this is no longer the case which means that you cannot use component variables to pass information between a component and a CI.) Global variables have a scope of a

Page 33: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

PeopleSoft session. That means you cannot use globals to pass information between sessions. More detail on this is available in PeopleBooks.

#10. What's the largest value that can be held in PeopleCode variables?

This depends on the representation used. For the base 10 decimal number which you get with a Number type declaration, I think the largest decimal number is 5,192,296,858,534,827,628,530,496,329,220,095. The largest float number (which you can declare using the Float type) is (using IEEE floating point which I think all of our systems (even OS/390?) use) is about 10**308, since we use "double precision" (64 bits).

Page 34: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

8. Appendix C - PeopleCode Application Classes – Design and Programming Best Practices

This section incorporates excerpts from a document written by Art Machado that discusses Best Practices for Programming with App Classes.

8.1 OverviewApp classes allow you to use object-oriented programming techniques in peoplecode applications. There are some tremendous potential benefits to this, but there is also an inherent development paradigm shift.

This section will explain the advantages of object oriented design and then describe some of the more significant design patterns that comprise the approach. Next, it will take a closer look at app class structure and syntax in particular, enumerating all the things you can do with app classes and then identifying best practices.

8.2 What do app classes bring to the table?While app classes in themselves can’t protect you from poor design decisions that undermine their potential benefits, the simple fact that they are non-visual components should lessen the tendency to tightly couple them with specific graphical interfaces or application flows.

The one high-level thing that app classes can facilitate is increased maintainability of code. The single most way in which app classes can help you achieve this is by being reusable, which centralizes code and reduces the codebase. A smaller codebase with succinct, specialized components is easier to maintain than a larger codebase with long, monolithic pieces of often redundant code.

8.3 Why decouple code from the GUI?A graphical interface, like a window or page, is typically only used once in any application. This may or may not imply that functionality that initializes that GUI or that occurs in events elicited by that GUI is equally unique. If it’s not unique, it’s a candidate for reuse.

And whereas it’s quite possible that a unique series of processes, or a uniquely configured process, gets initiated by a specific GUI, the individual processes that make up that series, or a single configurable process, may very well be reusable.

At the end of the day, very little intelligent code is inherently linked to a GUI. Instead, GUI-specific code usually performs mapping functions that assign the values of object properties to GUI controls and vice-versa, or performs simple invocations of business processes. Application flow is arguably GUI-specific; I would contend that it belongs in an app class that encapsulates and centralizes application flow.

Page 35: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

8.4 What is GUI-specific code?At the end of the day, very little intelligent code is inherently linked to a GUI. Instead, GUI-specific code usually performs mapping functions that assign the values of object properties to GUI controls and vice-versa, or performs simple invocations of business processes.

Application flow is arguably GUI-specific; I would contend that it belongs in an app class that encapsulates and centralizes application flow. If your application flow is decentralized behind sundry GUI controls, it’s difficult to manage or even understand.

At the end of the day, GUI-specific code should largely be just “glue”. Don’t underestimate the reusability of even compound business processes…For example, don’t assume that only page X will ever want to do high level transaction Y which in turn performs business process A, B, C, D, E, and F. If you encapsulate transaction Y into an app class method, it can potentially be reused. If you don’t, it can NEVER be reused.

8.5 How do app classes increase maintainability?App classes can revolutionize the maintainability of peopletools applications, but it requires a relatively significant paradigm shift from traditional peopletools programming.

You should use app classes because they’re the most conducive structure in peoplecode for reuse. If you’re modeling for reuse, you have to be able to envision how to make a component generic even though you probably have a very specific initial application in mind for that component.

If you can design components generically, than theoretically they can be used in multiple places. When you need to change business logic in a component, you only need to do it in one place. This can dramatically reduce the impact of changes (not to mention expedite the initial debugging of code).Since well-designed components have well-defined public interfaces, it should typically be easy to assess the impact of code changes on calling applications.

However, the benefits of application classes can be seriously undermined if programmers don’t adopt them universally. If it only partially centralizes functionality, you’re still forced to perform exhaustive impact analyses whenever changes occur.

And if app classes don’t follow good design principles internally, you may end up with the ironic situation of being afraid to touch private app class code because you can’t easily determine what the internal impact will be to the app class’s publicly exposed functionality.

One of the often unsung advantages of centralizing code into app classes is that the code becomes an easy target for automated documentation. If code is fragmented across GUI components (and redundantly so), it’s very difficult and tedious to document. By

Page 36: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

adopting some simple conventions in inline app class documentation, tools can generate detailed API docs, which make promoting reuse much easier. Otherwise, you run into the problem of having beautifully designed reusable components that nobody knows about, or that people opt not to use because they’re undocumented and accordingly arduous to use.

8.6 Abstract base classesAbstract base classes are “unfinished” classes…Classes that shouldn’t be instantiated by themselves. Instead, they are meant to be always subclassed by classes, which flesh out their details. The advantage of this is that the abstract base class can contain a lot of reusable logic that relies on a little bit of more specialized logic…Although it can’t do anything without the specialized code, it’s done its part of centralizing reusable code.

An example is a database service’s save() method which performs all the mechanics of calling generic validation routines, talking to the database, and error handling. All that’s missing the specific logic for mapping a given data class to a particular database table (or Row object), and perhaps some optional validation.

The abstract base class makes life easier for the subclass by defining an placeholder method for the specialized logic…All the subclass has to do is override that method and it will be called at the right time, complete with appropriate exception handling, etc.

Peoplecode app classes don’t currently support abstract classes explicitly, but all that means is that the compiler is not going to complain if you forget to implement an abstract (unimplemented) class. There are three solutions to this:

1. Just document your abstract base class well and assume that subclassing programmers will do their due diligence

2. Program a passive validation check into your base class…something like an isValid() method that an offline validation checker could call to make sure all abstract methods have been overridden.

3. Program an active validation check into your base class that blows up rudely at runtime if abstract methods haven’t been overridden (like throw an UnimplementedMethodException).

This final option is probably best because offending subclasses won’t get very far.

8.7 Generic base classesGeneric base classes are very similar to abstract base classes except that their functionality is self-contained and fully implemented.

Some “generic” base classes aren’t all that generic. They may never have more than two subclasses. But it still makes all the sense in the world to define them before cloning common code across to different classes. There is certainly overhead associated with

Page 37: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

inheritance, but it’s nominal compared to the maintainability benefits, and it’s somewhat mitigated by writing specialized classes with relatively small footprints.

Other base classes are truly generic in that they will be subclassed by almost all classes in your system. For example, all or most service classes should typically extend a service base class that provides infrastructure services like configurable application logging. It’s something that can be useful to every service, at least for debugging. Similarly, all or most data classes should typically extend a generic base class that allows you to plug in adapters to support sorting their instances with custom sort algorithms.

It’s often hard to imagine ahead of time what belongs in a generic base class, or even that a particular class should extend one. As you develop classes you should just always be on the lookout for potential reuse and always capitalize on these opportunities as soon as possible. It’s common to start with a single class, and then half-way through (or two months later) decide to break it into base and subclass because you’ve identified another potential implementation for much of its functionality.

8.8 Utility classesSome reusable code is the kind you typically don’t want to inherit you just want to use here and there. Utility classes serve this purpose. There’s actually a fine line between utility classes and services.

Utility classes are kind of like the junk drawer of app classes. Little string parsing, data type conversion, and formatting routines are all good candidates for utility classes. Anytime you code something that’s more than just a line or two, and that reeks of reusability, you should file it off in a utility class.

8.9 App class structureApp class structure and syntax is relatively rich in the sense that there is more than one way to do some basic things. There’s no right or wrong way to do anything you CAN do, but it’s better if development teams standardize on a single way of doing each thing.

I’ll point out these situations out as we discuss each aspect of app class syntax, and in each case advocate a preferred approach

8.10 ImportsImports aren’t just to allow you to refer to class names without fully-qualifying them. They’re required if you want to reference any other app classes. Fully-qualifying a classname is always optional. The syntax is

<package>:<subpackage>:<…>:<classname or wildcard>

Page 38: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

Although it’s easier to use wildcards when importing multiple classes from the same package, it’s preferable to individually import each class for two reasons:

1. By glancing at a class header you can immediately see what its specific dependencies are.

2. It will make it easier for documentation tools to determine what packages classes that are referenced in code come from. If you don’t explicitly import a class and then proceed to reference it without fully-qualifying it with it’s package name, there’s no way for a tool to do cool things like provide a hyperlink to the referenced class’s documentation.

8.11 Class declarationThe extends keyword allow you to specify another class which the current class inherits from. If you specify an ancestor for a class, you must also explicitly initialize %super with an instance of the superclass (actually, the App Designer validation checker will only require that you initialize %super…it doesn’t care with what, so theoretically you could set %super to a new instance of the current class you are defining and start a hard infinite loop…but this isn’t advised).

8.12 Private InterfaceThe private interface of a peoplecode app class may include constants, instance variables, and methods. You specify where the public interface ends and the private interface begins with the keyword ‘private.’

The private interface is all the gory details that the outside world doesn’t need to know about. In particular, private methods are a way of breaking up the business logic in the app class into manageable, reusable parts. The internal structure of app class code should reflect the same design principles as the external structure: no long monolithic hunks of redundant, confusing code. You incur overhead by calling methods internally, so you shouldn’t go crazy, but succinct operations should typically be segregated into separate methods even if they aren’t reused in order to improve the readability and maintainability of the code. Alternatively you can achieve the same ends with generous inline comments.

8.13 PropertiesProperties in peoplecode app classes are interesting. They’re kind of like public instance variables, except that they can be designated as read-only and can be associated with implicit accessor and mutator methods.

Public, mutable properties are typically a bad thing in object-oriented code because it allows external code to read and change the internal state of the object without any

Page 39: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

controls. For example, if you want to perform some special logic whenever the value of a particular property changes, you can’t do it because the property can be accessed directly. Accordingly, the general rule of thumb is to store an object’s state exclusively in private instance variables which are accessible via public setter and getter methods…If you need to perform some sort of logic before or after a private instance variable is changed or read, you can do it in these methods.

However, peoplecode introduces an interesting solution to this. You can modify property declarations with ‘get’ and ‘set’ keywords. If you define a property with a ‘get’, you also have to define a get/end-get block in the same part of the code that contains method bodies. This block of code is basically the same thing as a getter method, except it executes whenever the property is read.

The ‘set’ keyword is the same: you have to define a set/end-set block that is functionally equivalent to a setter method. App Designer automatically declares an implicit argument in set blocks representing the new value that a property is getting set to when the block gets executed.

There may be a slight performance advantage to using get/set blocks instead of getter and setter methods (and it will save you a lot of typing). So they are probably the way to go.

8.14 Instance variablesAny variable that should be accessible everywhere within an instance of a class but not to the outside world should be declared as an instance variable (which are always private in peoplecode).

A common design flaw is to make every instance-scoped variable a public property. App class developers should consider each variable individually and decide if it belongs in the public interface or not. If it does, further consideration should be given as to whether the property warrants get and/or set modifiers. If the variable only serves internal purposes, it should be declared as a private instance variable.

8.15 Constants and readonly propertiesConstants are always private in peoplecode. There’s nothing particularly special about them except for that, and maybe that their datatype is always strictly implicit from the value you assign to them.

One noticeable omission in peoplecode app classes is static class variables, which is like a public constant associated with a class.

These are very useful for increasing the readability of code, making code less bug-prone, and centralizing literal values (which lessens the impact of changes).

Page 40: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

You can create something functionally similar to public static class variables in peoplecode by declaring readonly properties. As long as the class that defines them initializes them and doesn’t change them, they’re almost as good (the only difference is that you need an instance of the class to access them).

For example, consider a Category object has a categoryType member that can be one of the following values: ‘R’ for Regular, ‘B’ for Browse, and ‘N’ for News. The best way to handle this is to define 3 readonly properties corresponding to each of these values (REGULAR, BROWSE, and NEWS) and then initialize them to the appropriate values in the constructor of the class. Calling code that has to evaluate the categoryType of the category would always perform a test like this:

If (&category.getCategoryType() = &constants.NEWS) then…

This is better than referring directly to the string literal ‘N’ for several reasons: First, if you spell it wrong, the compiler won’t catch it…if you spell the readonly property wrong, it will. Secondly, if you ever need or want to change the value of NEWS, you only have to do it in one place. Thirdly, it’s easier to understand the code (because NEWS is a lot more intelligible than ‘N’).

It’s a judgement call whether you should define readonly properties like this in a class closely associated with the constant, or if you should centralize them in one place (for example, a Constants class). A middleground is probably best: If constants are inextricably linked to a class, it’s probably clearer and more efficient to define them within that class. If they’re more general (potentially used by multiple classes), they should be consolidated into maybe an application-level or package-level constants class.

Message catalog set and message numbers are also good candidates to centralize into a single constants class. It can both improve the readability of code and dramatically increase the ease of maintaining these values. For example, you can define a constant MSG_SET_NBR, and then other meaningful constant values for each message that explain a little about what the message represents (like REQUIRED_VALUE_MISSING, UNIMPLEMENTED_METHOD, or INSUFFICIENT_PRIVILEGES).

As a convention, it’s useful to make all constants uppercase (whereas variable names are always mixed case). This provides a clear visual cue that they are constants.

8.16 Method parameters

In peoplecode functions, parameters are always passed by reference; in app class methods parameters with primitive data types are passed by value by default. If the ‘out’ modifier is specified, the parameter will be passed by reference.

For example

Page 41: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

/* argument passed by reference */method increment(&value as number out);

/* argument passed by value */method increment(&value as number);

Parameters with object data types are always passed by reference:

/* argument passed by reference */method storeInfo(&f as File);

If you specify the ‘out’ modifier for a method parameter with an object data type, it becomes a reference parameter. This means that the parameter variable is passed by reference instead of the object that it is pointing at when passed.

For example, if you pass an object parameter with the ‘out’ modifier like this:

Local MyObjectClass &o1 = create MyObjectClass(“A”);Local MyOtherObjectClass &o2 = create MyOtherObjectClass();

&o2.myMethod(&o1);

And inside myMethod this occurs:

Method myMethod&arg = create MyObjectClass(“B”);

end-method;

Since the method argument gets reassigned within the body of myMethod, &o1 will not point at the new instance of MyObjectClass (initialized with “B”) after the method call completes. This is because &o1 necessarily still references the original instance of MyObjectClass.

However, if &o1 had been passed with the ‘out’ modifier, then after the method call completes &o1 will point at whatever the parameter was last assigned to (in this case, the new instance of MyObjectClass): the parameter, rather than the object, gets passed by reference.

It’s important to understand the distinction. It is a powerful feature of peoplecode, but it can also be a debugging nightmare if you don’t know what the expected behavior is.

8.17 Other variablesPeoplecode allows you to declare global variables in app class code (below end-class, alongside method body declarations). It’s unclear why, because it doesn’t really bring anything to the table except for scope ambiguity. App classes shouldn’t know about anything scoped outside of their instance, and instance variables take care of that. If global variables could be accessed directly by subclasses (like protected variables), than there would be a stronger argument for them. But this

Page 42: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

isn’t the case, so unless you discover a special situation in which nothing else will suffice, you shouldn’t use global variables.

The same thing goes for overriding variables. Since instance variables are private in scope, you can’t access them in subclasses. However, you can redeclare them in subclasses. The same is true for public properties, but it’s more confusing because you can explicitly access the overridden or overriding property.I’m sure there are ways to use this, but they’re all a little too clever for their own good. In other words, they’ll only end up making your code more confusing and bug-prone, and I guarantee there will always be another more intuitive way around the problem.

8.18 ExceptionsApp class methods shouldn’t communicate back to calling code with cryptic return values (for example, 0 means success, -1 means error X happened, -2 means error Y happened, etc.). Instead, app classes throw exceptions.

Exceptions are necessarily instances or subclasses of the built-in peoplecode class Exception. You can create exceptions by calling the built-in function CreateException(msgSetNubr, msgNbr, defaultTxt, subst. Var list) (Although you’d be better served creating an Exception base class that encapsulates the built-in function call and handles its function parameters consistenly).

In calling code you should always catch exceptions that can be potentially thrown using try/catch blocks. Unfortunately, peoplecode method declarations don’t include a throws clause indicating the exceptions that the method can throw. Accordingly, without good documentation, programmers have to resort to reviewing the app class code to figure out what they have to catch.

This is exacerbated by the fact that method A can call method B which can call method C which throws an exception….If method A and B don’t handle that exception, they will also throw it (but it becomes much more difficult for the programmer to figure out what the potential exceptions are.

Since the same method can throw exceptions for several different reasons, it’s a good idea to subclass Exception often and throw strongly typed exceptions so calling code can make decisions based on the exception type (instead of having to resort to something very granular and potentially mutable like message numbers.

For example, a method that retrieves customer data from a database might throw a SQLException if a database error occurs, or a PrivilegeException if the current user doesn’t have the appropriate permissions to perform the operation. There maybe be lots of different errors that can cause either of these exception types to be thrown in this method, and if necessary the calling code could examine the specific message numbers and react accordingly. However, by using specialized exception subclasses, the methods

Page 43: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

provide a middle ground for calling apps: if any SQLException occurs, do X; if any PrivilegeException occurs, do Y; otherwise, do Z.

If you don’t catch an exception in a page script, the script will abort and peopletools will display a modal dialog box with the message catalog entry in addition to information about the technical context of the exception (for example, peoplecode object name, etc.) This may seem like an acceptable way to handle exceptions, but it’s very suboptimal for the following reasons:

1. It makes the assumption that the script should abort if an exception occurs. Quite often application code would continue some form of processing even in the event of an exception. Even if you know what you’re doing and it’s perfectly okay in a particular script to let processing abort if an exception occurs, this won’t be self-evident in your code. A maintenance programmer has no way of knowing if you were aware of the possibility of a given exception being thrown…and worse yet, the maintenance programmer may not have any clue that an exception potentially gets thrown somewhere, and may write some important code that gets inadvertently skipped in the event of an exception. If you write explicit try/catch blocks, your code will be much clearer and less bug-prone.

2. The error message that peopletools displays is not an appropriate message for end users because it contains technical context information. And the fact that peopletools currently throws a dialog box in this situation is undocumented behavior and susceptible to change.

The implications of not catching exceptions in other contexts, like App Engine programs are similar, but typically less likely to be ok. You may have an AE program that performs 10 different operations. An uncaught exception in the first operation will cause the whole process to abort.

8.19 Documentation conventionsA good API reference is absolutely key for app classes to be used efficiently and effectively. Part of the burden is on the programmer to design clean and intuitive public interfaces, and to generously document code inline. The other part of the burden necessarily has to be on a tool that can automatically generate API docs from source code, because writing them from scratch and maintaining them is too time-consuming and tedious a process.

Javadoc is a well-known and very good tool that generates API documentation for java source code. In order for it to do all its magic, though, it requires that you follow certain conventions in your inline documentation to assist in its parsing of the source code. Any similar tool build for peoplecode will require similar assistance, so it’s a good idea to start adopting conventions and including documentation-tool-friendly directives in your code today even if you have no tool.

Page 44: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

8.20 Method and class header commentsFor example, there are two types of inline comments…those which should remain strictly internal and those which can potentially be reused in API docs. Normal peoplecode comments start with /* and end with */….by starting a comment that is intended to be turned into documentation with and additional asterisk (/**), documentation tools can differentiate between the two types of comments.

Keeping a running line of asterisks along the side of an extensive comment helps the readability of the code by making the entire comment stand out from the surrounding code.

The following is an example of javadoc documentation conventions for method header comments, which are also suitable for peoplecode:

/** * Description of what method does * * @param a optional description of param a * @param b optional descriptions of param b * @exception PrivilegeException thrown if etc, etc * @exception SQLException thrown if etc., etc. * @return String – description of potential values */Method myMethod

...End-method;

Method parameters can be inferred from method signatures, but associated comments can’t. Adding an optional @param tag inside comments for each method parameter that you want to specify a description for solves this problem.

Return values can be inferred from method signatures also; adding a @return tag to comments allows you to comment the return value specifically (for example, explaining the potential values a method may return, or whether it may return null in some circumstances).

Exceptions (especially those thrown by nested methods) are very, very difficult to infer from peoplecode…and descriptions explaining what causes certain exceptions to be thrown can’t be inferred at all. Adding @exception tags that cite each exception class with an optional description address this.

Realize that not all methods require documentation at all. For example, some simple setter and getter methods have method signatures that say it all (unless there’s some hidden functionality in them, in which case it should be documented). In other cases, it may suffice to simply include a brief sentence in the header explaining what the method does. The specific @param, @exception, and @return value tags are only necessary when they help to explain what might otherwise be unclear. Programmers should use

Page 45: dbmanagement.infodbmanagement.info/Books/MIX/Performance_tips,_guidelines... · Web viewSet the file layout once, outside the loop. Are you done with that? Once you are done with

their discretion and not waste time over-documenting…but also not under-document out of laziness.

Peoplecode Get/set blocks are, for all intents and purposes, methods…So they should carry method header comments also.

Class header comments should provide descriptive information about the class, and may optionally include additional tags, indicating version and author:

/*** class description** @version 1.0* @author amachado*/

Documentation is important in all code, but even more so in app class code, which aims at (and incurs certain overhead in the name of) higher levels of reuse.

8.21 Summary

Peoplecode applications are fundamentally a better way to write most of the code in peopletools-based applications.

They aren’t a panacea without adherence to certain design principles, they will incur performance overhead, and they will not realize their promise without good engineering processes that efficiently document and publicize the existing codebase so it can be leveraged for reuse. But the potential benefits far outweigh the disadvantages as long as development teams cooperate and commit to the discipline required to pull off this paradigm shift.


Recommended