IPC-ConcurrencyLimit-Lock-Redis

 view release on metacpan or  search on metacpan

lib/IPC/ConcurrencyLimit/Lock/Redis.pm  view on Meta::CPAN


sub _release_lock {
  my $self = shift;
  my $id = $self->id;
  return if not $id;

  $self->script_cache->run_script(
    'release_lock', [1, $self->key_name, $id]
  );

  $self->{id} = undef;
}

sub _updated_uuid {
  my ($self) = @_;
  my $old_uuid = $self->uuid;
  my $new_uuid = $UUIDGenerator->create;
  substr($new_uuid, 8, 8) = substr($old_uuid, 8, 8);
  vec($new_uuid, 15, 4) = vec($old_uuid, 15, 4);
  return $new_uuid;
}

sub heartbeat {
  my $self = shift;
  my $conn = $self->redis_conn;
  return() if not $conn;

  my $proc_info = $self->proc_info;
  my $new_uuid = $self->_updated_uuid;
  my $olddata = $self->uuid . "-" . $proc_info;
  my $newdata = $new_uuid . "-" . $proc_info;

  my $ok;
  eval {
    $ok = $self->script_cache->run_script(
      'update_uuid',
      [ 1, $self->key_name, $self->id, $olddata, $newdata ]
    );
    1
  } or return(); # server gone away?

  if (not $ok) {
    return(); # lock was acquired by somebody else?
  }

  $self->{uuid} = $new_uuid;
  return 1; # probably all fine
}

sub DESTROY {
  local $@;
  my $self = shift;
  $self->_release_lock();
}


# This is so ugly because we compile slightly different code depending on whether
# we're running on a perl that can do big-endian-forced-quads or not.
# FIXME Will work on 64bit perls only. Implementation for 32bit integers welcome.
# FIXME is this worth it or should it just do a run-time perl version check like heartbeat()?
eval(<<'PRE' . ($] ge '5.010' ? <<'NEW_PERL' : <<'OLD_PERL') . <<'POST')
sub clear_old_locks {
  my ($class, $redis_conn, $key_name, $cutoff) = @_;

  my %hash = $redis_conn->hgetall($key_name);
  return if not keys(%hash);
  my $ncleared = 0;
  foreach my $lockid (keys %hash) {
PRE
    my ($quad) = unpack("Q>", $hash{$lockid});
    $quad -= $quad % 16; # 60 bit only
NEW_PERL
    my ($x, $y) = unpack("N2", $hash{$lockid});
    $y -= $y % 16; # 60 bit only
    my $quad = $x*2**32 + $y;
OLD_PERL
    if ($quad/1e7 < $cutoff) {
      $ncleared += $redis_conn->eval($LuaScript_ClearOldLock, 1, $key_name, $lockid, $hash{$lockid});
    }
  }
  return $ncleared;
}
1
POST
or do {
  my $err = $@ || 'Zombie error';
  die "Failed to compile clear_old_locks code: $err";
};

1;

__END__


=head1 NAME

IPC::ConcurrencyLimit::Lock::Redis - Locking via Redis

=head1 SYNOPSIS

  # see also: IPC::ConcurrencyLimit::Lock
  
  use IPC::ConcurrencyLimit;
  use Redis;
  
  my $redis = Redis->new(server => ...);
  my $limit = IPC::ConcurrencyLimit->new(
    type       => 'Redis',
    max_procs  => 1, # defaults to 1
    redis_conn => $redis,
    key_name   => "mylock",
    # optional value to store. Will be prefixed with UUID (see below)
    # proc_info  => "...",
  );
  
  my $id = $limit->get_lock;
  if (not $id) {
    warn "Couldn't get lock";
    exit();
  }
  



( run in 1.550 second using v1.01-cache-2.11-cpan-8dfa8b56332 )