File Coverage

blib/lib/Pinto/Locker.pm
Criterion Covered Total %
statement 41 41 100.0
branch 8 10 80.0
condition 1 2 50.0
subroutine 9 9 100.0
pod 2 2 100.0
total 61 64 95.3


line stmt bran cond sub pod time code
1             # ABSTRACT: Manage locks to synchronize concurrent operations
2              
3             package Pinto::Locker;
4              
5 51     51   342 use Moose;
  51         91  
  51         396  
6 51     51   319755 use MooseX::StrictConstructor;
  51         118  
  51         497  
7 51     51   162722 use MooseX::MarkAsMethods ( autoclean => 1 );
  51         112  
  51         420  
8              
9 51     51   175104 use Path::Class;
  51         128  
  51         3649  
10 51     51   21417 use File::NFSLock;
  51         113764  
  51         2521  
11              
12 51     51   399 use Pinto::Util qw(debug throw whine);
  51         111  
  51         2670  
13 51     51   307 use Pinto::Types qw(File);
  51         110  
  51         481  
14              
15             #-----------------------------------------------------------------------------
16              
17             our $VERSION = '0.13'; # VERSION
18              
19             #-----------------------------------------------------------------------------
20              
21             our $LOCKFILE_TIMEOUT = $ENV{PINTO_LOCKFILE_TIMEOUT} || 50; # Seconds
22             our $STALE_LOCKFILE_TIMEOUT = $ENV{PINTO_STALE_LOCKFILE_TIMEOUT} || 0; # Seconds
23              
24             #-----------------------------------------------------------------------------
25              
26             has repo => (
27             is => 'ro',
28             isa => 'Pinto::Repository',
29             weak_ref => 1,
30             required => 1,
31             );
32              
33             has _lock => (
34             is => 'rw',
35             isa => 'File::NFSLock',
36             predicate => '_is_locked',
37             clearer => '_clear_lock',
38             init_arg => undef,
39             );
40              
41             #-----------------------------------------------------------------------------
42              
43              
44             sub lock { ## no critic qw(Homonym)
45 533     533 1 10801 my ( $self, $lock_type ) = @_;
46              
47 533 50       18256 return if $self->_is_locked;
48              
49 533   50     1952 $lock_type ||= 'SH';
50              
51 533         2147 local $File::NFSLock::LOCK_EXTENSION = '';
52 533         2105 local @File::NFSLock::CATCH_SIGS = ();
53              
54 533         13481 my $root_dir = $self->repo->config->root_dir;
55 533         3269 my $lock_file = $root_dir->file('.lock')->stringify;
56              
57 533 100       89617 if ($STALE_LOCKFILE_TIMEOUT) {
58 1         13 whine( 'PINTO_STALE_LOCKFILE_TIMEOUT > 0, may steal lock !!');
59             }
60            
61 533 100       6421 my $lock = File::NFSLock->new( $lock_file, $lock_type, $LOCKFILE_TIMEOUT, $STALE_LOCKFILE_TIMEOUT )
62             or throw 'The repository is currently in use -- please try again later (' . $File::NFSLock::errstr . ')';
63              
64 529         407092 debug("Process $$ got $lock_type lock on $root_dir");
65              
66 529         18385 $self->_lock($lock);
67              
68 529         2558 return $self;
69             }
70              
71             #-----------------------------------------------------------------------------
72              
73              
74             sub unlock {
75 593     593 1 12629 my ($self) = @_;
76              
77 593 100       20257 return $self if not $self->_is_locked;
78              
79             # I'm not sure if failure to unlock is really a problem
80 529 50       14792 $self->_lock->unlock or warn 'Unable to unlock repository';
81              
82 529         260763 $self->_clear_lock;
83              
84 529         21433 my $root_dir = $self->repo->config->root_dir;
85 529         6421 debug("Process $$ released the lock on $root_dir");
86              
87 529         2483 return $self;
88             }
89              
90             #------------------------------------------------------------------------------
91              
92             __PACKAGE__->meta->make_immutable;
93              
94             #-----------------------------------------------------------------------------
95             1;
96              
97             __END__
98              
99             =pod
100              
101             =encoding UTF-8
102              
103             =for :stopwords Jeffrey Ryan Thalhammer NFS
104              
105             =head1 NAME
106              
107             Pinto::Locker - Manage locks to synchronize concurrent operations
108              
109             =head1 VERSION
110              
111             version 0.13
112              
113             =head1 DESCRIPTION
114              
115             =head1 METHODS
116              
117             =head2 lock
118              
119             Attempts to get a lock on a Pinto repository. If the repository is already
120             locked, we will attempt to contact the current lock holder and make sure they
121             are really alive. If not, then we will steal the lock. If they are, then
122             we patiently wait until we timeout, which is about 60 seconds.
123              
124             =head2 unlock
125              
126             Releases the lock on the Pinto repository so that other processes can
127             get to work.
128              
129             In many situations, a Pinto repository is a shared resource. At any
130             given moment, multiple processes may be trying to add distributions,
131             remove packages, or pull files from a mirror. To keep things working
132             properly, we can only let one process fiddle with the repository at a
133             time. This module manages a lock file for that purpose.
134              
135             Supposedly, this does work on NFS. But it cannot steal the lock from
136             a dead process if that process was not running on the same host.
137              
138             =head1 AUTHOR
139              
140             Jeffrey Ryan Thalhammer <jeff@stratopan.com>
141              
142             =head1 COPYRIGHT AND LICENSE
143              
144             This software is copyright (c) 2015 by Jeffrey Ryan Thalhammer.
145              
146             This is free software; you can redistribute it and/or modify it under
147             the same terms as the Perl 5 programming language system itself.
148              
149             =cut