Tuesday, March 30, 2010

Specific call reduction approaches – stored procedures

When I drop into discussing specific approaches to doing thing, I’m going to have to use the terms and facilities that are commonly available in the enterprise deployment environment that I’ve spent most of my time in, which is Java/J2EE/Oracle. That said, the reasons why these approaches improve performance are about the physical structure of common deployment environments, so they should hold true on any technology stack.

In general, an enterprise application’s business logic and database tiers will reside on different platforms in the data center with a network segment in between. That separation means that calls between the tiers are going to have durations hundreds or thousands of times higher than calls within each tier. Also, when application data needs to be written to the database, SQL will probably be what is being executed and in modern applications that SQL will be prepared/compiled and will use bind variables.

When SQL using bind variables is used, there are several interactions between the DB client and the DB server:

prepare – the SQL text is passed to the server and compiled
bind/execute – bind variable values are passed to the server and the statement is executed
fetch – in the case of queries, one or more batches of rows are returned to the client

Certainly, there are optimizations available in certain deployment environments, but in the end these are three separate DB client/server interactions and they will generally all occur. So, for each statement executed we may have three separate network calls and each will be hundreds of times longer that an intra-tier call.

What we need is a way to increase the granularity of the statements being executed, which will decrease the number of executions necessary to implement a particular application use case. Stored procedures, which are basically blocks of code that can be called with parameters in the DB tier, are a very good way to accomplish this. What they allow is the accumulation of all the bind variables that would have been passed to the separate statement executions into a single parameter list to be passed in a single execution to the DB where the original statements can then be executed.

In situations where the statements to be executed are queries, stored procedures can increase the benefit you see from platform specific optimizations. The main one I’m thinking of here is what Oracle calls ‘row pre-fetch’, which returns the first batch of returned rows from the ‘execute’ step in the statement life cycle. For a single query being executed, particularly queries that return few rows, dropping the fetch call across the network is an important gain. But when many queries are accumulated into a stored procedure with multiple result sets returned as output parameters (Oracle calls these cursor references), the single execution of the procedure call statement can return all of the query result sets pre-fetched, thus completely avoiding additional network calls to support the multiple queries being run.

Monday, February 15, 2010

Minimizing calls across tiers - common themes

It’s easy to say that the number of calls between tiers should be minimized, but actually reducing the number of calls takes some effort. Fortunately, there are some strategies that can be applied at all levels in the application design to move in that direction.

Caching, which I would define as saving a commonly used data object locally so that it can be used the next time it’s needed, is an important option. By saving the object locally, future inter-tier calls to get it are avoided and if the object is very commonly used, many calls can be avoided. Cached data may be held in memory if that resource is not tightly constrained on the local tier or written to disk if it is. In either case, there will need to be a reliable way for the local tier to become aware that the underlying data has changed so that the cached data doesn’t become stale.

In deciding what types of data are good candidates for caching, the main thing to consider is the ratio of the number of reads or loads of the data type compared to the number of changes or writes. In most applications, configuration data is a particularly good candidate for caching as it is typically read hundreds or thousands of times more often than it is changed.

The concept of caching comes fairly naturally to the client and app server tiers where developers are used to implementing or integrating desired functionality, but it is also relevant on the DB tier. First, the DB server software will manage a cache of commonly used data blocks so that disk I/O operations can be avoided. Second, some DB vendors explicitly make available facilities for designating types of data that should be cached. Leveraging these features in the DB server software/hardware platform can absolutely bring caching to the DB tier.

Another common strategy for reducing calls between tiers is focusing remote interface design on the user actions identified in use case analysis. In other words, instead of focusing design on the persistence needs of a taxonomy of object types that exist in the business domain, design of application components that cross tier boundaries should be driven by what actions a user will take.

In general, a user interface has the potential to persistent instances of multiple data types. As an example, an order entry screen may have a representation of :

- the customer doing the ordering
- customer account details
- the products being ordered
- product inventory levels

and the screen implementation can probably modify all of them. The use case for the interface will almost certainly contains a single ‘save’ action to persist changes, but at some point the application logic will need to split to update different database tables. The closer that split occurs in the application stack to the DB, the fewer inter-tier calls will be needed to accomplish it.

To some extent, the approaches described here for reducing inter-tier calls may not be obvious in the context of object oriented programming. Still, as long as the duration of a call across tiers is measured in milliseconds and the duration of an inter tier call is measured in microseconds, performance considerations will argue for coarse grained remote interfaces.

Sunday, January 10, 2010

Basic Definitions + Guidelines

For enterprise applications, the distinguishing feature of the deployment environment are the network links that exist between application tiers:



The existence of these network links is relevant because the time necessary to make a call over a network link is thousands of times longer than what would be necessary to make a call passing the same parameters locally. Further, looking at the time necessary to make a network call in detail reveals two components:

Network Latency – A relatively fixed duration that is incurred by all transmissions across the link. Think of it as the time necessary for the first transmission packet to make its way from the sender, through all intermediate networks and network devices, to the receiver.

Serialization Delay – A highly variable duration that is the time required to move the entire transmission content over the effective bandwidth available. A good analog for this type of transmission delay is water draining out of a sink. The larger the drain hole is, that faster the sink will drain.

Given that these two types of transmission delay together are typically thousands of times longer that memory call durations and neither adds anything positive to the application user’s experience, you would think they should be minimized. That leads us directly to the most fundamental guidelines for performance engineering:
  • Reduce the number of calls across application tiers in order to avoid network latency.
  • Reduce the size of messages that are passed between application tiers in order to avoid serialization delay.


Design efficiencies that are not related to these two fundamental guidelines do exist and they are important, but I’m always amazed at the variety of performance problems that relate to one of these if you dig into it deep enough.

Sunday, January 3, 2010

Priorities

You might think the first priority of a blog called ‘The Performance Analyst’ would be performance engineering, but optimizing application performance is just one element of an organization’s software development process. A manager of mine stated our priorities in this order:

1) Functionality
2) Scalability/Performance
3) Maintainability

Simply listing these things in priority order brings a lot of clarity to development discussions, mostly because you just don’t sacrifice a higher priority aspect of the software to improve a lower priority aspect. As a performance engineer, I find that clarity focuses attention on figuring out how to accommodate required functionality as opposed to questioning it’s necessity. That having been said, if an organization is truly committed to delivering a quality product those who control the functionality aspect will consider the performance and maintainability ramifications of their decisions. Similarly, those of us involved in performance engineering need to consider the maintainability ramifications of our decisions.

I mention these things because I have often heard developers criticize elements of my organization’s performance engineering program as harming maintainability. In many cases, instead of a true conflict between priorities, this criticism is more about valuing a lower development priority over a higher one.