higgs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|