Hi, I posted a bit ago about session locking. I had a half-witted idea
then for how to do it, and now I have something even worse. Now, I'll
share the worse idea :)
Basically, I'm working on a fault-tolerant/HA server that is
multi-master replicated between two peers. Also, their data is
replicated into an SQL db real-time for reporting purposes. There
should never be a high volume of data, so I can easily trade bandwidth
for predictability and reliability.
There is a "main" session, a "server" sesssion, a "client" session. The
server session is a PoCo::Server::TCP, while the client session is a
PoCo::Client::TCP. Each of these works only in half-duplex. This way,
each daemon behaves identically (peer-peer vs. master-slave).
DAEMON A DAEMON B
|---------------| |---------------|
| client(20001) |--->| server(20002) |<--
| | | |
-->| server(20002) |<---| client(20001) |
|---------------| |---------------|
\ /
----------
| RDBMS |
----------
The server takes data from both the peer and from various clients
throughout the network. The client is only for sending replication
events to a peer server.
Now, onto the locking mechanism. Basically, I create a new POE::Session
for managing the locking, and most importantly, causing a call to the
lock to block. Postbacks play a large part in this. Here's the package
.... it's ugly, but currently working.
package Lock;
use POE::Session;
sub new {
my( $type, $resources ) = @_;
our %value = map { $_ => 1 } @$resources; # initialize all resources to 1 (locked)
my $self = { resources => $resources, value => \%value };
$self->{poe_session} = POE::Session->create(
inline_states => {
_start => sub { $_[KERNEL]->alias_set( 'LOCK' ) },
_stop => sub { 1 },
wait => sub {
my($kernel,$heap,$postback,$what) = @_[KERNEL,HEAP,ARG0,ARG1];
if ( $heap->{value}{$what} == 0 ) {
$postback->(); # call the postback since lock is unset
}
# wait for the lock to clear
else { $kernel->delay_set( 'wait', 1, $postback, $what ); }
}
},
heap => { value => \%value }
);
print "new session with id ", $self->{poe_session}->ID(), "\n";
$self->{start_wait} = $self->{poe_session}->postback( 'wait' );
return bless( $self, $type );
}
sub lock {
my( $self, $what ) = @_;
if ( $what eq 'all' ) {
foreach my $part ( keys(%{$self->{value}}) ) {
print "locking $part\n";
$self->{value}{$part}++;
}
}
elsif ( exists($self->{value}{$what}) ) {
$self->{value}{$what}++;
}
else {
die "invalid call to lock( '$what' ) from ".join(' ',caller());
}
print "lock(): $what\n";
}
sub unlock {
my( $self, $what ) = @_;
if ( $what eq 'all' ) {
foreach my $part ( keys(%{$self->{value}}) ) {
print "unlocking $part\n";
die "unlock when not '$part' not locked"
unless ( $self->{value}{$part} > 0 );
$self->{value}{$part}--;
}
}
elsif ( exists($self->{value}{$what}) ) {
die "unlock when not '$what' not locked"
unless ( $self->{value}{$what} > 0 );
$self->{value}{$what}--;
}
else {
die "invalid call to lock( '$what' )";
}
print "unlock(): $what\n";
}
# returns undef if not locked
sub locked {
my( $self, $what ) = @_;
die "invalid call to Lock::locked - improper what '$what'"
if ( !defined($what) || !exists($self->{value}{$what}) );
if ( $self->{value}{$what} == 0 ) {
return undef;
}
else {
return 1;
}
}
# usage: $lock->wait( [postback], [resource name] );
sub wait {
my( $self, $postback, $what ) = @_;
# add a check for valid $what in $self->{value} here
$POE::Kernel::poe_kernel->post( 'LOCK', 'wait', $postback, $what );
}
1;
# EOF
my $lock = Lock->new();
To check if a value is locked, just call $lock->locked( 'name' );. To
have an event block on a lock (without holding up the whole kernel) ,
you'd use $lock->wait( $_[SESSION]->postback( 'my_event', @arguments ),
'client' );.
Of course, if this process were running in threads, it would have to use
lock() or sem(get|op|set|ctl) to protect the %value hash, but since I'm
using a monolithic process, I get away with it.
Anyways, just posting this to see if anybody thinks its stupid or has
use for something like this. Maybe I should look into IKC ...?
-Al Tobey
********************************************************************
This email and any files transmitted with it are confidential
and intended solely for the use of the individual or entity
to whom they are addressed. If you have received this
email in error please notify the Priority Health Information
Services Department at (616) 942-0954.
********************************************************************