Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 389a5c591b93bef31c75254f9240cb092f94ce3e tadam committed Oct 3, 2011
Showing with 181 additions and 0 deletions.
  1. +5 −0 Changes
  2. +22 −0 dist.ini
  3. +144 −0 lib/Net/ZooKeeper/Lock.pm
  4. +10 −0 t/main.t
@@ -0,0 +1,5 @@
+Revision history for Net-ZooKeeper-Lock
+
+{{$NEXT}}
+ Initial release.
+
@@ -0,0 +1,22 @@
+name = Net-ZooKeper-Lock
+version = 0.01
+author = Yury Zavarin <yury.zavarin@gmail.com>
+license = Perl_5
+copyright_holder = Yury Zavarin
+
+[@Filter]
+bundle = @Classic
+
+[AutoPrereqs]
+[NextRelease]
+format = %-7v %{eee MMM dd, yyyy}d
+
+[MetaResources]
+homepage = http://github.com/tadam/Net-ZooKeeper-Lock
+repository.url = git://github.com/tadam/Net-ZooKeeper-Lock.git
+repository.web = http://github.com/tadam/Net-ZooKeeper-Lock
+repository.type = git
+
+[PodWeaver]
+
+[@Git]
@@ -0,0 +1,144 @@
+package Net::ZooKeeper::Lock;
+
+use strict;
+use warnings;
+
+use Net::ZooKeeper qw(:events :node_flags :acls);
+use Params::Validate qw(:all);
+
+# ABSTRACT: distributed locks via ZooKeeper
+
+=head1 SYNOPSIS
+
+ use Net::ZooKeeper::Lock;
+
+ # take a lock
+ my $lock = Net::ZooKeeper::Lock->new({
+ zkh => Net::ZooKeeper->new(host => 'localhost:2181'),
+ lock_name => 'bar',
+ });
+
+ # release a lock
+ $lock->unlock;
+ # or
+ undef $lock;
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+sub new {
+ my $class = shift;
+ my $p = validate(@_, {
+ zkh => { isa => 'Net::ZooKeeper' },
+ lock_prefix => { type => SCALAR, regex => qr{^/.+}o, default => '/lock' },
+ lock_name => { type => SCALAR, regex => qr{^[^/]+$}o },
+ create_prefix => { type => BOOLEAN, default => 1 },
+ watch_timeout => { type => SCALAR, regex => qr/^\d+$/o, default => 86400 * 1000 },
+ nonblocking => { type => BOOLEAN, default => 0 },
+ });
+ $p->{lock_prefix} =~ s{/$}{};
+
+ my $self = $p;
+ bless $self, $class;
+
+ $self->_lock;
+
+ return $self;
+}
+
+sub unlock {
+ my $self = shift;
+ $self->{zkh}->delete($self->{lock_path}) if ($self->{lock_path});
+}
+
+sub _create_cyclic_path {
+ my ($self, $path) = @_;
+
+ my $current_index = 1;
+ while ($current_index > 0) {
+ $current_index = index($path, "/", $current_index + 1);
+ my $current_path;
+ if ($current_index > 0) {
+ $current_path = substr($path, 0, $current_index);
+ } else {
+ $current_path = $path;
+ }
+
+ if (!$self->{zkh}->exists($path)) {
+ $self->{zkh}->create($path, '0',
+ acl => ZOO_OPEN_ACL_UNSAFE
+ );
+ }
+ }
+}
+sub _lock {
+ my $self = shift;
+
+ my $zkh = $self->{zkh};
+ my $lock_prefix = $self->{lock_prefix};
+ my $lock_name = $self->{lock_name};
+
+ if ($self->{create_prefix}) {
+ $self->_create_cyclic_path($lock_prefix);
+ }
+
+ my $lock_tmpl = $lock_prefix . "/" . $lock_name . "-";
+ my $lock_path = $zkh->create($lock_tmpl, '0',
+ flags => (ZOO_EPHEMERAL | ZOO_SEQUENCE),
+ acl => ZOO_OPEN_ACL_UNSAFE) or
+ die "unable to create sequence znode $lock_tmpl: " . $zkh->get_error . "\n";
+
+ while (1) {
+ my @child_names = $zkh->get_children($lock_prefix);
+ die "no childs\n" unless (scalar(@child_names));
+
+ my @less_than_me = sort
+ grep { ($_ =~ m/^${lock_name}-\d+$/) &&
+ ($lock_prefix . "/" . $_ lt $lock_path) }
+ @child_names;
+
+ unless (@less_than_me) {
+ $self->{lock_path} = $lock_path;
+ return;
+ }
+
+ if ($self->{nonblocking}) {
+ die "lock already taken\n";
+ }
+
+ $self->_exists($lock_prefix . "/" . $less_than_me[-1]);
+ }
+}
+
+sub _exists {
+ my ($self, $path) = @_;
+
+ my $zkh = $self->{zkh};
+
+ my $watcher = $zkh->watch(timeout => $self->{watch_timeout});
+ while (1) {
+ my $exists = $zkh->exists(
+ $path,
+ watch => $watcher,
+ );
+
+ if (!$exists) {
+ next;
+ } else {
+ last;
+ }
+ }
+}
+
+sub DESTROY {
+ local $@;
+
+ my $self = shift;
+
+ $self->unlock;
+};
+
+1;
@@ -0,0 +1,10 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use_ok('Net::ZooKeeper::Lock');
+
+done_testing;

0 comments on commit 389a5c5

Please sign in to comment.