last revised 5/08/00
All of the models are taken from Dr. Kenney's Ph.D. dissertation on transaction processing. The first models present fundamental concepts of transaction processing. Successive models present the properties of atomicity, isolation and durability. The models conclude with advanced features of security, distribution and performance.
A exempliary TP system architecture contains a single application program that communicates with a set of resources. A "boxes and arrows" depiction of such an architecture is given in Figure 1.
Figure 1: A TP System Architecture.
Figure 1 shows a system consisting of an application program component (at the top) and several (three) resource components (at the bottom). The arrows connecting the components depict that application program may exchange communication with each of the resources, while the resources cannot communicate with each other directly.
We will use the Rapide tools to produce evolutionary prototype models of TP. However, to do so we must first install the tools so that you can use them.
Run the raparch program by typing:
A box will appear with several menus at the top, two sets of tool bars on the left, and a grid (called a canvas) in the center. Canvas is equipped with a horizontal and a vertical rulers for convenient positioning of objects. The architecture name is specified in the entry widget (labelled "Arch Name:") at the bottom. There are two sets of tool bars on the lefthand side of a canvas. The first group is for drawing and editing components and connections, such as (create a component) or (create a connection) mode. The second group of tool bars is used to depict features of a component for its behavior specifications. A tool bar in this group can only be activated in conjuction with (select) mode of the first tool bar group.% raparch &
Figure 2: Initial View of Raparch
Figure 3: Single Application Program and Resource System Architecture.
Modify the module properties of application program component by (1)
being in mode, and (2) pressing down
in the "Module 1" box on <Alt>-RightButton. An associated module properties
window will appear. Modify the "Module Name" to be "APPLICATION_PROGRAM"
and modify the "Module Label" to be "Application\nProgram." (The "\n" in
the label means a newline) Then click on the OK button.
Modify the modules properties of the resource component by (1) being in mode, and (2) pressing down in the "Module 2" box on <Alt>-RightButton. Modify the "Module Name" to be "RESOURCE" and modify the "Module Label" to be "Resource." Optionally, you may change the color of the module by typing in a color or using the "Choose..." button to select a color. Finally, click on OK.
A double headed arrow creates a ``two-way'' connection between the components; by two-way we mean that in the animation that you will produce later in the this tutorial, events will traverse in both directions along this pathway. If you desire a ``one-way'' connection, which limits events to tranverse along the pathway in only the direction of the arrow, then press and release the LeftButton on the source component and <Shift>-LeftButton on the sink component.
Note: A pathway without either arrow heads defaults to one-way in the direction the pathway was created. The direction of the arrow can be changed using a path properties dialogue box.
Modify the connection's path properties by pressing and releasing <Alt>-RightButton anywhere along the path while you are in the mode. For example, change the "Path Width" to 3. Finally, click on OK.
Optionally, you can modify the direction of arrow and connection mode by clicking the appropriate radio button for the corresponding field. Like in a component properties window, you may change the color of the path by typing in a color or using the "Choose..." button to select a color. A path name can optinally be specified by typing in a particular name for the path as well.
Optionally, you can resize a component when you are in mode by pressing down and dragging an edge of the box with <Shift>-RightButton.
Optionally, you can reposition a path when you are in mode by pressing down and dragging an end of the path with <Shift>-RightButton.
Note: If you save the file under a different name, be sure to use that name instead of main.arch throughout the rest of the example! So far we are building a conceptual (initial) model of the system architecture. We save this conceptual architecture in "main_concept.arch" by the Save As option of the File menu. The file name is inherited from the name used in "Arch Name" field at the bottom of the canvas. A default architecture name used is "main".
These action declarations mean that the application program can generate "out" Request events and receive "in" Result events. These actions will be "visible" to the resource component as we shall see later.action out Request();action in Result();
Click on the "Behavior..." button to describe the component's behavior rules. It will bring up a new window for the component behavior, labelled "APPLICATION_PROGRAM." There are "Behavioral Declarations" and "Behavioral Rules" sections in the new window. Interfaces in Rapide can also include behavioral declarations that we will initially leave alone. We will modify them later to enrich our behavioral specification. Notice the default action declaration that is special. This action's associated events communicate to raptor the "name" of the component that generated the event.
Modify the behavioral rules section to be:
This behavior rule waits until a start event is observed and reacts to it by generating two events: an animation_Iam event and a Request event. The animation_Iam event is parameterized with the string "APPLICATION_PROGRAM."start => animation_Iam("APPLICATION_PROGRAM") -> Request();;
When you are finished making the modifications, click on the DONE button
for behavioral rules window and OK button for type interface window. The
snapshot of this architecting process is depicted in Figure 4.
Figure 4: Application Program Component Features and its Behavioral Descriptions.
And modify the "behavioral rules" section in a behavior window to be:action in Request();action out Result();
When you are finished making the modifications, click on the DONE button for behavioral rules window and OK button for type interface window. The snapshot for RESOURCE component behavior modification can be shown in a similar figure like Figure 4.start => animation_Iam("RESOURCE");; Request() => Result();;
Modify connections for the system. First, delete a connection made
between application program and resource components by selecting the path
between two components and using Delete option of the Edit menu. Instead
we will make a connection between two components, in terms of components'
features, that is, "Request" and "Result" action pair of each component.
To create a path between features of components, first enter the
"connect modules" mode by clicking on the button in the first tool bar
group with icon. Press and
release LeftButton on the out action "Request of Application Program"
and then again on the in action "Result of Resource" component..
This will produce a solid line between two features. Modify the connection's
path properties by pressing and releasing <Alt>-RightButton anywhere
along the path while you are in the
mode. For example, modify the "Path Width" to 3 and connection mode to
be "pipe" for now, then click on the OK button. You will see a change of
line pattern for a path, from a solid line to a dotted line. Details about
connections will be discussed later in this tutorial. Note: Since features
of components already implies the direction of flow (e.g., out or
in),
arrows may not be required for connections between features. A solid line
represents a basic connection and a dotted line for a pipe
connection in Rapide. Figure 5 shows two pipe connections between
Request and Result action pairs of ApplicationProgram and Resource components
in the architecture.
Figure 5: Connections between Components based on their Features.
And make sure "Architectural Connections" section to be:APPLICATION_PROGRAM : APPLICATION_PROGRAM; RESOURCE : RESOURCE;
When you are finished making the modifications, if needed, and click on the OK button. The situation is depicted in Figure 6.connect APPLICATION_PROGRAM.Request() => RESOURCE.Request(); RESOURCE.Result() => APPLICATION_PROGRAM.Result();
Figure 6: Main Architecture Description
The architecture code that you have specified can be used to generate
a
Rapide program via the Generate Rapide Code option of the RAPIDE
menu. Enter "main.rpd" in the selection widget to create a file named main.rpd.
When you are finished making the selection, click on the OK button. You will have produced a file named main.rpd containing the following Rapide program.
Also, save raparch's state with the Save option of the File menu. This completes the construction of the structural and behavioral model, in two files named "main.arch" and "main.rpd."TYPE APPLICATION_PROGRAM IS INTERFACE action out Request(); action in Result(); BEHAVIOR action animation_Iam(name: string); BEGIN start => animation_Iam("APPLICATION_PROGRAM") -> Request();; END; TYPE RESOURCE IS INTERFACE action in Request(); action out Result(); BEHAVIOR action animation_Iam(name: string); BEGIN start => animation_Iam("RESOURCE");; Request() => Result();; END; ARCHITECTURE MAIN() IS APPLICATION_PROGRAM : APPLICATION_PROGRAM; RESOURCE : RESOURCE; CONNECT APPLICATION_PROGRAM.Request() => RESOURCE.Request(); RESOURCE.Result() => APPLICATION_PROGRAM.Result(); END;
% r.mklib
Compiling the program should take approximately 1 minute on a Sun Ultra. If you get errors during the compilation, please check the associated sections of the code for typographical errors. Upon correcting the error, don't forget to save the program.% rpdc -M main -o main main.rpd
Execute the program by typing:
This will produce a history of the execution recorded in main.log.% main
% raptor -a main.arch main.log &
The pov is invoked with the log file name as its command line argument:
The pov starts initially with the view manager window which lists the computations (and views) that have been loaded; at this point only one computation has been loaded, the main.log, and it has been given the label "Computation 1." To examine the poset graphically, select Computation 1 and use the poset viewer option of the View menu. Also, try the table viewer to represent the events in a tabular form.% pov main.log &
Figure 6: Pov's PlanarViewer of main Execution.
If you are running a secure X server, you can coordinate the animation's displaying of the events with modification of the pov's poset viewer's coloring of the events. To coordinate the animation with the visualization, you may drag and drop the computation from the pov to raptor. One way to do this is to click and hold RightButton on "Computation 1" in the view manager window of the pov and drag the computation over to the raptor window. When the dragged computation turns green, you may release the button. If the computation doesn't turn green, you are probably not running a secure X server.
Modify the behavioral declarations section to include:
Also add the following rule to the behavioral rules section:i : var Integer := 0;
Save raparch's state with the Save As option of the File menu and generate the Rapide code (via Generate Rapide Code option of RAPIDE menu) under the name main2.arch and main2.rpd respectively.Result() => if ( $i < 4 ) then i := $i + 1; Request(); end if;;
To compile this version, we must remove the references to the APPLICATION_PROGRAM and RESOURCE modules in the library manager's database. To do so, clean the library or remove the main.rpd.
or% r.rm main.rpd
and to compile it:% r.cleanlib
After execution we can analyze the execution via raptor and pov.% rpdc -M main -o main2 main2.rpd
% main2 % raptor -a main2.arch main2.log & % pov main2.log &
Figure 7: Pov's PlanarViewer of main2 Execution.
We will modify the (main2) system to demonstrate how to use service features. We can construct AP_R interface in two ways: (i) create the interface graphically, specify properties of component with constituent features (in the same way we constructed the other components in the architecture), and then delete the graphical AP_R object from the canvas - in effect only the component's type interface kept, or (ii) create the interface textually using the Show Type Interfaces option of the RAPIDE menu. We introduce the latter method: select the Show Type Interfaces option of the RAPIDE menu. Press on the NEW button of the shwif window. Name the interface "AP_R" and add the followings to the actions and services section:
Modify the behavior rules section to be:action out Request(); in Result();
When you are finished making the modifications, click on the DONE button and the OK button in order.start => animation_Iam("AP_R");;
Notice that the AP_R entry is at the bottom of the Rapide Type Interfaces section of the shwif window. This is a minor problem, because we will be using the AP_R definition in our modifications of the APPLICATION_PROGRAM and RESOURCE type interfaces, and a type can only be used after it is declared. Therefore, we will need to reorder the list of interface types. This is accomplished by dragging the AP_R entry above the APPLICATION_PROGRAM entry with the RightButton. Click on Done.
service R : AP_R;And modify the behavioral rules section to look like:
Click on the DONE and the OK Buttons in order for APPLICATION_PROGRAM type interface window.start => animation_Iam("APPLICATION_PROGRAM") -> R.Request();; R.Result() => if ( $i < 4 ) then i := $i + 1; R.Request(); end if;;
Now, we need to make a connection between two services of application program and resource components in the same way explained in the previous architecture (main.arch). And modify connection's properties by pressing <Alt>-RightButton anywhere along the path in mode: Path Width to be 3, connection mode to be pipe.service AP : dual AP_R; start => animation_Iam("RESOURCE");; AP.Request() => AP.Result();;
connectAPPLICATION_PROGRAM.R => RESOURCE.AP;
Click on OK. The architecture with services extension is demonstrated
by Raparch in Figure 8.
Figure 8: Main Architecture Description with Services Extension (main3.arch)
Analysis of Architectures with Services
Save state with the Save As option of the File menu and generate the
Rapide
code with the latest modifications under the name main3.arch and main3.rpd.
Then, compile and execute main3 via:
% r.cleanlib % rpdc -M main -o main3 main3.rpd % main3 % raptor -a main3.arch main3.log & % pov main3.log &
And add the following declaration to the behavioral declarations section:service R : AP_R; R2 : AP_R;
And modify the behavioral rules section to look like:action generate_requests();
When you finish modifications, click on DONE and OK buttons.start => animation_Iam("APPLICATION_PROGRAM") -> generate_requests(); R.Result() ~ R2.Result() => if ( $i < 4 ) then i := $i + 1; generate_requests(); end if;; generate_requests() => ( R.Request() || R2.Request() );;
Position the second resource component where you want the box to be. Then, create a dual AP_R service feature on the second resource box: Name the service to be "R2" and the type to be "AP_R" in the feature property window. Open up the RESOURCE2 type interface window. The type interface window should contain the following declaration in the actions and services section:
service AP : dual AP_R;And make the following modifications in its behavioral rules sections:
start => animation_Iam("RESOURCE2");;
AP.Request () => AP.Result ();;Note: Due to the limitation in the current raparch implementation, when you duplicate an component, features of a component are not duplicated, thus you must create constituent features manually. You can move components around on the canvas by selecting and dragging them with LeftButton while in mode. Also you may want to turn off "snap" option in Options menu for more precise positioning of objects on the canvas.
Create a pair of connections between application program component and two resource components (Resource and Resource2): one path between "R of Application Program" and "AP of Resource" and the other between "R2 of Application Program" and "AP of Resource2".
And make sure that the architectural connections section to be:APPLICATION_PROGRAM : APPLICATION_PROGRAM;RESOURCE : RESOURCE;RESOURCE2 : RESOURCE2;
The architecture with a second Resource component is demontrated by Raparch in Figure 9.APPLICATION_PROGRAM.R => RESOURCE.AP; APPLICATION_PROGRAM.R2 => RESOURCE2.AP;
Figure 9: Main Architecture with a Second Resource Component (main4.arch)
% r.cleanlib % rpdc -M main -o main4 main4.rpd % main4 % raptor -a main4.arch main4.log & % pov main4.log &
The work-around is to use a parameter of the event to record the index. Thus, instead of writing the previous pattern we will write:(?Idx : Integer) Service_Set(?Idx).Action_Name()
Therefore, we will modify the actions of the AP_R service interface to include a parameter that will record the appropriate service set index value. Select the Show Type Interfaces option of the RAPIDE menu. Modify the AP_R interface to be:(?Idx : Integer) Service_Set(?Idx).Action_Name(?Idx)
action out Request(index : Integer); in Result(index : Integer);
service Rs(1..2) : AP_R;
action generate_requests();
start => animation_Iam("APPLICATION_PROGRAM") -> generate_requests();; generate_requests() => [ !i in 1..2 rel || ] Rs(!i).Request(!i);; [ !i in 1..2 rel ~ ] Rs(!i).Result(!i) => if ( $i < 4 ) then i := $i + 1; generate_requests(); end if;;
Modify the behavior section to be:service AP(1..2) : dual AP_R;
And make a connection between service set of application program and resources components and modify the properties: Path width to be 3, Connection mode to be pipe.start => animation_Iam("RESOURCES");; (?i : Integer) AP(?i).Request(?i) => AP(?i).Result(?i);;
APPLICATION_PROGRAM : APPLICATION_PROGRAM; RESOURCES : RESOURCES;
The architecture with service set extension is demonstrated by Raparch in Figure 10.APPLICATION_PROGRAM.Rs => RESOURCES.AP;
Figure 10: Main Architecture Description with Service Set Extension (main5.arch)
% r.cleanlib % rpdc -M main -o main5 main5.rpd % main5 % raptor -a main5.arch main5.log & % pov main5.log &
Modify the modules properties of the resources component: 1) module
name to be "SOME_RESOURCES" and 2) module label to be "Some\nResources."
start => animation_Iam("RESOURCE");;
(?i: integer) AP.Request(?i) => AP.Result(?i);;Now add a similar behavior to the RESOURCE2 component. A trick to efficiently copying the RESOURCE's behavior over to the RESOURCE2 component is to bring up the type interface window for the RESOURCE2 component, change the name to be RESOURCE, and then press Enter. This will bring over the RESOURCE behavior. Modify the name back to RESOURCE2. Dont' worry about the name in the animation_Iam event. Raparch will automatically update the name to be the name of the interface type.
action animation_Iam(name: string); RESOURCE : RESOURCE; RESOURCE2 : RESOURCE2;
connect AP(1) => RESOURCE.AP; // SOME_RESOURCES => RESOURCE.AP; AP(2) => RESOURCE2.AP; // SOME_RESOURCES => RESOURCE2.AP; initial animation_Iam("SOME_RESOURCES");
Finally, verify that main architecture contains the following in
architectural declarations and connectictions sections:
The architecture with a subarchitecture is demontrated by Raparch
in Figure 11.
Figure 11: Main Architecture Description with a subarchitecture (main6.arch)
When you animate main6, you can examine the subarchitecture by double-clicking on the Resources component. Another window will appear that contains the Resources subarchitecture.% r.cleanlib % rpdc -M main -o main6 main6.rpd % main6 % raptor -a main6.arch main6.log & % pov main6.log &
Modify the formal parameters to be:
and the actions & services from:(n : Integer)
toservice Rs(1..2) : AP_R;
and the behavioral rules from:service Rs(1..n) : AP_R;
to[!i in 1..2 rel ~] Rs(!i).Result(!i) => [!i in 1..2 rel || ] Rs(!i).Request(!i);;
Note: Currently, a bug in the modification function causes the old application program interface to persist, despite being modified to include the formal parameter. Please remove it from the type interfaces list. (NOT any longer !)[!i in 1..n rel ~] Rs(!i).Result(!i) => [!i in 1..n rel || ] Rs(!i).Request(!i);;
Formal parameters:
and the behavioral rules from:(n : Integer)
toservice AP(1..2) : dual AP_R;
Don't forget to remove the old RESOURCES type interface.service AP(1..n) : dual AP_R;
Modify the formal parameters of the architecture constructor to be:
Modify the Return Type Interface to be:(n : Integer)
Modify the Declarations in architecture to be:RESOURCES(n)
Modify the Architectural Connections to be:action animation_Iam(name: string); Rs : array[Integer] of RESOURCE is (1..n, default is new(RESOURCE));
Note: Experiment by using different connections here. In particular, look at the poset produced using "to" and "||>". Details of connections may be found in the Architecture LRM, but we will give a brief overview of Rapide connections:connect for i : Integer in 1..n generate AP(i) => Rs[i].AP; end generate; initial animation_Iam("SOME_RESOURCES");
A connection consists of an optional sequence of outermost placeholder declarations, a trigger to the left of an operator, an operator (to, => or ||>) and a body to the right of the operator. The scope of the outermost placeholder declarations extends to the end of the rule.
A connection rule with the to operator is called a basic connection.
A connection rule with the => operator is called a pipe connection.
A connection rule with the ||> operator is called an agent connection.
A connection rule between service names is called a service connection.
In general, connections execute by searching for matches to its trigger. Upon finding a match and associated binding of outermost placeholders, the body of the connection will be generated. The relationships among the events of the body, the triggering events, and other events generated by the connection are dependent upon whether the connection is basic, pipe or agent.
Duality is the Rapide way of denoting the gender of the services. To connect two components together through services, the trigger and body of the rule must be service names and the service named in the body must be of the dual type of the type of the service named in the trigger.
A connection between services is usually bi-directional in the sense that constituents from each service will be action names in a trigger of a connection defined by the service connection.
Optionally, modify the architecture connections. Experiment by using different connections here also. In particular, look at the poset produced using "to" and "||>".n : Integer is Cast( String, Integer, Arguments[1] ); AP : APPLICATION_PROGRAM(n); Rs : RESOURCES(n) is SOME_RESOURCES(n);
Note: If you use raptor to animate main7, you may notice the events in the subarchitecture don't flow to the "right" resource all of the time. We will correct this in the next section. The architecture with variable number of resources (main7.arch) is graphically equivalent to Figure 11 (i.e., main6.arch; in this case, the maximum number of resources that will be created is "2").
Also, modify the behavior replace:action in Init(i : Integer);
to be:start => animation_Iam("RESOURCE");;
(?i : Integer) Init(?i) => animation_Iam("RESOURCE" & Cast(Integer, String, ?i));;
In particular, you may optionally modify (i) rename Resource component as Resource1, and (ii) the interface type associated with the Resource2 component to be RESOURCE instead of RESOURCE2.
animation_Iam => for i : Integer in 1..n generate Rs[i].Init(i) end generate;
Note: include as many lines of the following form as the number of resources in your subarchitecture. The lines indicate the particular RESOURCE, a component of the SOME_RESOURCES architecture, should be initially be invisible.proc per_event { event_idx name parameters caller } { if { $event_idx == "0" } { rpChangeState SOME_RESOURCES RESOURCE invisible -p rpChangeState SOME_RESOURCES RESOURCE2 invisible -p } if { $name == "INIT" } { set id [lindex $parameters 0] set module "RESOURCE$id" rpChangeState SOME_RESOURCES $module normal -p } }
rpChangeState SOME_RESOURCES RESOURCEn invisible -p
Save state and generate the Rapide code with the latest modifications
under the name main8.arch and main8.rpd. Then, compile and execute main8.
Don't forget to add a command line parameter to indicate the number of
resources. Note that the base architecture with dynamically varying number
of resources (main8.arch) is graphically equivalent to Figure 11 (i.e.,
main6.arch).
Actions & Services
Behavioral Declarationsservice Rs(1..n) : AP_R;
Behavioral Rulesaction animation_Iam(name: string), Continue(), Parallel_Requests(), Single_Request(i : Integer); bool : var Boolean; str : var String;
The "[ !i in 1..N rel || ] Rs(!i).Request(!i)" in the above code is what we call an iteration pattern. It will generate "n" request events. Each of the request events will be causally independent from the other request events. Note: all of the request events will causally follow the triggering parallel_requests event.start => animation_Iam("APPLICATION_PROGRAM");; /* After a Start or Continue event, decide to quit or generate either a single resource request or requests to all resources in parallel. */ Start or Continue => IO.Cput("Which resource to use? [-1] quit, [0] all in parallel"); str := IO.Cget(); bool := Can_Cast(String, Integer, $str); while ( not $bool ) do IO.Cput($str & " is not an integer, enter an integer."); str := IO.Cget(); bool := Can_Cast(String, Integer, $str); end do; if ( Cast(String, Integer, $str) = 0 ) then Parallel_Requests(); elsif ( Cast(String, Integer, $str) > 0 ) then Single_Request( Cast(String, Integer, $str) ); end if;; /* After making the decision generate a single resource request. */ (?i : Integer) Single_Request(?i) ||> Rs(?i).Request(?i);; /* After making the decision generate a request to each (1 to N) of the resources in parallel. */ Parallel_Requests ||> [ !i in 1..N rel || ] Rs(!i).Request(!i);; /* If the AP made a single request and received a result back or if the AP made parallel requests and received N results back, then continue. */ ((?i : Integer) Single_Request(?i) ~ Rs(?i).Result(?i)) or (Parallel_Requests ~ [ !i in 1..N rel ~ ] Rs(!i).Result(!i)) ||> Continue();;
Similarly, the "[ !i in 1..N rel ~ ] Rs(!i).Result(!i))" in the above code is another iteration pattern. In this case, we want to match when we don't care what the relationships between the results events are; the compiler will match even when some of the the results events are causally independent from one another and others are causally dependent.