(Internal classification: major example)
Reference paper: “KALA: Kernel Aspect Language for Advanced Transactions” Johan Fabry, Eric Tanter, Theo D'Hondt, In Elsevier Science of Computer Programs 71(3) P 165 – 180, 2008.
Advanced transaction management
Transactions are the cornerstone of concurrency management in multi-tier distributed systems. Originally designed to provide concurrency management for short and unstructured data accesses to databases, they are however now used outside of this domain. This observation is not new, and significant research has been performed to address the shortcomings of classical transactions through the use of advanced transaction models (ATMS). There are many such advanced models, each specifically tailored to address one shortcoming. To achieve this, ATMS typically relax some of the ACID properties associated with classical transactions, and add new properties to the transaction model, in a structured way. Chrysanthis and Ramamritham defined a formalism, called ACTA 1), that allows for a formal specificiation of how a given model modifies/adds these properties.
The major issue with using an ATMS in an application is how these extra properties can be specified. In other words, how programmer attaches the extra transactional properties to parts of the application code that are ment to run within a transaction.
A KALA program declares transactional properties for a number of transactions in a Java program, based on the life-cycle of a given transaction. As is usual in OO programs that use transactions, the life-cycle of every transaction coincides with the life-cycle of a method. The transaction begins when the method begins, commits when the method ends normally, and aborts if the method ends with a specific type of exception.
Transactional properties
take effect at given times in the life-cycle of the transaction:
properties can be declared to apply at begin time, commit time and
abort time. This is done by placing these declarations, which are KALA
statements, in a begin
block commit
block and abort
block, respectively.
Outside of these blocks, a number of statements can be placed in the preliminaries.
Transactional properties are taken from the ACTA formal
model: views
, delegation
and dependencies
. Each of
these properties is reified as a statement in KALA, respectively view
, del
and dep
statements.
We provide the SDF definition from the ReLAx 2) paper:
KDecl* -> CompilationUnit \\ FQMPattern KBody -> KDecl "{" Prelim? BeginBlock? CommitBlock? AbortBlock? "}" -> KBody PrelimStm* -> Prelim "begin" "{" BlockStm* "}" -> BeginBlock "commit" "{" BlockStm* "}" -> CommitBlock "abort" "{" BlockStm* "}" -> AbortBlock AStartStm -> PrelimStm NamingStm -> PrelimStm NamingStm -> BlockStm DepStm -> BlockStm ViewStm -> BlockStm DelStm -> BlockStm TermStm -> BlockStm "autostart" "(" MethSig ASActuals KBody ")" ";" -> AStartStm "alias" "(" KBinding ")" ";" -> NamingStm "name" "(" KBinding ")" ";" -> NamingStm "groupAdd" "(" KBinding ")" ";" -> NamingStm "dep" "(" JavaId JavaId JavaId ")" ";"-> DepStm "view" "(" Min? JavaId JavaId ")" ";" -> ViewStm "del" "(" JavaId JavaId ")" ";" -> DelStm "terminate" "(" JavaExpr ")" ";" -> TermStm JavaId JavaExpr -> KBinding
A KALA declaration
consists of a signature and body. The
join-point model contains method executions, the signatures are the
pointcut expressions, and the body constitutes advice. The advice
specifies the transactional properties of a set of methods, specified
by the pointcut expressions. Furthermore, within those methods reads
and writes to objects that implement the Resourceable
interface are made transactional.
Dependencies, views and delegation need to be able to denote the
two transactions they affect; therefore there is a need for a variable
binding mechanism. Within KALA code, such a binding is known as an
alias
. An alias is looked up through the use of a global naming
service, which is declared using the alias
statement. This statement takes as argument the alias
for a transaction, i.e. the variable name, and a
Java expression that evaluates to a key that is used to look up the
transaction reference in the name service. This expression, as
all expressions in KALA, has
access to the actual parameters of the method and to aliases which
have already been resolved. Special cases are the alias self
,
which is always bound to the currently running transaction, and the
null transaction
, which is the result of a lookup failure. KALA
statements which have as an argument the null transaction fail
silently. Adding transactions to the naming service is performed using the
name
statement, which takes as argument an alias and a Java
expression that evaluates to the key for the naming service.
When placed in the preliminaries, aliases are looked up and names are registered immediately before the transaction starts. Aliases placed in begin, commit and abort blocks are looked up at that particular moment in the life-cycle of the transaction. The scope of aliases within a KALA declaration follows the usual lexical scoping rules: aliases obtained in the preliminaries of a declaration are accessible thoughout the remainder of the KALA code for that declaration; aliases placed in begin, commit and abort blocks are only accessible there.
KALA provides support for named groups of transactions. A transaction
can be added to a group using the groupAdd
statement. All KALA statements have an overloaded
behavior for groups, e.g. setting a view from a transaction to a group
of transactions implies setting the view to each member of the group.
The only non-obvious case is when a group is a destination of a
delegation statement. As semantically this has no sense –delegating
some changes to a group of transactions–, a failure is produced.
Because dependencies may refer to transactions which have
already ended, it is impossible to perform automatic garbage
collection of names and dependency relationships when transactions
have ended. Instead the KALA programmer is made responsible for such
cleanup operations. This is performed through the terminate
statement, which takes as argument a Java expression. This expression
is resolved to a name of the transaction or group of transactions to
be collected. Termination of transactions can be performed within a
begin
, commit
and abort
block. Note that if a transaction is
terminated when it has not yet ended, it is immediately forced to
rollback.
This model is one of the oldest and arguably the best-known
ATMS. It enables a running transaction T
to have a
number of child transactions T_c
. Each T_c
can view the data used by
T
. This is in contrast to classical transactions, where the data of
T
is not shared with other transactions. T_c
may itself also have
a number of children T_{gc}
, forming a tree of nested transactions.
When a child transaction T_c
commits its data, this data is not
written to the database, but instead it is delegated to its
parent T
, where it becomes part of the data of T
. If a transaction
T_x
is the root of a transaction tree, i.e. it has no parent, its
data is committed to the database when it commits. Another
characteristic of this model is that if a child transaction T_c
aborts, the parent T
is not required to abort, i.e. when it
ends it may choose to either commit or abort.
util.strategy.Hierarchical.child*() { alias(parent Thread.currentThread() ); name(self Thread.currentThread()); groupAdd(self "ChildrenOf"+parent); begin { dep(self wd parent); dep(parent cd self); view(self parent); } commit { del(self parent); name(parent Thread.currentThread()); terminate("ChildrenOf"+self); } abort { name(parent Thread.currentThread()); terminate("ChildrenOf"+self);} }
The first line of the KALA code above is the KALA signature, which identifies the transactional methods. As a result, all data accesses to shared data within these methods (and within methods called by these methods) are included in the transaction.
The view property declared in the code above states
that the current transaction, which is a child transaction, can see
the data of its parent transaction. This property is established when
the transaction begins. The delegation property (del
)
states that upon commit, the child transaction delegates its data
changes to its parent transaction. The dependency self wd parent
states that if the parent aborts before this
transaction ends, then this transaction will be forced to also abort.
parent cd self
states that if the parent wants to commit, it has
to wait until this transaction has ended.
The current thread is first used as a key to
lookup the parent transaction, then to register the current
transaction (overriding the binding), and finally, upon
commit or abort, the parent binding is restored. Also, the code adds the current transaction to the group of children of
the parent
transaction and states that if a nested
transaction finishes (by commit or abort), it terminates
the group of its child transactions.
The language only allows for specific kinds of transactional semantics to be declared at specific life-cycle moments of the transaction. However, expressions used in naming statements run unchecked: any Java expression can be evaluated here. Although these expressions do not have any access to KALA internal state, they can break the program by throwing an exception.
The first implementation of KALA was done in an ad-hoc way, by performing source-code transformation. A second implementation, called ReLAx, was performed as a case study for the capabilities of DSAL infrastructure of the Reflex AOP kernel, and is described in the footnote3). In summary: the syntax of KALA is described in SDF, as we have shown above. This specification is used to generates a parser that produces an object representation of the parsed KALA program. The KALA signature determines where to weave the KALA implementation logic, which is generic. The objectified version of the KALA body is treated as configuration parameters by the implementation logic, resulting in the specified properties being ensured at runtime.