last revised 98/7/10
Atomicity is a critical property of transaction processing systems. Atomicity is based upon the notion of grouping primitive actions to form larger, atomic transactions. Primitive actions are assumed to have behaviors that are not decomposable, i.e., atomic. Atomic operations are operations that are performed entirely or not at all; they cannot be only partially done at termination. Thus, transactions are not guaranteed to execute to completion, but instead are assured to be performed entirely or not at all.
Also, the service that is shared among the application program and the resources has to be enriched to not only allow the application to make requests of the resources, but also instruct them whether they should commit the performance of the request or to undo the performance, called rollback.
Atomicity can be expressed via the following constraint:
never (?x : Xid; ?rm1, ?rm2 : Resource_Manager)
?rm1.Commit_retn(?x,ok) ~ ?rm2.Rollback_retn(?x,ok);
Which means: A transaction, ?x, should never be committed by one
resource, ?rm1, and rolled back by another resource, ?rm2.
In particular, the control logic for coordinating the resources to implement the atomicity property are abstracted into a transaction manager component. This saves each application program from having to implement the control logic.
architecture System_A(NumRMs : Integer) is
AP_A : Application_Program_A(NumRMs);
TM_A : Transaction_Manager_A(NumRMs);
RMs_A : Resource_Managers_A(NumRMs)
is Some_RMs_A(NumRMs);
connect
AP_A.TM ||> TM_A.AP;
for i : Integer in 1..NumRMs generate
AP_A.RMs(i) ||> RMs_A.RMs(i).AP;
TM_A.RMs(i) ||> RMs_A.RMs(i).TM;
end generate;
end architecture System_A;
type Application_Program_A(NumRMs : Integer) is
interface
service (1)
RMs(1..NumRMs) : dual AP_RM_A;
TM : dual AP_TM_A;
behavior
action (1)
Continue(),
Parallel_Requests(x : Xid),
Single_Request(x : Xid; i : Integer);
bool : var Boolean;
str : var String;
begin
/*
After a Start or Continue event, decide what requests to perform and ask the TM for an xid.
*/
Start or Continue =>
IO.Cput("Which resource to use? [-1] quit, [0] all in parallel");
str := IO.Cget();
bool := Is_Image(Integer, $str);
while ( not $bool ) do
IO.Cput($str & " is not an integer, enter an integer.");
str := IO.Cget();
bool := Is_Image(Integer, $str);
end do;
if ( Value(Integer, $str) >= 0 ) then
TM.Begin_call();
end if;;
/*
After getting an xid, decide to quit or generate either a single resource request or requests to all resources in parallel.
*/
(?x : Xid) TM.Begin_retn(?x, ok) =>
if ( Value(Integer, $str) = 0 ) then
Parallel_Requests(?x);
elsif ( Value(Integer, $str) > 0 ) then
Single_Request(?x, Value(Integer, $str) );
end if;;
/*
After making the decision generate a single resource request.
*/
(?x : Xid; ?i : Integer) Single_Request(?x, ?i) ||> (1)
RMs(?i).Request(?x);;
/*
After making the decision generate a request to each (1 to NumRMs) of the resources in parallel.
*/
(?x : Xid) Parallel_Requests(?x) ||> (1)
[!i in 1..NumRMs rel ||] RMs(!i).Request(?x);;
/*
If the AP made a single request and received a result back or if the AP made parallel requests and received NumRMs results back, then continue.
*/
(?x : Xid) (1)
(( (?i : Integer) Single_Request(?x, ?i)
~ RMs(?i).Results(?x))
or
( Parallel_Requests(?x)
~ [!i in 1..NumRMs rel ~] RMs(!i).Results(?x) ))
||>
TM.Commit_call(?x);;
TM.Commit_retn ||> Continue();;
end interface Application_Program_A;
type Resource_Managers_A(NumRMs : Integer) is
interface
service
RMs(1..NumRMs) : Resource_Manager_A;
end interface Resource_Managers_A;
type Resource_Manager_A is
interface
service
AP : AP_RM_A;
TM : TM_RM_A;
behavior
begin
(?x : Xid) AP.Request(?x) ||> AP.Results(?x);;
(?x : Xid) TM.Prepare_call(?x) ||> TM.Prepare_retn(?x, ok);;
(?x : Xid) TM.Commit_call(?x) ||> TM.Commit_retn(?x, ok);;
(?x : Xid) TM.Rollback_call(?x) ||> TM.Rollback_retn(?x, ok);;
end interface Resource_Manager_A;
architecture Some_RMs_A(NumRMs : Integer)
return Resource_Managers_A(NumRMs) is
RM_array : array[Integer] of Resource_Manager_A
is (1..NumRMs, default is new(Resource_Manager_A));
connect
for i : Integer in 1..NumRMs generate
RMs(i).AP ||> RM_array[i].AP;(1)
RMs(i).TM ||> RM_array[i].TM;(1)
end generate;
end architecture Some_RMs_A;
type Transaction_Manager_A(NumRMS : Integer) is
interface
service
AP : AP_TM_A;
RMs(1..NumRMs) : dual TM_RM_A;
constraint
never (?a, ?b : Integer; ?x : Xid) (1)
RMs(?a).Commit_retn(?x, ok)
~ RMs(?b).Rollback_retn(?x, ok);
never (?a, ?b : Integer; ?x : Xid; ?r : Return_Code) (1)
RMs(?a).Prepare_retn(?x, ?r)
|| RMs(?b).Commit_call(?x);
behavior
Num : Integer is 3; // Used to break the implmentation
last_xid : var Xid := 0;
function New_Xid_t() return Xid is
begin
last_xid := $last_xid + 1;
return $last_xid;
end function New_Xid_t;
x : var Xid;
begin
AP.Begin_call() ||>
x := New_Xid_t();
AP.Begin_retn($x, ok);;
(?x : Xid)
AP.Commit_call(?x) ||>
[!i in 1..NumRMs rel ||] RMs(!i).Prepare_call(?x);;
(?x : Xid) (1)
[!i in 1..Num rel ~] RMs(!i).Prepare_retn(?x, ok)
||>
[!i in 1..NumRMs rel ||] RMs(!i).Commit_call(?x);;
(?x : Xid) (1)
( ( (?j : Integer) RMs(?j).Prepare_retn(?x, error) ) union
( [!i in 1..Num rel ~]
((?r : Return_Code) RMs(!i).Prepare_retn(?x, ?r)) ) )
||>
[!i in 1..NumRMs rel ||] RMs(!i).Rollback_call(?x);;
(?x : Xid) (1)
[!i in 1..NumRMs rel ~]( RMs(!i).Commit_retn(?x, ok) )
||>
AP.Commit_retn(?x, ok);;
(?x : Xid) (1)
[!i in 1..NumRMs rel ~]( RMs(!i).Rollback_retn(?x, ok) )
||>
AP.Rollback_retn(?x, ok);;
end interface Transaction_Manager_A;
type AP_RM_A is
interface
action
in Request(x : Xid); (1)
out Results(x : Xid); (1)
end interface AP_RM_A;
type AP_TM_A is
interface
action
in Begin_call ();
out Begin_retn (x : Xid; rc : Return_Code);
in Commit_call (x : Xid);
out Commit_retn (x : Xid; rc : Return_Code);
in Rollback_call(x : Xid);
out Rollback_retn(x : Xid; rc : Return_Code);
end interface AP_TM_A;
type TM_RM_A is
interface
action
in Prepare_call(x : Xid);
out Prepare_retn (x : Xid; rc : Return_Code); (1)
in Commit_call(x : Xid); (1)
out Commit_retn (x : Xid; rc : Return_Code); (1)
in Rollback_call(x : Xid);
out Rollback_retn(x : Xid; rc : Return_Code); (1)
end interface TM_RM_A;
type Xid is Integer;
type Return_Code is enum ok, error end enum Return_Code;
map Map_A_to_FC(N : Integer)
from D : System_A
to System_FC_nb(N)
is
rule
D.AP_A.Continue ||> AP_FC.Continue();;
D.AP_A.Parallel_Requests ||> AP_FC.Parallel_Requests();;
(?x : Xid; ?i : Integer)
D.AP_A.Single_Request(?x, ?i)
||>
AP_FC.Single_Request(?i);;
(?i : Integer; ?x : Xid)
D.AP_A.RMs(?i).Request(?x, ?i)
||>
AP_FC.Rsrcs(?i).Request();;
(?i : Integer; ?x : Xid)
D.RMs_A.RMs(?i).AP.Request(?x, ?i)
||>
Rs_FC.Rsrcs(?i).AP.Request();;
(?i : Integer; ?x : Xid)
Resource_Manager_A::AP.Results(?x, ?i)
||>
Rs_FC.Rsrcs(?i).AP.Results(performer is Rs_FC.Rsrcs(?i));;
(?i : Integer; ?x : Xid)
D.RMs_A.RMs(?i).AP.Results(?x, ?i)
||>
Rs_FC.Rsrcs(?i).AP.Results();;
(?i : Integer; ?x : Xid)
D.AP_A.RMs(?i).Results(?x, ?i)
||>
AP_FC.Rsrcs(?i).Results();;
end map Map_A_to_FC;
architecture Main_A() is
N : Integer is String_To_Integer(Arguments[1]);
S : Root is System_A(N);
M is map Map_A_to_FC(N, S);
connect
end architecture Main_A;
The previous architectural description is executable, and upon execution the following history was produced. This history reflects the user's choice of performing a single request, parallel requests, and then another single request.
Running the animation is not available via html. The architecture file for the animation is here .
The Rapide 1.0 compiler is limited to performing only a limited form of dynamic subtype checking and pattern matching. These limitations are signification in the application program here and in the map here.
A workaround for these bugs require the following additional action definitions placed here.
action
in Continue(),
Parallel_Requests(x : Xid),
Single_Request(x : Xid; i : Integer);
and the removal of the corresponding action definitions in the
behavior here.
A workaround for this bug requires the behavioral rule placed here to be replaced with:
(?x : Xid; ?i : Integer) Single_Request(?x, ?i) ||> RMs(?i).Request(?x, ?i);;
and a similar replacement here to be replaced with:
(?x : Xid) Parallel_Requests(?x) ||> [!i in 1..NumRMs rel ||] RMs(!i).Request(?x, !i);;
and a similar replacement here to be replaced with:
(?x : Xid) (( (?i : Integer) Single_Request(?x, ?i) ~ RMs(?i).Results(?x, ?i) ) or ( Parallel_Requests(?x) ~ [!i in 1..NumRMs rel ~] RMs(!i).Results(?x, !i) )) ||> TM.Commit_call(?x);;
A workaround for this bug requires this additional connection rules placed here:
(?x : Xid) RMs(i).AP.Request(?x) ||> RM_array[i].AP.Request(?x); (?x : Xid) RM_array[i].AP.Results(?x) ||> RMs(i).AP.Results(?x, i);
A workaround for this bug requires this additional connection rules placed here:
(?x : Xid) RMs(i).TM.Prepare_call(?x) ||> RM_array[i].TM.Prepare_call(?x); (?x : Xid; ?c : Return_Code) RM_array[i].TM.Prepare_retn(?x, ?c) ||> RMs(i).TM.Prepare_retn(?x, ?c, i); (?x : Xid) RMs(i).TM.Commit_call(?x) ||> RM_array[i].TM.Commit_call(?x); (?x : Xid; ?c : Return_Code) RM_array[i].TM.Commit_retn(?x, ?c) ||> RMs(i).TM.Commit_retn(?x, ?c, i); (?x : Xid) RMs(i).TM.Rollback_call(?x) ||> RM_array[i].TM.Rollback_call(?x); (?x : Xid; ?c : Return_Code) RM_array[i].TM.Rollback_retn(?x, ?c) ||> RMs(i).TM.Rollback_retn(?x, ?c, i);
The workaround also requires the constraints defined here and here to be modified as follows:
never (?a, ?b : Integer; ?x : Xid)
RMs(?a).Commit_retn(?x, ok, ?a)
~ RMs(?b).Rollback_retn(?x, ok, ?b);
never (?a, ?b : Integer; ?x : Xid; ?r : Return_Code)
RMs(?a).Prepare_retn(?x, ?r, ?a)
|| RMs(?b).Commit_call(?x, ?b);
The workaround also requires the rule defined here to be modified to:
(?x : Xid) [!i in 1..Num rel ~] RMs(!i).Prepare_retn(?x, ok, !i) ||> [!i in 1..NumRMs rel ||] RMs(!i).Commit_call(?x, !i);;
The workaround also requires the rule defined here to be modified to:
(?x : Xid)
( ( (?j : Integer) RMs(?j).Prepare_retn(?x, error, ?j) ) union
( [!i in 1..Num rel ~]
((?r : Return_Code) RMs(!i).Prepare_retn(?x, ?r, !i)) ) )
||>
[!i in 1..NumRMs rel ||] RMs(!i).Rollback_call(?x);;
The workaround also requires the rule defined here to be modified to:
(?x : Xid) [!i in 1..NumRMs rel ~]( RMs(!i).Commit_retn(?x, ok, !i) ) ||> AP.Commit_retn(?x, ok);;
The workaround also requires the rule defined here to be modified to:
(?x : Xid) [!i in 1..NumRMs rel ~]( RMs(!i).Rollback_retn(?x, ok, !i) ) ||> AP.Rollback_retn(?x, ok);;
As well as replacing the action declarations placed here and here with:
in Request(x : Xid; i : Integer); out Results(x : Xid; i : Integer);
As well as replacing the action declarations placed here, here, here, and here with:
out Prepare_retn (x : Xid; rc : Return_Code; i : Integer); in Commit_call(x : Xid; i : Integer); out Commit_retn (x : Xid; rc : Return_Code; i : Integer); out Rollback_retn(x : Xid; rc : Return_Code; i : Integer);