Will perl monitor the commit and rollback actions of transactions?
----- Original Message -----
From: "Perl6 RFC Librarian" <[EMAIL PROTECTED]>
To: <[EMAIL PROTECTED]>
Cc: <[EMAIL PROTECTED]>
Sent: Monday, September 04, 2000 4:35 PM
Subject: RFC 130 (v5) Transaction-enabled variables for Perl6


> This and other RFCs are available on the web at
>   http://dev.perl.org/rfc/
>
> =head1 TITLE
>
> Transaction-enabled variables for Perl6
>
> =head1 VERSION
>
>   Maintainer: Szabó, Balázs <[EMAIL PROTECTED]>
>   Date: 17 Aug 2000
>   Last Modified: 02 Sep 2000
>   Mailing List: [EMAIL PROTECTED]
>   Version: 5
>   Number: 130
>   Status: Developing
>
> =head1 ABSTRACT
>
> Transactions are quite important in a database-enabled application.
> Professional database systems have transaction-handling inside, but there
are
> only a few laguage out there, what supports transactions in variable
level.
>
> In this RFC we will look at how these variables would look like in perl6.
>
> We would like to choose the keyword we want to use for this purposes,
because
> there are a lot of alternatives.
>
> =head1 WHAT'S NEW IN VERSION 5
>
> =over 4
>
> =item *
>
> TRANSACTION-ENABLED TIED VARIABLE EXAMPLE added
>
> =item *
>
> BEGIN_TRANSACTION and TIE_BEGIN_TRANSACTION added
>
> =item *
>
> TIE* renamed to TIE_*
>
> =item *
>
> CHANGES moved to the end of the RFC
>
> =item *
>
> Added alternatives to "trans"
>
> =head1 DESCRIPTION
>
> In short, we have "local" keyword, which changes a value of a variable for
only
> the runtime of the current scope. The transaction-enabled variables should
> start up like "local", but IF the currenct scope reaches at the end, it
then
> copied into the global one.
>
> We need to get a keyword to mark a variable transaction-enabled. I chosed
> "trans" in this ducument, but other suggestions are welcome. Possible
> alternatives are:
>
>   transaction
>   transactional
>   acid
>   atomic
>   onsuccess
>   consistent
>
> The final decision  will be made by the porters, I use "trans" in this
> document.
>
> Preferred syntax:
>
>   sub trans_test { my ($self,@params)=@_;
>     trans $self->{value}=$new_value;
>
>     # ...
>
>     die "Error occured" if $some_error;
>
>     function_call(...)
>
>     # ...
>
>   } ;
>
> Meaning (in semi perl5 syntax):
>
>   sub trans_test {
>     local $self->{value}=$new_value;
>
>     # ...
>
>     die "Error occured" if $ome_erre;
>
>     function_call(...)
>
>     # ...
>
>     global $self->{value}=$self->{value};
>   };
>
> If we want to gain more control and want to maintain easy syntax, we can
use
> another pragma, which sets up the attributes of the isolation of
transaction
> data.  I think the "transaction" pragma could be a good name:
>
>   use transaction (mode => 'lock', timeout=>6000);
>
> Parameters for "use transaction":
>
> =over 4
>
> =item mode
>
> can be:
>
> =over 4
>
> =item simple
>
> No blocking, concurrency control (default).
>
> In a not-threaded environment this causes minimal overhead, and no locking
> overhead at all.
>
> =item lock
>
> Explicitly lock the accessed variables. (shared and exclusive locks used
> between threads).
>
> =item version
>
> This is simlar to the postgres' multi-version concurrency control. It
requires
> more memory, but has a less chance to get into deadlock.
>
> =back
>
> =item timeout
>
> Timeout in ms until we wait for the lock. 0 means nonblocking. If the
timeout
> reached, the system throws an exception.
>
> =item isolation
>
> Transaction isolation level. This can be:
>
> =over 4
>
> =item 0
>
> Read uncommitted
>
> =item 1
>
> Read committed (default)
>
> =item 2
>
> Repeatable read
>
> =item 3
>
> Serializable.
>
> =back
>
> PostgreSQL implements only 1 and 3 AFAIK, so I think we could implment
only
> those levels. Then 0 and 2 will be equal to 1 and 3, but we could keep the
> place for a future implementation.
>
> See the postgreSQL documentation for the details.
>
> =back
>
> =head2 Two phase commit
>
> Two phase commit is the common way to deal with distributed transactions.
Perl
> need an interface to objects and tied variables to deal with these to
become a
> reliable transaction-handler. You can choose to implement these features
in
> your object and your tied variable. If you don't do that, perl will give
you a
> rough default.
>
> At the end of the transaction, 2 different thing can happen: rollback or
> commit. When rollback occured, all the transaction variables must be
rolled
> back. In commit, a two-phase commit procedure has been started.
>
> The first phase is preparing to the commit: check the resources, allocates
> resources to the commit, flushes caches, etc. After that it can decide
wheter
> you can do a commit or not. If all participants send "yes", then the
commit
> phase begins: the coordinator sends "commit" messages to the participants,
and
> the transaction finishes. If any of the participants in the "prepare"
phase
> sends a false value, then the whole transaction need to be rolled back.
>
> How it looks like in perl?
>
> You have objects. Objects can be transaction-enabled, and if you want
that, you
> need to define the following functions as callbacks: COMMIT, ROLLBACK,
PREPARE,
> BEGIN_TRANSACTION. If you have a tied variable, then you can define
callbacks
> for this: TIE_COMMIT, TIE_ROLLBACK, TIE_PREPARE, TIE_BEGIN_TRANSACTION.
These
> can be used to extend an object or a tied variable to transaction-safe. If
you
> don't define PREPARE or TIE_PREPARE, then it will be only a one phase
commit.
> If you don't define COMMIT (or TIE_COMMIT) and ROLLBACK (or TIE_ROLLBACK),
then
> perl will do the simple "copy back the old value on rollback" mechanism,
which
> works well in cases when no multithreading and no special handling is
necessary
> for the data. If you don't define BEGIN_TRANSACTION or
TIE_BEGIN_TRANSACTION,
> then no special initialization performed on "trans" call.
>
> =head2 Tie interface
>
> Adding transaction-enabled property of a tied variable is not
straightforward.
> Imagine you have been tied a hash into a (not transaction-enabled) dbm
file.
> When you fetch, you need to put a shared lock (or version-control) the dbm
file
> or key, when you read, you need to put an exclusive lock, and when the
> transaction ends, you need to release the lock. For this reason, we can
add two
> callback: TIE_COMMIT and TIE_ROLLBACK.
>
> If we don't want to use locking, or want to do an advanced
> transaction-management, we can provide a transaction-id to the callbacks.
This
> can be done with a new package global variable (which is localized in
every
> call), the name can be  $Package::TRANSACTION_ID. We could use a new
parameter,
> but it is not is not so neat, because some of the callbacks (PUSH, POP,
> UNSHIFT, PRINT, PRINTF, etc) are expecting LISTs as an attribute, and this
can
> cause unnecessary rewrite of the tie interface.
>
> Following is the description of the modifications of the tie interface:
>
> =over 4
>
> =item New package global
>
> $Package::TRANSACTION_ID will be a unique identifier of the current
transaction
> (if any).
>
> =item New Callbacks
>
> Some new callbacks required:
>
> =over 4
>
> =item TIE_PREPARE $this
>
> This is the first part of the 2 way commit transaction. This must return
true
> if the variable is prepared for the COMMIT, false otherwise. If this
callback
> is not defined, then the variable lose the right to abort the transaction,
and
> perl implicitly returns 1 in this cases.
>
> =item TIE_COMMIT $this
>
> If it is defined, then it is called after TIE_PREPARE returns 1 for all
the
> transaction-enabled variables in the current scope. This must be used to
commit
> the transaction.
>
> =item TIE_ROLLBACK $this
>
> If it is defined, then it is called at the end of a failed transaction. If
NOT
> defined, then STORE will be called with the old value of the variable.
>
> =item TIE_BEGIN_TRANSACTION $this,$parent_transaction_id
>
> It is called by the system, if the program execution finds a "trans $this"
in
> the program. This is intended to initialize the transaction.
>
> This callback is special, because if you use the "trans" or "local"
keyword in
> this subroutine, this will not last only the end of the sub, but the end
of the
> transaction!
>
> $parent_transaction_id can be used to track the sub-transaction relations.
This
> is the ID of the transaction which contains this transaction. undef if
this is
> the master transaction (has no parent). If a subtransaction is terminated
(and
> the exception is catched), then the proper rollback callbacks are called,
but
> the parent transaction can be successful!
>
> =back
>
> =back
>
> If a package used in "tie" has one of the above callbacks, then perl
_must_
> emulate the transaction in every call, so a simple FETCH in
non-transaction
> enironment must be the sequence of TIE_BEGIN_TRANSACTION, FETCH,
TIE_PREPARE ?
> TIE_COMMIT : TIE_ROLLBACK and a simple STORE must be:
TIE_BEGIN_TRANSACTION,
> STORE, TIE_PREPARE ? TIE_COMMIT : TIE_ROLLBACK.
>
> =head2 Object interface
>
> Object interface is similar to the tied interface: you will need
callbacks:
> PREPARE, COMMIT, ROLLBACK and BEGIN_TRANSACTION. These will do the same as
> described in the Tie interface. The $Package::TRANSACTION_ID will be set
in
> this case also.
>
> Note, if you declare an object as "trans", this means that this is
localized
> for the runtime of the transaction and that PREPARE, COMMIT, ROLLBACK will
be
> called at the end of the block of the declaration. It doesn't mean that
all the
> data structure under that is transaction safe. It cannot be guaranteed,
and you
> need to explicitly declare them as "trans" variables.
>
> =head1 TRANSACTION-ENABLED TIED VARIABLE EXAMPLE
>
> This is an example of a transaction-enabled tie interface.
>
> The following package can be tied to any variable, and can be used as a
> persistent, transaction-enabled data.
>
> Usage:
>
>   tie $scalar, "Transaction::ScalarFile", $filename;
>
>   sub my_transaction {
>     trans $scalar;
>     $scalar="Perl" x 1024;
>
>     ...
>
>   };
>
> The data in the file referred by $filename can be accessed, modified as
> $scalar. $scalar can be used in a transaction, supports subtransactions,
and
> supports two-phase commits and locks the accessed file with flock(), so it
can
> be used in multithreaded and multiprocess environment.
>
> Here is the code:
>
>   package Transaction::ScalarFile;
>   use transaction (mode => 'lock', timeout => 30);
>   use strict;
>   use Fcntl qw( :flock );
>
>   # constant declaration
>   sub FILENAME        { 0; };
>   sub FILEHANDLE      { 1; };
>   sub VALUE           { 2; };
>   sub LOCKED          { 3; };
>   sub PARENT_TRANS    { 4; };
>   sub TEMP_FILENAME   { 5; };
>   sub TEMP_FILEHANDLE { 6; };
>
>   sub TIE_BEGIN_TRANSACTION { my ($s,$parent)=@_;
>     trans $s->[VALUE];  # The value is transaction-enabled
>     local $s->[PARENT_TRANS]=$parent;
>   };
>
>   sub TIESCALAR { my ($class,$filename)=@_;
>     my $s=[$filename];
>     bless $s,ref($class) || $class;
>     $s;
>   };
>
>   sub FETCH { my ($s)=@_;
>     return $s->[VALUE] if defined $s->[VALUE];
>     $s->open or return undef;
>     flock $s->[FILEHANDLE], LOCK_SH;
>     local $/=undef;
>     $s->[LOCKED]=1;
>     return $s->[VALUE]= < $s->[FILEHANDLE] >;
>   };
>
>   sub STORE { my ($s,$value)=@_;
>     if ($s->[LOCKED]<=1) {
>       $s->open or return undef;
>       flock $s->[FILEHANDLE], LOCK_EX;
>       $s->[LOCKED]=2;
>     };
>     $s->[VALUE]=$value;
>   };
>
>   sub TIE_PREPARE { my ($s)=@_;
>     # If it is a subtransaction: do nothing
>     return 1 if $s->[PARENT_TRANS];
>     # If the value is not modified: do nothing
>     return 1 if $s->[LOCKED]<2;
>     # Transaction failed if I cannot make the temp file
>     $s->create_and_lock_temp_file or return 0;
>     $!=0; # reset ERRNO
>     # Writes the new value to the tempfile
>     print $s->[TEMP_FILEHANDLE], $s->[VALUE];
>     # Transaction failed if error occured
>     unlink($s->[TEMP_FILENAME]),return 0 if $!;
>     return 1;
>   };
>
>   sub TIE_COMMIT { my ($s)=@_;
>     return if $s->[PARENT_TRANS];
>     # This is the weakest point of the transaction, we cannot make those
two
>     # operations atomic ...
>     rename $s->[FILENAME], $s->[FILENAME].".old.$$";
>     rename $s->[TEMP_FILENAME],$s->[FILENAME];
>     unlink $s->[FILENAME].".old.$$";
>     flock $s->[FILEHANDLE],LOCK_UN;
>     flock $s->[TEMP_FILEHANDLE],LOCK_UN;
>     $s->[LOCKED]=0;
>     $s->[TEMP_FILEHANDLE] = $s->[TEMP_FILENAME] = undef;
>   };
>
>   sub TIE_ROLLBACK { my ($s)=@_;
>     return if $s->[PARENT_TRANS]; # We don't care if it has parent trans.
>     flock $s->[TEMP_FIELHANDLE], LOCK_UN;
>     flock $s->[FILEHANDLE], LOCK_UN;
>     unlink $s->[TEMP_FILENAME];
>     $s->[TEMP_FILEHANDLE] = $s->[TEMP_FILENAME] = undef;
>   };
>
>   sub open { my ($s)=@_;
>     return $s->[FILEHANDLE]=new FileHandle("<".$s->[FILENAME]);
>   };
>
>   sub create_and_lock_temp_file { my ($s)=@_;
>     $s->[TEMP_FILENAME]=$s->[FILENAME].".trans.$$";
>     $s->[TEMP_FILEHANDLE]=new FileHandle(">".$s->[TEMP_FILENAME) or return
0;
>     flock $s->[TEMP_FILEHANDLE],LOCK_EX;
>   };
>
> =head1 IMPLEMENTATION
>
> =head2 Transaction handling methods
>
> =over 4
>
> =item simple
>
> This is the default method. This needs no magic, implementation is
> straightforward:
>
> When you use "trans", then the following will happen:
>
>   $save_value=$value;
>
> When you reaches the end of the block you are in, the saved value should
be
> dropped. If it was an exception that caused the termination of the block,
then
> the old value must be copied back to the global space:
>
>   $value=$save_value;
>
> =item lock
>
> We need to maintain locks (mutexes) on variables. We assume this will be
used
> in threaded applications.
>
> When we use "trans", then perl will put a shared lock on the variable.
>
> When we read the variable, we also put a shared lock to that.
>
> When we write the variable, we check if it is already locked, and if we
locked
> that already or no exclusive locks present, then write to the value, and
lock
> that with LOCK_EX. If other exclusive lock present on the variable, then
we
> need to wait for the releasing.
>
> When the "trans" content ends, we frees the shared (or exclusive lock).
If the
> content ends with a die then we puts the original value back if we have
locked
> it with exclusive lock.
>
> =item version
>
> It is the mechanism of making multiple versioned copies of the variable
every
> time somebody access this. This needs tiestamping, and postgreSQL-like
> concurrency control. I don't know more details.
>
> =back
>
> =head1 CHANGES IN PREVIOUS VERSIONS
>
> =head2 Version 4
>
> =over 4
>
> =item *
>
> TIE interface callbacks are renamed to TIE_COMMIT, TIE_ROLLBACK
>
> =item *
>
> 2 phase commit described
>
> =item *
>
> PREPARE, TIE_PREPARE added to support two phase commits
>
> =item *
>
> Object interface described
>
> =item *
>
> "safe" renamed to "trans"
>
> =back
>
> =head2 Version 3
>
> =over 4
>
> =item *
>
> Added a tie interface change request: COMMIT and ROLLBACK, and new global
>
> =item *
>
> Fixed some Formatting error of this pod.
>
> =item *
>
> 'use varlock' renamed to 'use transaction'.
>
> =back
>
> =head2 Version 2
>
> =over 4
>
> =item *
>
> Detailed implementation description
>
> =item *
>
> Add a new pragma 'varlock' for controlling the concurrency control.
>
> =back
>
> =head1 REFERENCES
>
> PostgreSQL Multi-version concurrency control
>     http://www.postgresql.org/docs/postgres/mvcc.htm
>
> Two phase commit: (Google found that :-)
>     http://oradoc.photo.net/ora8doc/DOC/server803/A54653_01/ds_ch3.htm
>
> RFC 19: Rename the C<local> operator
>
> RFC 119: object neutral error handling via exceptions
>
> perldoc perlthread: the perl5 threading interface
>
> perldoc perltie: the perl5 tie interface
>
>


Reply via email to