thoughtafter-lockfile 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/samples/a.rb ADDED
@@ -0,0 +1,112 @@
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/lock ADDED
@@ -0,0 +1,4 @@
1
+ host: jib.ngdc.noaa.gov
2
+ pid: 18176
3
+ ppid: 5245
4
+ time: 2004-11-18 14:40:43.218731
data/samples/lock.sh ADDED
@@ -0,0 +1,13 @@
1
+ #!/bin/sh
2
+
3
+ export RUBYLIB="../lib:./lib"
4
+
5
+ export PATH="../bin:./bin:$PATH"
6
+
7
+ #export LOCKFILE_DEBUG=1
8
+
9
+ (r=`ruby -e 'p(rand(2))'`; sleep $r; rlock ./lockfile 'sleep 1; printf "\n$$ aquired lock @ `date`\n\n"') &
10
+ (r=`ruby -e 'p(rand(2))'`; sleep $r; rlock ./lockfile 'sleep 1; printf "\n$$ aquired lock @ `date`\n\n"') &
11
+ (r=`ruby -e 'p(rand(2))'`; sleep $r; rlock ./lockfile 'sleep 1; printf "\n$$ aquired lock @ `date`\n\n"') &
12
+
13
+ wait
data/samples/lockfile ADDED
@@ -0,0 +1,4 @@
1
+ host: jib.ngdc.noaa.gov
2
+ pid: 18193
3
+ ppid: 5245
4
+ time: 2004-11-18 14:41:59.320552
@@ -0,0 +1,203 @@
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/out ADDED
@@ -0,0 +1,80 @@
1
+ lock_id <
2
+ {"pid"=>"15681", "time"=>"2004-11-16 17:08:42.931418", "host"=>"jib.ngdc.noaa.gov", "ppid"=>"15674"}
3
+ >
4
+ attempting to lock <./lockfile>...
5
+ polling attempt <0>...
6
+ aquired lock <./lockfile>
7
+ touched <./lockfile> @ <1100650122.93628>
8
+ loaded <
9
+ {"pid"=>"15681", "time"=>"2004-11-16 17:08:42.931418", "host"=>"jib.ngdc.noaa.gov", "ppid"=>"15674"}
10
+ >
11
+
12
+
13
+
14
+ 15682 aquired lock @ Tue Nov 16 17:08:43 MST 2004
15
+
16
+
17
+ lock_id <
18
+ {"pid"=>"15685", "time"=>"2004-11-16 17:08:43.962744", "host"=>"jib.ngdc.noaa.gov", "ppid"=>"15676"}
19
+ >
20
+ attempting to lock <./lockfile>...
21
+ polling attempt <0>...
22
+ poll sleep <0.08>...
23
+ lock_id <
24
+ {"pid"=>"15684", "time"=>"2004-11-16 17:08:43.964636", "host"=>"jib.ngdc.noaa.gov", "ppid"=>"15672"}
25
+ >
26
+ attempting to lock <./lockfile>...
27
+ polling attempt <0>...
28
+ poll sleep <0.08>...
29
+ polling attempt <1>...polling attempt <1>...
30
+
31
+ aquired lock <./lockfile>
32
+ poll sleep <0.08>...
33
+ touched <./lockfile> @ <1100650124.04751>
34
+ loaded <
35
+ {"pid"=>"15685", "time"=>"2004-11-16 17:08:43.962744", "host"=>"jib.ngdc.noaa.gov", "ppid"=>"15676"}
36
+ >
37
+ polling attempt <2>...
38
+ poll sleep <0.08>...
39
+ polling attempt <3>...
40
+ poll sleep <0.08>...
41
+ polling attempt <4>...
42
+ poll sleep <0.08>...
43
+ polling attempt <5>...
44
+ poll sleep <0.08>...
45
+ polling attempt <6>...
46
+ poll sleep <0.08>...
47
+ polling attempt <7>...
48
+ poll sleep <0.08>...
49
+ polling attempt <8>...
50
+ poll sleep <0.08>...
51
+ polling attempt <9>...
52
+ poll sleep <0.08>...
53
+ polling attempt <10>...
54
+ poll sleep <0.08>...
55
+ polling attempt <11>...
56
+ poll sleep <0.08>...
57
+ polling attempt <12>...
58
+ poll sleep <0.08>...
59
+ polling attempt <13>...
60
+ poll sleep <0.08>...
61
+
62
+
63
+
64
+ 15690 aquired lock @ Tue Nov 16 17:08:45 MST 2004
65
+
66
+
67
+ polling attempt <14>...
68
+ poll sleep <0.08>...
69
+ polling attempt <15>...
70
+ aquired lock <./lockfile>
71
+ touched <./lockfile> @ <1100650125.16655>
72
+ loaded <
73
+ {"pid"=>"15684", "time"=>"2004-11-16 17:08:43.964636", "host"=>"jib.ngdc.noaa.gov", "ppid"=>"15672"}
74
+ >
75
+
76
+
77
+
78
+ 15694 aquired lock @ Tue Nov 16 17:08:46 MST 2004
79
+
80
+
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thoughtafter-lockfile
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 2
8
+ - 0
9
+ - 0
10
+ version: 2.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Ara T. Howard
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-01 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description:
23
+ email: ara.t.howard@gmail.com
24
+ executables:
25
+ - rlock
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - bin/rlock
32
+ - doc/rlock.help
33
+ - lib/lockfile.rb
34
+ - rakefile
35
+ - readme.erb
36
+ - samples/a.rb
37
+ - samples/lock
38
+ - samples/lock.sh
39
+ - samples/lockfile
40
+ - samples/nfsstore.rb
41
+ - samples/out
42
+ has_rdoc: true
43
+ homepage: http://github.com/ahoward/lockfile/tree/master
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options: []
48
+
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ hash: 3
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ requirements: []
70
+
71
+ rubyforge_project: codeforpeople
72
+ rubygems_version: 1.3.7
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: lockfile
76
+ test_files: []
77
+