NAME

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

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();
  }
  
  # do work

DESCRIPTION

This module requires a Redis server that supports Lua scripting.

This locking strategy uses Redis to implement an IPC::ConcurrencyLimit lock type. This particular Redis-based lock implementation uses a single Redis hash (a hash in a single Redis key) as storage for tracking the locks.

Lock Implementation on the Server

The structure of the lock on the server is not considered an implementation detail, but part of the public interface. So long it is inspected and modified atomically, you can choose to modify it through different channels than the API of this class.

This is important because the lock is released in the lock object's destructor, so if a perl process segfaults or on network failure between the process and Redis then the lock cannot be released! More on that below.

Given a lock "mylock" with a max_procs setting of 5 (default: 1) and three out of five lock instances taken, the lock structure in Redis would look as follows:

  "mylock": {
                "1": "BINARYUUID1-some info",
                "2": "BINARYUUID2-some other",
                "3": "BINARYUUID3-yet other info"
            }

where BINARYUUIDX is understood to be a 128bit/16byte binary UUID whose first 60bits are a microsecond-precision timestamp in binary, from high to low significance bits. See "version 4s" UUIDs as described in Data::UUID::MT.

If subsequently lock number 2 is released, the structure becomes:

  "mylock": {
                "1": "BINARYUUID1-some info",
                "3": "BINARYUUID3-yet other info"
            }

The next lock to be obtained would again use entry number 2. When creating a lock object, you may pass a proc_info parameter. This parameter (string) will be used as the value of the corresponding hash entry after prepending the lock's UUID (So proc_info would be "some info", etc. above). By default, proc_info is the empty string.

The combination of the time-ordered UUID and custom proc_info properties may be used to evict stale locks before attempting to obtain a new lock. The default behaviour of using the current time as part of the UUID allows for expiring old locks if that is good enough for your application. Using PIDs in proc_info could be used to clean out stale locks referring to the same client host, etc.

Most importantly, however, the UUID is (with on certainty bordering probability) unique so that you can use to clearly indicate whether you lost the lock if it changes from under you (cf. heartbeat).

METHODS

new

Given a hash ref with options, attempts to obtain a lock in the pool. On success, returns the lock object, otherwise undef.

Required named parameters:

max_procs

The maximum no. of locks (and thus usually processes) to allow at one time.

redis_conn

A Redis connection object. See Redis.

key_name

Indicates the Redis key to use for storing the lock hash.

Options:

proc_info

If provided, this string will be stored in the value slot for the lock obtained together with a UUID (see above). Defaults to the empty string.

heartbeat

This IPC::ConcurrencyLimit::Lock subclass implements a heartbeat method that check whether the UUID and proc_info on the server is still the same as the UUID and proc_info properties of the object.

Whenever called (successfully), updates the time portion of the UUID to the current time in both client and server.

clear_old_locks

Class method!

Given a Redis connection object, a key name for the lock, and a cutoff time stamp (in seconds, potentially fractional), removes all locks in the given key that are older than the provided cutoff time stamp.

This is only going to work on 64bit perls right now, sorry. Patches welcome.

Since heartbeat() updates the UUID timestamp, the cutoff refers to the most recent heartbeat, not to the original lock creation time. But that is what you usually want anyway.

SEE ALSO

IPC::ConcurrencyLimit and the abstract lock base class IPC::ConcurrencyLimit::Lock.

Redis, the Perl-Redis interface used by this module.

Redis::ScriptCache, which makes Lua-scripting with Redis a bit less work.

Data::UUID::MT, whose "version 4s" UUIDs are used here.

AUTHOR

Steffen Mueller, smueller@cpan.org

COPYRIGHT AND LICENSE

 (C) 2012 Steffen Mueller. All rights reserved.
 
 This code is available under the same license as Perl version
 5.8.1 or higher.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.