Contents


Improving performance and scalability for IBM Sterling Order Management

Best practices for design and coding

Comments

Overview

IBM Sterling Order Management is an OLTP application and it is considered scalable if it can be upgraded to process more transactions by adding new processors, devices, and storage. Custom design or code has a significant impact on the scalability of the entire application. This article highlights the best design practices that can be adopted for a Sterling Order Management implementation to ensure application scalability.

  1. Reduce dependency impact from external systems
  2. Define optimal transaction boundary
  3. Eliminate garbage collection (GC) overhead
  4. Lessen I/O overhead
  5. Decrease network overhead

Design best practice 1 – Reduce dependency impact from external systems

IBM Sterling Selling and Fulfilment Suite® application interacts with various external applications like AVS (Address Verification System), payment system, tax calculation as part of order fulfillment cycle. These interactions are created from either application layer transactions or from agent layer transactions. An application layer transaction that is synchronous in nature is started by a request or response operation, and the result of the process is returned to the caller immediately after this operation. However, if the agent layer creating asynchronous transaction is started by a one-way operation and the results are returned by starting other one-way operations; interaction with external systems synchronously builds a strong dependency that impacts user experience. If the external system is unresponsive, a client application like COM or SOM would result in an unresponsive window for the user. Multiple invocations within a single transaction boundary impede the scalability since a web container thread will not be released to the common pool in time to handle other requests. This results in scarcity for the configured optimal number of web container threads. An application server has configurable thread timeout settings to ensure that the thread is not stuck for an indefinite period. However, it is the agent layer that might pose this threat since it does not have thread timeout settings. Therefore, it is vital to include a timeout implementation either in the Sterling Selling and Fulfilment Suite side or in the middleware (if present) and to throw the appropriate response timeout message to the user's screen in the event of no response from the external system.

Design best practice 2 – Define optimal transaction boundary

A transaction comprises a unit of work that is performed in "all or nothing" mode that ensures atomicity. Defining an optimal transaction boundary requires a clear understanding of the sequence of operations that are taking place within the transaction boundary to ensure a scalable and restorable application. The transaction boundary is applicable to both synchronous and asynchronous transactions. The user response time or throughput and locking mechanism are the two critical aspects to be considered when you determine the optimal transaction boundary. CreateOrder and ScheduleOrder APIs getting clubbed in a single transaction boundary for one implementation might not meet the throughput or response time for another implementation. All synchronous transactions should be shorter and ensure minimal exclusive locking of rows to achieve better response time. Incorrect template extension of base APIs might also cause unnecessary locking.

For example, the getShipmentDetails API invocation in V8.5 with the default template does not put a lock whereas if the template is extended to contain order details then exclusive locking of the rows on order record takes place. Scenarios where locking is inevitable like changeOrder, manageOrganizationHierarchy; a designer needs to define a shorter transaction boundary as all locks acquired by statements within a transaction are held during the transaction. Longer the transaction boundary the higher the chances of having row lock contentions in both application and agent layer that eventually impedes scalability.

The following example provides better clarity on the issue of locking. Assuming getOrderDetails and getShipmentList APIs are started one after other in a single transaction boundary. getOrderDetails acquires a lock on order record and does not release until getShipmentList completes the execution. All List APIs should be started with stringent criteria parameters to ensure a narrow search. This is even more critical in the event of List APIs that are exposed as a web service component to external system. If multiple records are fetched from the database and the ORDER BY clause exists in the SQL. Then the entire transaction has a much longer execution time due to sorting operation on database side. The database always tries to sort in the RAM space within sort_area_size and goes to a disk sort only when the RAM memory is exhausted. If the number of records does not fit the memory, then sorting takes place in disk, which results in even higher CPU cycles. This ORDER BY clause can be eliminated with the addition of an attribute IgnoreOrdering=Y in the input .xml file.

Design best practice 3 – Eliminate garbage collection (GC) overhead

Both the application and the agent layer use JVM™ for dynamic memory allocation. As more objects get created from inefficient custom code, there is little room for new objects in heap area, and JVM™ performs extra work that results in frequent garbage collection (GC). GC identifies data objects that are no longer in reference and then reclaims the memory that is occupied by those objects. Object creation is one of the most expensive operations in terms of memory utilization and performance impact. It is therefore advisable that you create or initialize an object only when it is required in the code. You must avoid creating temporary objects within frequently called methods. It is better to create the objects early and hold those objects until required. Java™ programs spend a significant amount of time in a loop. So never create temporary objects inside the loop instead optimize loop to achieve better performance. The following example shows an optimized for loop sample:

Listing 1. Initial for loop sample code
public loop control 1()
{
  String str = "Sudhanshu";
  for (int j = 0; j < str.length(); j++)
  {
    // code doesn’t change the length of the string.
  }
}

public loop control 2()
{
  String str = "Sudhanshu";
  int len = str.length();
  for (int j = 0; j < len; j++)
  {
    // code doesn’t change the length of the string.
  }
}

The loop control2 can be improved even more by modifying the loop to count backwards. The JVM™ is optimized to compare to integers between -1 and +5. So rewriting a loop to compare against 0 produces faster loops. The loop in Listing 1 can be changed to use the following for loop instruction:

Listing 2. Changed for loop sample code
for (int j = len-1; j >= 0; j-)

String concatenation with the "+" operator creates many temporary objects and increases garbage collection and is much slower in performance as compare to StringBuffer. Methods that operate on a string return a new string not an updated copy of the old one.

Listing 3. String concatenation sample code
String abc = "<OrderLineList>" +
		" <OrderLine DeliveryMethod="  "
		ItemGroupCode="  "
		OrderLineKey="  "
		OrderedQty="  " +
		" PrimeLineNo=" "
		SubLineNo="/>" + 
		" >) ";

Usage of inappropriate collections creates significant GC overhead. For example, an ArrayList creates space and copies all its references during an insertion, it becomes expensive as the ArrayList gets bigger. It is also important to remember a TreeMap is slower than aHashMap.

Design best practice 4 – Enforce appropriate database extension

Extensions to the OOB database tables are inevitable to hold custom columns as part of customization, however due diligence is required to choose the appropriate data type for the custom column. For example, you must choose between CHAR and VARCHAR2.

  • CHAR is used for storing fix length character strings. String values are space/blank padded before stored on disk. If this type is used to store variable length strings, it wastes disk space.
  • VARCHAR2 on the contrary is used to store variable length character strings. The string value's length is stored on disk with the value itself resulting in efficient usage of disk space.

Therefore, CHAR is a large number and because of this in Sterling Selling and Fulfilment Suite V9.1 all OOB CHAR columns are being replaced by VARCHAR2. A Sterling Selling and Fulfilment Suite OLTP transaction goes through frequent inserts and updates on the transaction data. These updates enhance the probability of row chaining and row migration issue to surface in the event of inappropriate data types are added as extended columns.

  • Row migration - The Oracle® database moves the entire row to a new data block if the row fits in a new block. The original row of a migrated row contains a pointer or "forwarding address" to the new block that contains the migrated row.
  • Row chaining - The Oracle® database stores the data for the row in a chain of one or more data blocks that are reserved for the segment. Row chaining most often occurs with large rows.

If the database performs one I/O to read an index and one I/O to read a table for a non-migrated row, then the database requires an additional I/O to obtain the actual row data for the migrated or chained row. Extending an OOB table that contains many columns like YFS_ITEM – 152 columns, YFS_ORDER_LINE – 179 columns, YFS_SHIPMENT – 156 columns to hold CLOB data type would result in row migration in the event of frequent updates. If total number of columns goes beyond 255 in a table, row chaining would surface. Therefore, any extensions to these Sterling Selling and Fulfilment Suite tables need to be performed diligently. Always evaluate the option of maintaining a separate entity with foreign key relationship to these entities to include custom columns for CLOB or VARCHAR (column size > 1000) data types. Also, implementation of template pruning is essential in the custom code to control the data that is fetched from only primary table versus both primary and secondary table to reduce the number of IOPS.

Design best practice 5- Lessen network and I/O overhead

A request for data from the application agent layer to the database layer goes though the network and in turn the database performs an I/O operation to fetch data from disk as response. With the optimal amount of data traveling through the network layer the database performs I/O operation in the expected time frame and the application meets the expected throughput and response time. If inappropriate design or code introduces overheads the application is no longer scalable. Exception handling is one of the features if implemented in appropriately induces significant network overheads.

For example, if an exception occurs during user operation and the custom logic throws entire error stack trace to a COM or SOM user, it results in network congestion as unnecessary data must traverse through network to display to the user. This displayed data is of little relevance to user. The OOB custom error code feature is used to display appropriate error message.

Listing 4. Inappropriate coding
yfsUserExitException.setErrorDescription(e.getMessage());
yfsUserExitException.setStackTrace(e.getStackTrace());

Presence of System.out.println statement inside custom Java™ code consumes disk space that is allocated for the application server instance and results in disk space contention. It also introduces latency in log writing resulting in slowing the throughput or response time as JVM logging is synchronized. The default logging is INFO and ERROR from the Sterling Selling and Fulfilment Suite application. Therefore, Log4j must be used to do the logging instead of System.out.println. Database would encounter I/O overheads due to large number of extended columns with data types as CLOB or VARCHAR2 (size > 2000) in tables.

For example, internally the product runs select query mostly with ‘*’ on YFS_ITEM.

Listing 5. SELECT QUERY
SELECT  /*YANTRA*/ YFS_ITEM.* FROM YFS_ITEM YFS_ITEM WHERE (YFS_ITEM.ITEM_KEY = 'XYZ')

The query in Listing 5 fetches a single record from database as Item_KEY being primary key is used in WHERE clause. But the I/O operation introduces a performance bottleneck because of the large number of columns that include extended CLOB or VARCHAR2 columns present in the YFS_ITEM entity. Therefore, you need to ensure that the data that is fetched is strictly controlled by the template.

Conclusion

Scalability and performance are closely entwined. An application cannot scale in the presence of critical performance bottlenecks that are introduced from inappropriate design and development. These five design practices must be implemented during both design and development phase of an implementation to ensure scalability.


Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Commerce, Java development
ArticleID=979916
ArticleTitle=Improving performance and scalability for IBM Sterling Order Management
publish-date=08072014