lockfile 1.1.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/rlock +125 -109
- data/bin/rlock-1.3.0 +342 -0
- data/lib/lockfile-1.3.0.rb +518 -0
- data/lib/lockfile.rb +286 -205
- metadata +16 -33
- data/BUGS +0 -3
- data/README +0 -165
- data/VERSION +0 -1
- data/doc/rlock.help +0 -88
- data/install.rb +0 -143
- data/lib/lockfile-1.1.0.rb +0 -437
- data/lockfile-1.1.0.gem +0 -0
- data/lockfile.gemspec +0 -22
- data/samples/a.rb +0 -112
- data/samples/nfsstore.rb +0 -203
- data/samples/test.db +0 -0
- data/samples/test.db~ +0 -0
data/samples/a.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
$:.unshift '../lib'
|
3
|
-
#
|
4
|
-
# puts this script in an nfs located directory and run from a couple of nodes at
|
5
|
-
# once. the list should appear ordered from either host - note that times may
|
6
|
-
# not be ordered depending on the system clocks
|
7
|
-
#
|
8
|
-
|
9
|
-
#
|
10
|
-
# builtin
|
11
|
-
#
|
12
|
-
require 'socket'
|
13
|
-
require 'pstore'
|
14
|
-
#
|
15
|
-
# do what we can to invalidate any nfs caching
|
16
|
-
#
|
17
|
-
def timestamp time = Time.now
|
18
|
-
#{{{
|
19
|
-
usec = "#{ time.usec }"
|
20
|
-
usec << ('0' * (6 - usec.size)) if usec.size < 6
|
21
|
-
time.strftime('%Y-%m-%d %H:%M:%S.') << usec
|
22
|
-
#}}}
|
23
|
-
end
|
24
|
-
def hostname
|
25
|
-
#{{{
|
26
|
-
@__hostname__ ||= Socket::gethostname
|
27
|
-
#}}}
|
28
|
-
end
|
29
|
-
def tmpnam dir = Dir.tmpdir, seed = File.basename($0)
|
30
|
-
#{{{
|
31
|
-
pid = Process.pid
|
32
|
-
path = "%s_%s_%s_%s_%d" %
|
33
|
-
[hostname, seed, pid, timestamp.gsub(/\s+/o,'_'), rand(101010)]
|
34
|
-
File.join(dir, path)
|
35
|
-
#}}}
|
36
|
-
end
|
37
|
-
def uncache file
|
38
|
-
#{{{
|
39
|
-
refresh = nil
|
40
|
-
begin
|
41
|
-
is_a_file = File === file
|
42
|
-
path = (is_a_file ? file.path : file.to_s)
|
43
|
-
stat = (is_a_file ? file.stat : File.stat(file.to_s))
|
44
|
-
refresh = tmpnam(File.dirname(path))
|
45
|
-
File.link path, refresh rescue File.symlink path, refresh
|
46
|
-
File.chmod stat.mode, path
|
47
|
-
File.utime stat.atime, stat.mtime, path
|
48
|
-
ensure
|
49
|
-
begin
|
50
|
-
File.unlink refresh if refresh
|
51
|
-
rescue Errno::ENOENT
|
52
|
-
end
|
53
|
-
end
|
54
|
-
#}}}
|
55
|
-
end
|
56
|
-
#
|
57
|
-
# raa - http://raa.ruby-lang.org/project/lockfile/
|
58
|
-
#
|
59
|
-
require 'lockfile'
|
60
|
-
pstore = PStore.new 'test.db'
|
61
|
-
timeout = 60
|
62
|
-
max_age = 8
|
63
|
-
refresh = 2
|
64
|
-
debug = false
|
65
|
-
lockfile = Lockfile.new 'test.lock',
|
66
|
-
:timeout => timeout,
|
67
|
-
:max_age => max_age,
|
68
|
-
:refresh => refresh,
|
69
|
-
:debug => debug
|
70
|
-
#
|
71
|
-
# flock throws ENOLCK on nfs file systems in newer linux kernels
|
72
|
-
# plus we want to show that lockfile alone can do the locking
|
73
|
-
#
|
74
|
-
class File
|
75
|
-
def flock(*args,&block);true;end
|
76
|
-
end
|
77
|
-
#
|
78
|
-
# if locking does not work this loop will blow up (Marshal load error) or appear
|
79
|
-
# un-ordered. actually it will eventually blow up due to nfs caching - but that
|
80
|
-
# is not the fault of the lockfile class! for the most part it is a simply demo
|
81
|
-
# of locking. the file will never become corrupt, it just will be unreadable at
|
82
|
-
# times due to kernel caching.
|
83
|
-
#
|
84
|
-
loop do
|
85
|
-
lockfile.lock do
|
86
|
-
uncache pstore.path
|
87
|
-
pstore.transaction do
|
88
|
-
#
|
89
|
-
# get/update list
|
90
|
-
#
|
91
|
-
pstore[:list] = [] unless pstore.root? :list
|
92
|
-
list = pstore[:list]
|
93
|
-
tuple = [list.size, hostname, Time.now.to_f]
|
94
|
-
list << tuple
|
95
|
-
#
|
96
|
-
# show last 16 elements
|
97
|
-
#
|
98
|
-
puts '---'
|
99
|
-
list[-([list.size, 16].min)..-1].each{|tuple| p tuple}
|
100
|
-
puts '---'
|
101
|
-
#
|
102
|
-
# keep it a reasonable size
|
103
|
-
#
|
104
|
-
list.shift while list.size > 1024
|
105
|
-
#
|
106
|
-
# write back updates
|
107
|
-
#
|
108
|
-
pstore[:list] = list
|
109
|
-
end
|
110
|
-
end
|
111
|
-
sleep 1
|
112
|
-
end
|
data/samples/nfsstore.rb
DELETED
@@ -1,203 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# How to use:
|
3
|
-
#
|
4
|
-
# db = NFSStore.new("/tmp/foo")
|
5
|
-
# db.transaction do
|
6
|
-
# p db.roots
|
7
|
-
# ary = db["root"] = [1,2,3,4]
|
8
|
-
# ary[0] = [1,1.5]
|
9
|
-
# end
|
10
|
-
|
11
|
-
# db.transaction do
|
12
|
-
# p db["root"]
|
13
|
-
# end
|
14
|
-
|
15
|
-
require "ftools"
|
16
|
-
require "digest/md5"
|
17
|
-
require "socket"
|
18
|
-
|
19
|
-
require 'lockfile'
|
20
|
-
|
21
|
-
class NFSStore
|
22
|
-
HOSTNAME = Socket::gethostname
|
23
|
-
class Error < StandardError
|
24
|
-
end
|
25
|
-
|
26
|
-
def initialize(file)
|
27
|
-
dir = File::dirname(file)
|
28
|
-
unless File::directory? dir
|
29
|
-
raise NFSStore::Error, format("directory %s does not exist", dir)
|
30
|
-
end
|
31
|
-
if File::exist? file and not File::readable? file
|
32
|
-
raise NFSStore::Error, format("file %s not readable", file)
|
33
|
-
end
|
34
|
-
@transaction = false
|
35
|
-
@filename = file
|
36
|
-
@lockfile = Lockfile.new "#{ @filename }.lock",:max_age=>64,:refresh=>8
|
37
|
-
@abort = false
|
38
|
-
end
|
39
|
-
|
40
|
-
def in_transaction
|
41
|
-
raise NFSStore::Error, "not in transaction" unless @transaction
|
42
|
-
end
|
43
|
-
private :in_transaction
|
44
|
-
|
45
|
-
def [](name)
|
46
|
-
in_transaction
|
47
|
-
@table[name]
|
48
|
-
end
|
49
|
-
def fetch(name, default=NFSStore::Error)
|
50
|
-
unless @table.key? name
|
51
|
-
if default==NFSStore::Error
|
52
|
-
raise NFSStore::Error, format("undefined root name `%s'", name)
|
53
|
-
else
|
54
|
-
default
|
55
|
-
end
|
56
|
-
end
|
57
|
-
self[name]
|
58
|
-
end
|
59
|
-
def []=(name, value)
|
60
|
-
in_transaction
|
61
|
-
@table[name] = value
|
62
|
-
end
|
63
|
-
def delete(name)
|
64
|
-
in_transaction
|
65
|
-
@table.delete name
|
66
|
-
end
|
67
|
-
def roots
|
68
|
-
in_transaction
|
69
|
-
@table.keys
|
70
|
-
end
|
71
|
-
def root?(name)
|
72
|
-
in_transaction
|
73
|
-
@table.key? name
|
74
|
-
end
|
75
|
-
def path
|
76
|
-
@filename
|
77
|
-
end
|
78
|
-
def commit
|
79
|
-
in_transaction
|
80
|
-
@abort = false
|
81
|
-
throw :pstore_abort_transaction
|
82
|
-
end
|
83
|
-
def abort
|
84
|
-
in_transaction
|
85
|
-
@abort = true
|
86
|
-
throw :pstore_abort_transaction
|
87
|
-
end
|
88
|
-
|
89
|
-
# do what we can to invalidate any nfs caching
|
90
|
-
def uncache file
|
91
|
-
begin
|
92
|
-
stat = file.stat
|
93
|
-
path = file.path
|
94
|
-
refresh = "%s.%s.%s.%s" % [path, HOSTNAME, $$, Time.now.to_i % 1024]
|
95
|
-
File.link path, refresh
|
96
|
-
file.chmod stat.mode
|
97
|
-
File.utime stat.atime, stat.mtime, path
|
98
|
-
rescue Exception => e
|
99
|
-
warn e
|
100
|
-
ensure
|
101
|
-
begin
|
102
|
-
File.unlink refresh if File.exist? refresh
|
103
|
-
rescue Exception => e
|
104
|
-
warn e
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
|
110
|
-
def transaction(read_only=false)
|
111
|
-
raise NFSStore::Error, "nested transaction" if @transaction
|
112
|
-
file = nil
|
113
|
-
value = nil
|
114
|
-
|
115
|
-
@lockfile.lock do
|
116
|
-
begin
|
117
|
-
@transaction = true
|
118
|
-
backup = @filename+"~"
|
119
|
-
begin
|
120
|
-
file = File::open(@filename, read_only ? "rb" : "rb+")
|
121
|
-
orig = true
|
122
|
-
rescue Errno::ENOENT
|
123
|
-
raise if read_only
|
124
|
-
file = File::open(@filename, "wb+")
|
125
|
-
end
|
126
|
-
#file.flock(read_only ? File::LOCK_SH : File::LOCK_EX)
|
127
|
-
uncache file
|
128
|
-
file.rewind
|
129
|
-
if read_only
|
130
|
-
@table = Marshal::load(file)
|
131
|
-
elsif orig and (content = file.read) != ""
|
132
|
-
@table = Marshal::load(content)
|
133
|
-
size = content.size
|
134
|
-
md5 = Digest::MD5.digest(content)
|
135
|
-
content = nil # unreference huge data
|
136
|
-
else
|
137
|
-
@table = {}
|
138
|
-
end
|
139
|
-
begin
|
140
|
-
catch(:pstore_abort_transaction) do
|
141
|
-
value = yield(self)
|
142
|
-
end
|
143
|
-
rescue Exception
|
144
|
-
@abort = true
|
145
|
-
raise
|
146
|
-
ensure
|
147
|
-
if !read_only and !@abort
|
148
|
-
file.rewind
|
149
|
-
content = Marshal::dump(@table)
|
150
|
-
if !md5 || size != content.size || md5 != Digest::MD5.digest(content)
|
151
|
-
File::copy @filename, backup
|
152
|
-
begin
|
153
|
-
file.write(content)
|
154
|
-
file.truncate(file.pos)
|
155
|
-
content = nil # unreference huge data
|
156
|
-
rescue
|
157
|
-
File::rename backup, @filename if File::exist?(backup)
|
158
|
-
raise
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
if @abort and !orig
|
163
|
-
File.unlink(@filename)
|
164
|
-
end
|
165
|
-
@abort = false
|
166
|
-
end
|
167
|
-
ensure
|
168
|
-
@table = nil
|
169
|
-
@transaction = false
|
170
|
-
file.close if file
|
171
|
-
end
|
172
|
-
end
|
173
|
-
value
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
if __FILE__ == $0
|
186
|
-
db = NFSStore.new("/tmp/foo")
|
187
|
-
db.transaction do
|
188
|
-
p db.roots
|
189
|
-
ary = db["root"] = [1,2,3,4]
|
190
|
-
ary[1] = [1,1.5]
|
191
|
-
end
|
192
|
-
|
193
|
-
1000.times do
|
194
|
-
db.transaction do
|
195
|
-
db["root"][0] += 1
|
196
|
-
p db["root"][0]
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
db.transaction(true) do
|
201
|
-
p db["root"]
|
202
|
-
end
|
203
|
-
end
|
data/samples/test.db
DELETED
Binary file
|
data/samples/test.db~
DELETED
Binary file
|