| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package IPC::ConcurrencyLimit::Lock::Redis; |
|
2
|
1
|
|
|
1
|
|
24499
|
use 5.008001; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
44
|
|
|
3
|
1
|
|
|
1
|
|
7
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
50
|
|
|
4
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
6
|
|
|
|
1
|
|
|
|
|
57
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
|
7
|
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
6
|
use Carp qw(croak); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
82
|
|
|
9
|
1
|
|
|
1
|
|
1089
|
use Redis; |
|
|
1
|
|
|
|
|
158282
|
|
|
|
1
|
|
|
|
|
35
|
|
|
10
|
1
|
|
|
1
|
|
947
|
use Redis::ScriptCache; |
|
|
1
|
|
|
|
|
6424
|
|
|
|
1
|
|
|
|
|
33
|
|
|
11
|
1
|
|
|
1
|
|
8
|
use Digest::SHA1 (); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
16
|
|
|
12
|
1
|
|
|
1
|
|
448
|
use Data::UUID::MT (); |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use Time::HiRes (); |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our $UUIDGenerator = Data::UUID::MT->new(version => "4s"); |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use IPC::ConcurrencyLimit::Lock; |
|
18
|
|
|
|
|
|
|
our @ISA = qw(IPC::ConcurrencyLimit::Lock); |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
use Class::XSAccessor |
|
21
|
|
|
|
|
|
|
getters => [qw( |
|
22
|
|
|
|
|
|
|
redis_conn |
|
23
|
|
|
|
|
|
|
max_procs |
|
24
|
|
|
|
|
|
|
key_name |
|
25
|
|
|
|
|
|
|
proc_info |
|
26
|
|
|
|
|
|
|
script_cache |
|
27
|
|
|
|
|
|
|
uuid |
|
28
|
|
|
|
|
|
|
)]; |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
our $LuaScript_GetLock = q{ |
|
31
|
|
|
|
|
|
|
local key = KEYS[1] |
|
32
|
|
|
|
|
|
|
local max_procs = ARGV[1] |
|
33
|
|
|
|
|
|
|
local proc_info = ARGV[2] |
|
34
|
|
|
|
|
|
|
local i |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
for i = 1, max_procs, 1 do |
|
37
|
|
|
|
|
|
|
local x = redis.call('hexists', key, i) |
|
38
|
|
|
|
|
|
|
if x == 0 then |
|
39
|
|
|
|
|
|
|
redis.call('hset', key, i, proc_info) |
|
40
|
|
|
|
|
|
|
return i |
|
41
|
|
|
|
|
|
|
end |
|
42
|
|
|
|
|
|
|
end |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
return 0 |
|
45
|
|
|
|
|
|
|
}; |
|
46
|
|
|
|
|
|
|
our $LuaScriptHash_GetLock = Digest::SHA1::sha1_hex($LuaScript_GetLock); |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# FIXME should this also check the uuid/etc like ClearOldLock? |
|
49
|
|
|
|
|
|
|
our $LuaScript_ReleaseLock = q{ |
|
50
|
|
|
|
|
|
|
local key = KEYS[1] |
|
51
|
|
|
|
|
|
|
local lockno = ARGV[1] |
|
52
|
|
|
|
|
|
|
redis.call('hdel', key, lockno) |
|
53
|
|
|
|
|
|
|
return 1 |
|
54
|
|
|
|
|
|
|
}; |
|
55
|
|
|
|
|
|
|
our $LuaScriptHash_ReleaseLock = Digest::SHA1::sha1_hex($LuaScript_ReleaseLock); |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# FIXME use this for heartbeat: Generate new data with update time |
|
59
|
|
|
|
|
|
|
our $LuaScript_UpdateUUID = q{ |
|
60
|
|
|
|
|
|
|
local key = KEYS[1] |
|
61
|
|
|
|
|
|
|
local lockno = ARGV[1] |
|
62
|
|
|
|
|
|
|
local olddata = ARGV[2] |
|
63
|
|
|
|
|
|
|
local newdata = ARGV[3] |
|
64
|
|
|
|
|
|
|
if redis.call('hget', key, lockno) == olddata then |
|
65
|
|
|
|
|
|
|
redis.call('hset', key, lockno, newdata) |
|
66
|
|
|
|
|
|
|
return 1 |
|
67
|
|
|
|
|
|
|
end |
|
68
|
|
|
|
|
|
|
return 0 |
|
69
|
|
|
|
|
|
|
}; |
|
70
|
|
|
|
|
|
|
our $LuaScriptHash_UpdateUUID = Digest::SHA1::sha1_hex($LuaScript_UpdateUUID); |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
our $LuaScript_ClearOldLock = q{ |
|
73
|
|
|
|
|
|
|
local key = KEYS[1] |
|
74
|
|
|
|
|
|
|
local id = ARGV[1] |
|
75
|
|
|
|
|
|
|
local proc_info = ARGV[2] |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
local cleared = 0 |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
local x = redis.call('hget', key, id) |
|
80
|
|
|
|
|
|
|
if x == proc_info then |
|
81
|
|
|
|
|
|
|
redis.call('hdel', key, id) |
|
82
|
|
|
|
|
|
|
cleared = 1 |
|
83
|
|
|
|
|
|
|
end |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
return cleared |
|
86
|
|
|
|
|
|
|
}; |
|
87
|
|
|
|
|
|
|
our $LuaScriptHash_ClearOldLock = Digest::SHA1::sha1_hex($LuaScript_ClearOldLock); |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub new { |
|
91
|
|
|
|
|
|
|
my $class = shift; |
|
92
|
|
|
|
|
|
|
my $opt = shift; |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
my $max_procs = $opt->{max_procs} |
|
95
|
|
|
|
|
|
|
or croak("Need a 'max_procs' parameter"); |
|
96
|
|
|
|
|
|
|
my $redis_conn = $opt->{redis_conn} |
|
97
|
|
|
|
|
|
|
or croak("Need a 'redis_conn' parameter"); |
|
98
|
|
|
|
|
|
|
my $key_name = $opt->{key_name} |
|
99
|
|
|
|
|
|
|
or croak("Need a 'key_name' parameter"); |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
my $sc = Redis::ScriptCache->new(redis_conn => $redis_conn); |
|
102
|
|
|
|
|
|
|
$sc->register_script(\$LuaScript_GetLock, $LuaScriptHash_GetLock); |
|
103
|
|
|
|
|
|
|
$sc->register_script(\$LuaScript_ReleaseLock, $LuaScriptHash_ReleaseLock); |
|
104
|
|
|
|
|
|
|
$sc->register_script(\$LuaScript_UpdateUUID, $LuaScriptHash_UpdateUUID); |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
my $proc_info = $opt->{proc_info}; |
|
107
|
|
|
|
|
|
|
$proc_info = '' if not defined $proc_info; |
|
108
|
|
|
|
|
|
|
my $uuid = $UUIDGenerator->create; |
|
109
|
|
|
|
|
|
|
my $self = bless { |
|
110
|
|
|
|
|
|
|
max_procs => $max_procs, |
|
111
|
|
|
|
|
|
|
redis_conn => $redis_conn, |
|
112
|
|
|
|
|
|
|
key_name => $key_name, |
|
113
|
|
|
|
|
|
|
id => undef, |
|
114
|
|
|
|
|
|
|
script_cache => $sc, |
|
115
|
|
|
|
|
|
|
proc_info => $proc_info, |
|
116
|
|
|
|
|
|
|
uuid => $uuid, |
|
117
|
|
|
|
|
|
|
} => $class; |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
$self->_get_lock($key_name, $max_procs, $sc, $uuid . "-" . $proc_info) |
|
120
|
|
|
|
|
|
|
or return undef; |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
return $self; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub _get_lock { |
|
126
|
|
|
|
|
|
|
my ($self, $key, $max_procs, $script_cache, $uuid_proc_info) = @_; |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
my ($rv) = $script_cache->run_script( |
|
129
|
|
|
|
|
|
|
$LuaScriptHash_GetLock, [1, $key, $max_procs, $uuid_proc_info] |
|
130
|
|
|
|
|
|
|
); |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
if (defined $rv and $rv > 0) { |
|
133
|
|
|
|
|
|
|
$self->{id} = $rv; |
|
134
|
|
|
|
|
|
|
return 1; |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
return(); |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub _release_lock { |
|
141
|
|
|
|
|
|
|
my $self = shift; |
|
142
|
|
|
|
|
|
|
my $id = $self->id; |
|
143
|
|
|
|
|
|
|
return if not $id; |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
$self->script_cache->run_script( |
|
146
|
|
|
|
|
|
|
$LuaScriptHash_ReleaseLock, [1, $self->key_name, $id] |
|
147
|
|
|
|
|
|
|
); |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
$self->{id} = undef; |
|
150
|
|
|
|
|
|
|
} |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub _updated_uuid { |
|
153
|
|
|
|
|
|
|
my ($self) = @_; |
|
154
|
|
|
|
|
|
|
my $old_uuid = $self->uuid; |
|
155
|
|
|
|
|
|
|
my $new_uuid = $UUIDGenerator->create; |
|
156
|
|
|
|
|
|
|
substr($new_uuid, 8, 8) = substr($old_uuid, 8, 8); |
|
157
|
|
|
|
|
|
|
vec($new_uuid, 15, 4) = vec($old_uuid, 15, 4); |
|
158
|
|
|
|
|
|
|
return $new_uuid; |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub heartbeat { |
|
162
|
|
|
|
|
|
|
my $self = shift; |
|
163
|
|
|
|
|
|
|
my $conn = $self->redis_conn; |
|
164
|
|
|
|
|
|
|
return() if not $conn; |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
my $proc_info = $self->proc_info; |
|
167
|
|
|
|
|
|
|
my $new_uuid = $self->_updated_uuid; |
|
168
|
|
|
|
|
|
|
my $olddata = $self->uuid . "-" . $proc_info; |
|
169
|
|
|
|
|
|
|
my $newdata = $new_uuid . "-" . $proc_info; |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
my $ok; |
|
172
|
|
|
|
|
|
|
eval { |
|
173
|
|
|
|
|
|
|
$ok = $self->script_cache->run_script( |
|
174
|
|
|
|
|
|
|
$LuaScriptHash_UpdateUUID, |
|
175
|
|
|
|
|
|
|
[ 1, $self->key_name, $self->id, $olddata, $newdata ] |
|
176
|
|
|
|
|
|
|
); |
|
177
|
|
|
|
|
|
|
1 |
|
178
|
|
|
|
|
|
|
} or return(); # server gone away? |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
if (not $ok) { |
|
181
|
|
|
|
|
|
|
return(); # lock was acquired by somebody else? |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
$self->{uuid} = $new_uuid; |
|
185
|
|
|
|
|
|
|
return 1; # probably all fine |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub DESTROY { |
|
189
|
|
|
|
|
|
|
local $@; |
|
190
|
|
|
|
|
|
|
my $self = shift; |
|
191
|
|
|
|
|
|
|
$self->_release_lock(); |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# This is so ugly because we compile slightly different code depending on whether |
|
196
|
|
|
|
|
|
|
# we're running on a perl that can do big-endian-forced-quads or not. |
|
197
|
|
|
|
|
|
|
# FIXME Will work on 64bit perls only. Implementation for 32bit integers welcome. |
|
198
|
|
|
|
|
|
|
# FIXME is this worth it or should it just do a run-time perl version check like heartbeat()? |
|
199
|
|
|
|
|
|
|
eval(<<'PRE' . ($] ge '5.010' ? <<'NEW_PERL' : <<'OLD_PERL') . <<'POST') |
|
200
|
|
|
|
|
|
|
sub clear_old_locks { |
|
201
|
|
|
|
|
|
|
my ($class, $redis_conn, $key_name, $cutoff) = @_; |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
my %hash = $redis_conn->hgetall($key_name); |
|
204
|
|
|
|
|
|
|
return if not keys(%hash); |
|
205
|
|
|
|
|
|
|
my $ncleared = 0; |
|
206
|
|
|
|
|
|
|
foreach my $lockid (keys %hash) { |
|
207
|
|
|
|
|
|
|
PRE |
|
208
|
|
|
|
|
|
|
my ($quad) = unpack("Q>", $hash{$lockid}); |
|
209
|
|
|
|
|
|
|
$quad -= $quad % 16; # 60 bit only |
|
210
|
|
|
|
|
|
|
NEW_PERL |
|
211
|
|
|
|
|
|
|
my ($x, $y) = unpack("N2", $hash{$lockid}); |
|
212
|
|
|
|
|
|
|
$y -= $y % 16; # 60 bit only |
|
213
|
|
|
|
|
|
|
my $quad = $x*2**32 + $y; |
|
214
|
|
|
|
|
|
|
OLD_PERL |
|
215
|
|
|
|
|
|
|
if ($quad/1e7 < $cutoff) { |
|
216
|
|
|
|
|
|
|
$ncleared += $redis_conn->eval($LuaScript_ClearOldLock, 1, $key_name, $lockid, $hash{$lockid}); |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
return $ncleared; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
1 |
|
222
|
|
|
|
|
|
|
POST |
|
223
|
|
|
|
|
|
|
or do { |
|
224
|
|
|
|
|
|
|
my $err = $@ || 'Zombie error'; |
|
225
|
|
|
|
|
|
|
die "Failed to compile clear_old_locks code: $err"; |
|
226
|
|
|
|
|
|
|
}; |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
1; |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
__END__ |