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   331 use Moose;
  51         99  
  51         410  
6 51     51   322814 use MooseX::StrictConstructor;
  51         130  
  51         451  
7 51     51   168170 use MooseX::MarkAsMethods ( autoclean => 1 );
  51         116  
  51         449  
8              
9 51     51   170728 use Path::Class;
  51         113  
  51         3798  
10 51     51   21326 use File::NFSLock;
  51         114992  
  51         2339  
11              
12 51     51   387 use Pinto::Util qw(debug throw whine);
  51         105  
  51         2683  
13 51     51   304 use Pinto::Types qw(File);
  51         119  
  51         481  
14              
15             #-----------------------------------------------------------------------------
16              
17             our $VERSION = '0.14'; # 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 9978 my ( $self, $lock_type ) = @_;
46              
47 533 50       16486 return if $self->_is_locked;
48              
49 533   50     1842 $lock_type ||= 'SH';
50              
51 533         2166 local $File::NFSLock::LOCK_EXTENSION = '';
52 533         2221 local @File::NFSLock::CATCH_SIGS = ();
53              
54 533         12382 my $root_dir = $self->repo->config->root_dir;
55 533         3101 my $lock_file = $root_dir->file('.lock')->stringify;
56              
57 533 100       81157 if ($STALE_LOCKFILE_TIMEOUT) {
58 1         9 whine( 'PINTO_STALE_LOCKFILE_TIMEOUT > 0, may steal lock !!');
59             }
60            
61 533 100       5956 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         394684 debug("Process $$ got $lock_type lock on $root_dir");
65              
66 529         18286 $self->_lock($lock);
67              
68 529         2411 return $self;
69             }
70              
71             #-----------------------------------------------------------------------------
72              
73              
74             sub unlock {
75 593     593 1 11560 my ($self) = @_;
76              
77 593 100       18336 return $self if not $self->_is_locked;
78              
79             # I'm not sure if failure to unlock is really a problem
80 529 50       14315 $self->_lock->unlock or warn 'Unable to unlock repository';
81              
82 529         240789 $self->_clear_lock;
83              
84 529         19881 my $root_dir = $self->repo->config->root_dir;
85 529         5727 debug("Process $$ released the lock on $root_dir");
86              
87 529         2212 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.14
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