Partitioning and
Scalability:
New Directions in Developer/2000
Release 2.0
An Oracle White Paper
October 1996
In the past few years, client/server design has gone
from miraculous cure-all to administrator's nightmare to
its rightful place as one of several mature alternatives
available to system architects. During that time, two
enduring questions have arisen again and again:
· "How should I best divide the processing
between the client and the server?"
· "How can I ensure that an application that is
adequate for the workgroup will be satisfactory for the
enterprise?"
These two questions are intertwined since one of the
biggest influences on the scalability of a system is the
partitioning of its processing. In this paper we will
attempt to provide a little background about what these
questions mean for the designer, and then take a look at
some of the innovations in Developer/2000 Release 2.0
that address these issues.
Application Partitioning
Application partitioning determines the amount of
processing performed on the client; the amount of
processing performed on the server; and, perhaps most
significantly, the amount of network traffic between the
two. Network capacity is often the most scarce resource.
Although PC processing power is not unlimited or free,
PCs are dramatically more powerful than they were a
decade ago, and continue to double in power every 18
months. Servers can be configured in parallel and cluster
configurations of bewildering power such that only the
very largest, most demanding organizations in the world
are concerned that there might not be a powerful enough
server configuration for their needs. And, as with PCs,
Unix and NT servers have gone from 16 bit to 32 bit to 64
bit hardware - an exponential growth in capability - in a
breathtakingly short span of time.
In that same decade, LANs have gone from 10 Mbps to ...
well, in most organizations, to 10 Mbps. We are perhaps
now on the threshold of a widespread move to 100 Mbps
LANs internally, coupled with a growing demand for
applications that can satisfy the Internet-based consumer
connected at 28.8 Kbps or less. At the same time, the
competition for bandwidth has never been more intense:
sound, images, video-conferencing,, net telephony and
other bandwidth "hogs" are making a scarce
resource even scarcer. We know that the next two
generations of Intel chip are already in progress; but
nobody can say where the next big leap in bandwidth might
come from.
There is no simple answer to the question of application
partitioning, because the right answer depends entirely
on what you mean by "best". By putting more
processing on the server you may be trading off greater
efficiency against less interactivity. For example, by
passing all validation to the server you can minimize the
amount of work required of the client, at the cost of an
often tedious wait for the network round trip. (Anybody
who has ever used an HTML form-based application on the
World Wide Web can attest to just how tedious a network
round trip can be!)
For these reasons, we usually suggest that the most
important goal for application partitioning is to
minimize the amount of network traffic: both the number
of round trips performed, to eliminate the latency; and
the volume of traffic, to reduce contention. In this
respect it is worth noting that there really only two
kinds of application: the system is either "fast
enough" or "too slow" (in the particular
case of a system that replaces a legacy system, this
becomes "fast enough" or "slower than the
old system").
Scalability Example
One of the greatest difficulties with partitioning and
scalability is that a large system can exhibit completely
opposite behavior to a small system. Consider a simple
example of a screen that presents a list of employee
records, which the user can choose to sort by name,
employee number, department or so on. (Picture a Windows
95 file folder, if you are familiar with that system).
Where would it be best to sort the records? For a small
number of records - say, 100 - it would probably be best
to fetch all the records to the client and sort them
there. Then, each time the user wants to see the records
in a different order, they can be re-sorted on the
client. They do not need to be fetched across the network
again.
For a large number of records however, it would be unwise
to fetch say, 10,000 records if the user only needs to
see the first ten before deciding that they prefer to see
the records in a different order. This would produce both
unnecessary network traffic as well as an unacceptable
delay. Far better to sort the records on the server and
transfer only the first ten. Of course, each time the
user wants to see the records in a different order, we
will need to re-sort on the server and send over another
ten records. But the user would have to try an awful lot
of different sorts before the network traffic matched
fetching all 10,000 records!
Somewhere in between there is a number of records where
it is hard to say which is better; the amount of traffic
will depend on just how often the users like to play
around with the sequence of the records. And of course,
you really won't know the answer to this until the
application goes into production.
The moral of this example is that for many questions of
scalability the best partitioning is very hard to
determine by pure reasoning and deduction. This means
that it is important to have a tool that allows you to
easily adjust your partitioning and scalability
decisions.
Developer/2000 and Scalability
One of the strengths of Developer/2000 has always been
features that help to address scalability and ease of
partitioning. We will describe some of the enhancements
in Developer/2000 Release 2.0 which directly reduce
network traffic and others which increase the developer's
available choices about partitioning. In the tradition of
Developer/2000, most of the features we will discuss
require little or no coding; and because they are
implemented by setting declarative properties, they can
easily be integrated into existing applications. We will
begin with a couple of features for tuning the
performance of existing blocks.
Reducing Server Resources
Developer/2000 has long allowed the developer to specify
the array size for fetching records into a data block.
This allowed the developer to balance the immediate
response of fetching a small number of records with the
network efficiency of fetching large numbers of records.
In Release 2.0 we have added a new property to data
blocks which allows you to specify that you want to Query
All Records. Forms then fetches all of the records in the
block rather than as needed. The tradeoff here is that
the increase in initial delay and possibly unnecessary
network traffic allows the server to free up resources
that it was using, for example to maintain the state of
the cursor and cache fetched rows.
Reducing Network Traffic
In Release 2.0 we have extended this capability to
perform Array Fetches to Inserts, Updates, and Deletes.
Previously, Developer/2000 would take one network round
trip for each such DML statement. Now, it will
automatically bundle up an array size of statements into
a single network round trip. This "Array DML"
gives an immediate and often dramatic reduction in
network traffic.
Array DML is implemented in an application simply by
setting the "DML Array Size" property for the
data block. This makes it very easy to adjust the array
size to balance network traffic against memory usage on
the PC. The vast majority of applications will be able to
benefit from this enhancement.
Data Blocks Based on Stored Procedures
The most significant enhancement we have made for
partitioning and scalability is to provide declarative
support for basing a data block on a stored procedure.
Stored procedures are the most direct way of moving
processing to the server. When correctly designed, they
can also eliminate many network round trips.
For example, by basing a query on a stored procedure the
foreign key lookups and calculations can be performed on
the server rather than in Post-Query triggers. Such
triggers typically add at least one round trip per row,
so losing the benefit of array fetches.
Similarly, by performing updates through a stored
procedure, audit trails or denormalized data can be
written without an additional network round trip; so can
validations that might be necessary before attempting to
perform the DML. This eliminates network round trips that
previously might have occurred in Pre-Update, Pre-Insert,
and Pre-Delete triggers.
Stored Procedures for Query
While it has been possible in the past to manually build
a data block on a stored procedure by writing
transactional triggers, such as On-Select and On-Fetch,
this technique sacrificed the benefits of array fetches.
The new declarative approach in Release 2.0 is not only
substantially easier to build, it also allows Forms to
perform array fetches through stored procedures.
There are two options for queries through stored
procedures in Release 2.0. The first option is to base a
data block's query on a stored procedure that returns a
Ref Cursor; the other is a stored procedure that returns
a Table of Records.
Query Based on Ref Cursor
A Ref Cursor is a PL/SQL release 2.2 construct that
allows the stored procedure to open a cursor, and return
to the client a "pointer", or reference, to the
cursor. The client can then fetch records from the cursor
just as if the client had opened the cursor itself.
Records are fetched through the Ref Cursor using array
fetches exactly as if Forms had opened the cursor itself
for a data block based directly on a table or view.
A data block based on a Ref Cursor has many similarities
to a data block based on a view, but there are two major
advantages to a Ref Cursor. First, a stored procedure
provides better encapsulation of the data. By denying
direct query access to the tables, you can ensure that
applications query the data only in ways that are
meaningful (for example, a set of tables might be
designed to be joined in a specific way to produce a
particular set of information) or only in ways that are
efficient (for example, queried in such a way that the
best indexes can be used).
The second advantage is that the stored procedure can be
more flexible. The procedure can determine at runtime
which one of several Select statements to execute in
opening the cursor. This decision might depend on the
role or authority of the user, for example: a manager
might see all of the columns in the Emp table, but a
clerk would be shown blanks for the salary. Or it might
depend on a parameter so that a different set of data -
historical versus current, for instance - can be
displayed in a single data block. This decision can be as
complex as you wish, providing you can write it in
PL/SQL. The only limitations are that all of the
different Select statements must return a compatible set
of columns; and the Select statement cannot be composed
dynamically at run time. (The database doesnt yet
support Ref Cursors with dynamic SQL).
Query Based on Table of Records
Introduced with PL/SQL release 2.3, a Table of Records is
an in-memory structure similar to a database table. The
stored procedure can build an in-memory table consisting
of, quite literally, any data at all you can construct,
row by row. Whereas a Ref Cursor allows you to return
anything that you know how to construct in SQL, such as a
tree walk, a Table of Records allows you to return
anything that you know how to construct in PL/SQL, such
as a tree walk with a join. Not only can you perform
lookups and calculations on the server side, you can also
make complex decisions about which records to include or
exclude from the returned record set. One example of
something relatively easy to do in PL/SQL and very hard
to do in SQL would be to return the Employees in each
department whose salary is in the top 5 salaries for
their department. (What makes this hard in SQL is that
several people could have the equal fifth high salary. In
PL/SQL, it's a relatively simple loop.)
When called in response to a Forms query, the stored
procedure builds the Table of Records on the server side.
It then returns the whole result set to the client at
once, using as few physical network round trips as the
network packet size allows. Each record in the Table
becomes a row in the Forms block. This frees up server
resources and uses the network bandwidth very
efficiently, at the cost of client resources and
potentially wasting network traffic for unneeded records.
So although a Table of Records allows the procedure the
greatest flexibility in determining the result set, it
should be used with care.
Stored Procedures for DML
In Release 2, you can also use a Table of Records
returned to a stored procedure to perform inserts,
updates and deletes from a block. The stored procedure
can then "fan out" your changes to as many
tables as necessary, potentially saving many network
round trips if your data model is highly normalized.
Writing audit trails is another possible use.
This technique requires that you provide a procedure for
each of Insert, Update and Delete. As with a block based
on a regular table, Forms automatically maintains the
state of each record to determine if it is an inserted,
updated or deleted record. At commit time, Forms
constructs a Table of Records for all of the inserted
records, another for the updated records and another for
deleted records. It then calls each of the procedures,
passing it the relevant Table of Records. As with a
query, the Table of Records is passed in a "single
shot", using as few network packets as physically
possible. In contrast with the situation for a Query,
there is no wasted traffic in sending the whole Table of
Records, since all the records have to be sent to the
server to be committed anyway.
Finally, it is worth noting that you might combine these
other techniques in any way, for example you might choose
to query through a Ref Cursor while performing DML
through a Table of Records, giving you the best of both
worlds.
Summary
Partitioning and reduction of network traffic are crucial
factors in the scalability of a client/server
application. Developer/2000 release 2.0 enables the
developer to exploit a wide range of techniques for
distributing processing and minimizing network traffic.
The use of declarative properties rather than code makes
it easy to use and easy to tune these sophisticated
capabilities in pursuit of the best partitioning scheme
at any scale, from workgroup to enterprise.
Partitioning and Scalability
October 1996
Author: Carl Zetie
Reviewer: Vicki Shimizu
Copyright © Oracle Corporation 1996
All Rights Reserved Printed in the U.S.A.
This document is provided for informational purposes
only and the information herein is subject to change
without notice. Please report any errors herein to
Oracle Corporation. Oracle Corporation does not
provide any warranties covering and specifically
disclaims any liability in connection with this document.
Oracle is a registered trademark and Oracle7, PL/SQL and
Developer/2000 are trademarks of Oracle Corporation.
Oracle Corporation
World Headquarters
500 Oracle Parkway
Redwood Shores, CA 94065
U.S.A.
Worldwide Inquiries:
415.506.7000
Fax 415.506.7200
Copyright © Oracle Corporation 1996
All Rights Reserved
|