higgs 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +208 -0
- data/LICENSE +26 -0
- data/README +2 -0
- data/Rakefile +75 -0
- data/bin/higgs_backup +67 -0
- data/bin/higgs_dump_index +43 -0
- data/bin/higgs_dump_jlog +42 -0
- data/bin/higgs_verify +37 -0
- data/lib/cgi/session/higgs.rb +72 -0
- data/lib/higgs/block.rb +192 -0
- data/lib/higgs/cache.rb +117 -0
- data/lib/higgs/dbm.rb +55 -0
- data/lib/higgs/exceptions.rb +31 -0
- data/lib/higgs/flock.rb +77 -0
- data/lib/higgs/index.rb +164 -0
- data/lib/higgs/jlog.rb +159 -0
- data/lib/higgs/lock.rb +189 -0
- data/lib/higgs/storage.rb +1086 -0
- data/lib/higgs/store.rb +228 -0
- data/lib/higgs/tar.rb +390 -0
- data/lib/higgs/thread.rb +370 -0
- data/lib/higgs/tman.rb +513 -0
- data/lib/higgs/utils/bman.rb +285 -0
- data/lib/higgs/utils.rb +22 -0
- data/lib/higgs/version.rb +21 -0
- data/lib/higgs.rb +59 -0
- data/misc/cache_bench/cache_bench.rb +43 -0
- data/misc/dbm_bench/.strc +8 -0
- data/misc/dbm_bench/Rakefile +78 -0
- data/misc/dbm_bench/dbm_multi_thread.rb +199 -0
- data/misc/dbm_bench/dbm_rnd_delete.rb +43 -0
- data/misc/dbm_bench/dbm_rnd_read.rb +44 -0
- data/misc/dbm_bench/dbm_rnd_update.rb +44 -0
- data/misc/dbm_bench/dbm_seq_read.rb +45 -0
- data/misc/dbm_bench/dbm_seq_write.rb +44 -0
- data/misc/dbm_bench/st_verify.rb +28 -0
- data/misc/io_bench/cksum_bench.rb +48 -0
- data/misc/io_bench/jlog_bench.rb +71 -0
- data/misc/io_bench/write_bench.rb +128 -0
- data/misc/thread_bench/lock_bench.rb +132 -0
- data/mkrdoc.rb +8 -0
- data/rdoc.yml +13 -0
- data/sample/count.rb +60 -0
- data/sample/dbmtest.rb +38 -0
- data/test/Rakefile +45 -0
- data/test/run.rb +32 -0
- data/test/test_block.rb +163 -0
- data/test/test_cache.rb +214 -0
- data/test/test_cgi_session.rb +142 -0
- data/test/test_flock.rb +162 -0
- data/test/test_index.rb +258 -0
- data/test/test_jlog.rb +180 -0
- data/test/test_lock.rb +320 -0
- data/test/test_online_backup.rb +169 -0
- data/test/test_storage.rb +439 -0
- data/test/test_storage_conf.rb +202 -0
- data/test/test_storage_init_opts.rb +89 -0
- data/test/test_store.rb +211 -0
- data/test/test_tar.rb +432 -0
- data/test/test_thread.rb +541 -0
- data/test/test_tman.rb +875 -0
- data/test/test_tman_init_opts.rb +56 -0
- data/test/test_utils_bman.rb +234 -0
- metadata +115 -0
data/lib/higgs/jlog.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# = journal log writer
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'higgs/block'
|
12
|
+
|
13
|
+
module Higgs
|
14
|
+
# = journal log writer
|
15
|
+
class JournalLogger
|
16
|
+
# for ident(1)
|
17
|
+
CVS_ID = '$Id: jlog.rb 559 2007-09-25 15:20:20Z toki $'
|
18
|
+
|
19
|
+
include Block
|
20
|
+
|
21
|
+
MAGIC_SYMBOL = 'HIGGS_JLOG'
|
22
|
+
EOF_MARK = :END_OF_JLOG
|
23
|
+
BIN_EOF_MARK = Marshal.dump(EOF_MARK)
|
24
|
+
|
25
|
+
# see Higgs::Block#block_write for <tt>hash_type</tt>
|
26
|
+
def initialize(out, sync=false, hash_type=:MD5)
|
27
|
+
@out = out
|
28
|
+
@sync = sync
|
29
|
+
@hash_type = hash_type
|
30
|
+
end
|
31
|
+
|
32
|
+
def sync?
|
33
|
+
@sync
|
34
|
+
end
|
35
|
+
|
36
|
+
def size
|
37
|
+
@out.stat.size
|
38
|
+
end
|
39
|
+
|
40
|
+
def write(log, hash_type=nil)
|
41
|
+
bin_log = Marshal.dump(log)
|
42
|
+
start_pos = @out.tell
|
43
|
+
commit_completed = false
|
44
|
+
begin
|
45
|
+
block_write(@out, MAGIC_SYMBOL, bin_log, hash_type || @hash_type)
|
46
|
+
if (@sync) then
|
47
|
+
@out.fsync
|
48
|
+
else
|
49
|
+
@out.flush
|
50
|
+
end
|
51
|
+
commit_completed = true
|
52
|
+
ensure
|
53
|
+
@out.truncate(start_pos) unless commit_completed
|
54
|
+
end
|
55
|
+
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def write_EOF
|
60
|
+
JournalLogger.eof_mark(@out)
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def close(eof=true)
|
65
|
+
write_EOF if eof
|
66
|
+
@out.fsync
|
67
|
+
@out.close
|
68
|
+
@out = nil
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
class << self
|
73
|
+
def has_eof_mark?(path)
|
74
|
+
unless (File.exist? path) then
|
75
|
+
return true
|
76
|
+
end
|
77
|
+
|
78
|
+
File.open(path, 'r') {|f|
|
79
|
+
f.binmode
|
80
|
+
fsiz = f.stat.size
|
81
|
+
if (fsiz < Block::BLOCK_SIZE * 2) then
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
f.seek(fsiz - Block::BLOCK_SIZE * 2)
|
85
|
+
|
86
|
+
begin
|
87
|
+
bin_log = Block.block_read(f, MAGIC_SYMBOL) or return
|
88
|
+
log = Marshal.load(bin_log)
|
89
|
+
if (log == EOF_MARK) then
|
90
|
+
return true
|
91
|
+
end
|
92
|
+
rescue Block::BrokenError
|
93
|
+
return false
|
94
|
+
end
|
95
|
+
}
|
96
|
+
|
97
|
+
false
|
98
|
+
end
|
99
|
+
|
100
|
+
def need_for_recovery?(path)
|
101
|
+
! (has_eof_mark? path)
|
102
|
+
end
|
103
|
+
|
104
|
+
def open(path, *args)
|
105
|
+
begin
|
106
|
+
f = File.open(path, File::WRONLY | File::CREAT | File::EXCL, 0660)
|
107
|
+
rescue Errno::EEXIST
|
108
|
+
if (need_for_recovery? path) then
|
109
|
+
raise "need for recovery: #{path}"
|
110
|
+
end
|
111
|
+
f = File.open(path, File::WRONLY, 0660)
|
112
|
+
fsiz = f.stat.size
|
113
|
+
f.truncate(fsiz - Block::BLOCK_SIZE * 2)
|
114
|
+
end
|
115
|
+
f.binmode
|
116
|
+
f.seek(0, IO::SEEK_END)
|
117
|
+
new(f, *args)
|
118
|
+
end
|
119
|
+
|
120
|
+
def eof_mark(out)
|
121
|
+
Block.block_write(out, MAGIC_SYMBOL, BIN_EOF_MARK)
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def scan_log(io)
|
126
|
+
safe_pos = io.tell
|
127
|
+
begin
|
128
|
+
while (bin_log = Block.block_read(io, MAGIC_SYMBOL))
|
129
|
+
log = Marshal.load(bin_log)
|
130
|
+
if (log == EOF_MARK) then
|
131
|
+
break
|
132
|
+
end
|
133
|
+
yield(log)
|
134
|
+
safe_pos = io.tell
|
135
|
+
end
|
136
|
+
rescue Block::BrokenError
|
137
|
+
io.seek(safe_pos)
|
138
|
+
raise
|
139
|
+
end
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
def each_log(path)
|
144
|
+
File.open(path, 'r') {|f|
|
145
|
+
f.binmode
|
146
|
+
scan_log(f) {|log|
|
147
|
+
yield(log)
|
148
|
+
}
|
149
|
+
}
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Local Variables:
|
157
|
+
# mode: Ruby
|
158
|
+
# indent-tabs-mode: nil
|
159
|
+
# End:
|
data/lib/higgs/lock.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
# = multi-thread lock manager
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'forwardable'
|
12
|
+
require 'higgs/cache'
|
13
|
+
require 'higgs/exceptions'
|
14
|
+
require 'singleton'
|
15
|
+
require 'thread'
|
16
|
+
|
17
|
+
module Higgs
|
18
|
+
# = multi-thread lock manager
|
19
|
+
class LockManager
|
20
|
+
# for ident(1)
|
21
|
+
CVS_ID = '$Id: lock.rb 559 2007-09-25 15:20:20Z toki $'
|
22
|
+
|
23
|
+
include Exceptions
|
24
|
+
|
25
|
+
class Error < HiggsError
|
26
|
+
end
|
27
|
+
|
28
|
+
class TryLockTimeoutError < Error
|
29
|
+
end
|
30
|
+
|
31
|
+
SPIN_LOCK_COUNT = 100
|
32
|
+
TRY_LOCK_LIMIT = 10
|
33
|
+
TRY_LOCK_INTERVAL = 0.1
|
34
|
+
|
35
|
+
RAND_GEN = proc{|seed|
|
36
|
+
n = seed
|
37
|
+
cycle = 0xFFFF
|
38
|
+
proc{
|
39
|
+
n = (n * 37549 + 12345) % cycle
|
40
|
+
n.to_f / cycle
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
def initialize(options={})
|
45
|
+
@spin_lock_count = options[:spin_lock_count] || SPIN_LOCK_COUNT
|
46
|
+
@try_lock_limit = options[:try_lock_limit] || TRY_LOCK_LIMIT
|
47
|
+
@try_lock_interval = options[:try_lock_interval] || TRY_LOCK_INTERVAL
|
48
|
+
@rand_gen = options[:random_number_generator] || RAND_GEN
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :spin_lock_count
|
52
|
+
attr_reader :try_lock_limit
|
53
|
+
attr_reader :try_lock_interval
|
54
|
+
|
55
|
+
def new_rand(seed)
|
56
|
+
@rand_gen.call(seed)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.try_lock(lock, attrs)
|
60
|
+
t0 = Time.now
|
61
|
+
c = attrs.spin_lock_count
|
62
|
+
while (c > 0)
|
63
|
+
if (lock.try_lock) then
|
64
|
+
return
|
65
|
+
end
|
66
|
+
c -= 1
|
67
|
+
end
|
68
|
+
|
69
|
+
if (attrs.try_lock_limit > 0) then
|
70
|
+
rand = attrs.new_rand(::Thread.current.object_id ^ t0.to_i)
|
71
|
+
while (Time.now - t0 < attrs.try_lock_limit)
|
72
|
+
if (lock.try_lock) then
|
73
|
+
return
|
74
|
+
end
|
75
|
+
sleep(attrs.try_lock_interval * rand.call)
|
76
|
+
end
|
77
|
+
raise TryLockTimeoutError, 'expired'
|
78
|
+
else
|
79
|
+
# brave man who doesn't fear deadlock.
|
80
|
+
lock.lock
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class GiantLockManager < LockManager
|
86
|
+
# for ident(1)
|
87
|
+
CVS_ID = '$Id: lock.rb 559 2007-09-25 15:20:20Z toki $'
|
88
|
+
|
89
|
+
def initialize(*args)
|
90
|
+
super
|
91
|
+
@rw_lock = ReadWriteLock.new
|
92
|
+
end
|
93
|
+
|
94
|
+
class NoWorkLockHandler
|
95
|
+
include Singleton
|
96
|
+
|
97
|
+
def lock(key)
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
def unlock(key)
|
102
|
+
self
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def transaction(read_only=false)
|
107
|
+
if (read_only) then
|
108
|
+
lock = @rw_lock.read_lock
|
109
|
+
else
|
110
|
+
lock = @rw_lock.write_lock
|
111
|
+
end
|
112
|
+
r = nil
|
113
|
+
lock.synchronize{
|
114
|
+
r = yield(NoWorkLockHandler.instance)
|
115
|
+
}
|
116
|
+
r
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class FineGrainLockManager < LockManager
|
121
|
+
# for ident(1)
|
122
|
+
CVS_ID = '$Id: lock.rb 559 2007-09-25 15:20:20Z toki $'
|
123
|
+
|
124
|
+
def initialize(*args)
|
125
|
+
super
|
126
|
+
@cache = SharedWorkCache.new{|key| ReadWriteLock.new }
|
127
|
+
end
|
128
|
+
|
129
|
+
class LockHandler
|
130
|
+
def initialize(attrs, cache)
|
131
|
+
@attrs = attrs
|
132
|
+
@cache = cache
|
133
|
+
@lock_map = {}
|
134
|
+
end
|
135
|
+
|
136
|
+
def lock_list
|
137
|
+
@lock_map.values
|
138
|
+
end
|
139
|
+
|
140
|
+
def unlock(key)
|
141
|
+
if (lock = @lock_map.delete(key)) then
|
142
|
+
lock.unlock
|
143
|
+
else
|
144
|
+
raise "not locked key: #{key}"
|
145
|
+
end
|
146
|
+
self
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class ReadOnlyLockHandler < LockHandler
|
151
|
+
def lock(key)
|
152
|
+
r_lock = @cache[key].read_lock
|
153
|
+
LockManager.try_lock(r_lock, @attrs)
|
154
|
+
@lock_map[key] = r_lock
|
155
|
+
self
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class ReadWriteLockHandler < LockHandler
|
160
|
+
def lock(key)
|
161
|
+
w_lock = @cache[key].write_lock
|
162
|
+
LockManager.try_lock(w_lock, @attrs)
|
163
|
+
@lock_map[key] = w_lock
|
164
|
+
self
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def transaction(read_only=false)
|
169
|
+
if (read_only) then
|
170
|
+
lock_handler = ReadOnlyLockHandler.new(self, @cache)
|
171
|
+
else
|
172
|
+
lock_handler = ReadWriteLockHandler.new(self, @cache)
|
173
|
+
end
|
174
|
+
begin
|
175
|
+
r = yield(lock_handler)
|
176
|
+
ensure
|
177
|
+
for lock in lock_handler.lock_list
|
178
|
+
lock.unlock
|
179
|
+
end
|
180
|
+
end
|
181
|
+
r
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Local Variables:
|
187
|
+
# mode: Ruby
|
188
|
+
# indent-tabs-mode: nil
|
189
|
+
# End:
|